on programming Rails for fun and profit...

How to Access Hash Values Like Methods using OrderedOptions

In Ruby, if you want to access hash values like methods on an object, use ActiveSupport::OrderedOptions. This class inherits from Hash and provides dynamic accessor methods.

Typically, you’d do this with a Hash.

person = {
  name: "Jason",
  company: "Basecamp"

person[:company] # 'Basecamp'

Using OrderedOptions, you can write

require "active_support/ordered_options"

person = ActiveSupport::OrderedOptions.new

# set the values
person.name = "Jason"
person.company = "Basecamp"

# access the values
person.name  # => 'Jason'
person.company # => 'Basecamp'


Behind the scenes, Rails implements this feature using metaprogramming in Ruby. It uses the method_missing method to handle the method call.

def method_missing(name, *args)
  name_string = +name.to_s
  if name_string.chomp!("=")
    self[name_string] = args.first	# set the value
    bangs = name_string.chomp!("!")

    # get the value
    if bangs
      self[name_string].presence || raise(KeyError.new(":#{name_string} is blank"))

You can read the complete source code here. For more details on metaprogramming in Ruby, read my notes of the Metaprogramming Ruby 2 book.

Real-World Usage

Propshaft is an asset pipeline library for Rails. It uses OrderedOptions to define the config.assets settings, instead of creating a new configuration object. You can read the complete source on Github.

class Railtie < ::Rails::Railtie
    config.assets = ActiveSupport::OrderedOptions.new
    config.assets.paths          = []
    config.assets.excluded_paths = []
    config.assets.version        = "1"
    config.assets.prefix         = "/assets"

Now, this doesn’t mean you have to replace all your hashes with instances of OrderedOptions. It’s better to use them with configuration-like objects, which often results in more readable code.

Hope that helps. Let me know what you think about this approach.

Subscribe to Akshay's Blog

Sign up now to get access to the library of members-only issues.
Jamie Larson