How to Convert a Ruby Object to Hash

1 min read

Let’s say you have a Product object with properties @name = "Table" & @price = 10. What’s the best way in Rails to convert this object to the Hash { name: "Table", price: 10 }?

Here're a few different ways to achieve this.

The as_json Method (Rails)

The as_json method converts a model to a hash, containing the attributes with their names as keys and the values of the attributes as values

user = User.find(1)
user.as_json
# => { "id" => 1, "name" => "Konata Izumi", "age" => 16 }

Additionally, you can include the model itself as the root of the object.

user = User.find(1)
user.as_json(root: true)
# => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16 } }

This method also gives you more control over the resulting JSON representation. For example, you can use the :only and :except options to select which attributes you want to include and skip, respectively. For more details, refer the documentation.

The attributes Method (Rails)

The ActiveRecord#attributes is another method you can use. Given an active record, attributes returns a hash of all the attributes with their names as keys and the values of the attributes as values.

class Person < ActiveRecord::Base
end

person = Person.create(name: 'Francesco', age: 22)
person.attributes
# => {"id"=>3, "created_at"=>Sun, 21 Oct 2012 04:53:04, "updated_at"=>Sun, 21 Oct 2012 04:53:04, "name"=>"Francesco", "age"=>22}

The attributes method returns a hash where all the keys are strings. If you want, it’s easy to symbolize the hash using the aptly named symbolize_keys method or its alias to_options.

hash = { 'name' => 'Rob', 'age' => '28' }

hash.symbolize_keys
# => {:name=>"Rob", :age=>"28"}

The Ruby Way

If you are looking for a plain Ruby solution, you can do this using metaprogramming.

For this, you will need two methods:

  1. instance_variables returns an array of object’s instance variables.
  2. instance_variable_get returns the value of the instance variable, given the variable name.

With that, you could do something like this:

hash = Hash.new
record.instance_variables.each do |v| 
  hash[v.to_s.delete("@")] = record.instance_variable_get(v) 
end

What do you think? Did I miss anything?