Autonomous Machine

Convert string values to Ruby objects

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.

Enter 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"

Beautiful.

  • October 03, 2012
  • Article
  • Ruby

Troubleshooting db:push errors on Heroku

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 HEROKU_POSTGRESQL_*_URL.

  • May 30, 2012
  • Article
  • Heroku, Taps

Running Ruby 1.9.3 on Heroku Cedar

Heroku now supports specifying a Ruby version in an application's Gemfile:

# Gemfile
ruby '1.9.3'

This requires Bundler 1.2.0, which can be installed with the --pre flag:

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!

  • May 23, 2012
  • Article
  • Heroku, Ruby

Use Active Model Serializers with Mongoid

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 config/initializers/mongoid_active_model_initializers:

Mongoid::Document.send(:include, ActiveModel::SerializerSupport)
Mongoid::Criteria.delegate(:active_model_serializer, to: :to_a)
  • May 05, 2012
  • Article
  • Active Model Serializers, Ember, Mongoid, Rails

Store Hashie::Mash objects in Mongoid Documents

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']

Since 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 Mashed:

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 deserialize and serialize above, just about any kind of object could be persisted in a Mongoid document.

  • March 24, 2012
  • Article
  • MongoDB, Mongoid, Ruby

Tell Sprockets to precompile files other than application.js

I usually bundle all of a Rails project's JavaScript together. Occasionally, only a few pages need a large JavaScript library, and I don't want it to have to be downloaded on every page of the application. In these situations, I don't require the large library file or it's dependencies in 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/application.rb:

config.assets.precompile += ['another_file.js']

That's all there is to it.

  • March 17, 2012
  • Article
  • Rails, Sprockets