On a project at work we've started to use environment variables to toggle features on and off in the application. We're just getting into feature toggling, and this seemed to be the best lightest-weight way to get started.
Since environment variables are strings, and some of our values will be objects of other types, we needed a way to convert these strings into Ruby objects. A preliminary search yielded many options that utilized complicated regular expressions and other gymnastics. There had to be a better way.
YAML. It supports serializing the types we needed:
irb(main):001:0> require 'yaml' => true irb(main):002:0> YAML.load('true') => true irb(main):003:0> YAML.load('108') => 108 irb(main):004:0> YAML.load('just a string') => "just a string"
Here is the error I was seeing today (truncated for brevity):
$ heroku db:push --app=my-app-name --confirm=my-app-name Loaded Taps v0.3.23 Auto-detected local database: postgres://localhost/app_development?encoding=utf8 Sending schema Schema: 100% |==========================================| Time: 00:00:29 Sending indexes table_1: 100% |==========================================| Time: 00:00:01 schema_migrat: 100% |==========================================| Time: 00:00:00 table_2: 100% |==========================================| Time: 00:00:00 Sending data 20 tables, 2,783 records table_3: 0% | | ETA: --:--:-- Saving session to push_201205300813.dat.. !!! Caught Server Exception HTTP CODE: 500 Taps Server Error: LoadError: no such file to load -- sequel/adapters/ ["/app/.bundle/gems/ruby/1.9.1/gems/sequel-3.20.0/lib/sequel/core.rb:249:in `require'", ...
This was caused by my app not having a
DATABASE_URL config variable set. I assumed that
db:push would fail much earlier if it wasn't able to connect to a database, but this turned out to not be accurate.
The simple solution was to promote the existing database:
$ heroku pg:promote HEROKU_POSTGRESQL_COPPER --app=my-app-name
To find the name of the database to use in the previous command, use
heroku config --app=my-app-name. It should have a name like
# Gemfile ruby '1.9.3'
This requires Bundler 1.2.0, which can be installed with the
gem install bundler --pre
You also need to move 'bin' to the beginning of your application's PATH so the correct Ruby binary is found when starting your web processes:
heroku config:add PATH=bin:vendor/bundle/ruby/1.9.1/bin:/usr/local/bin:/usr/bin:/bin --app YOU_APP_NAME
Commit the Gemfile changes and push to Heroku, and your app should be running under Ruby 1.9.3!
I'm adding some new features to Forkchop using Ember, ember-rails, and the associated active_model_serializers. The only hiccup is that I'm using Mongoid, and Active Model Serializers only hooks into Active Record when it loads. This means that Rails doesn't automatically look for the appropriate serializer when using
render json: @object.
To remedy this, I added the following lines from this Gist to
Mongoid::Document.send(:include, ActiveModel::SerializerSupport) Mongoid::Criteria.delegate(:active_model_serializer, to: :to_a)
I've started using Mongoid's support for abitrary field types on a project. It involves working with a lot of nested hashes that are the result of parsing JSON, and I've found wrapping a
Hashie::Mash around these data structures helps keep the code interacting with them clean:
doc.data.key.another_key.and_another_key # vs doc.data['key']['another_key']['and_another_key']
Hashie::Mash objects are just a subclass of Ruby's standard
Hash, they can be stored in a field that's defined as a
Hash in Mongoid:
class Doc include Mongoid::Document field :data, type: Hash end
But when these documents are loaded back out of the database, the
data field will contain a standard
Hash. I'd like these hashes to be
Hashie::Mash instances. To accomplish this, I defined a new column type for Mongoid called
class Mashed include Mongoid::Fields::Serializable def deserialize(object) Hashie::Mash.new(object) end def serialize(object) Hash[object] end end
That's all that is required to create a new column type in Mongoid, which can be specified just like any other in your document class:
class Doc include Mongoid::Document field :data, type: Mashed end
Now I can save and load documents with
Hashie::Mash fields. This is a trivally simple example, but by replacing the implementations of
serialize above, just about any kind of object could be persisted in a Mongoid document.
application.js so it won't be bundled with the rest of the project's scripts. I'll add a script tag for the library to just the pages that need it, which works locally in development mode. And then inevitably I deploy and the additional files can't be found on the server because they haven't been precompiled.
It is in the docs, but I always seem to forget that for Sprockets to precompile anything other than
application.js, the additional files must be specifed in
config.assets.precompile += ['another_file.js']
That's all there is to it.