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:
instance_variables
returns an array of object’s instance variables.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?