I have the following object that has been created
@post = Post.create(:name => 'test', :post_number => 20, :active => true)
Once this is saved, I want to be able to get the object back to a hash, e.g. by doing somthing like:
@object.to_hash
How is this possible from within rails?
This question is related to
ruby-on-rails
ruby
There are some great suggestions here.
I think it's worth noting that you can treat an ActiveRecord model as a hash like so:
@customer = Customer.new( name: "John Jacob" )
@customer.name # => "John Jacob"
@customer[:name] # => "John Jacob"
@customer['name'] # => "John Jacob"
Therefore, instead of generating a hash of the attributes, you can use the object itself as a hash.
In most recent version of Rails (can't tell which one exactly though), you could use the as_json
method :
@post = Post.first
hash = @post.as_json
puts hash.pretty_inspect
Will output :
{
:name => "test",
:post_number => 20,
:active => true
}
To go a bit further, you could override that method in order to customize the way your attributes appear, by doing something like this :
class Post < ActiveRecord::Base
def as_json(*args)
{
:name => "My name is '#{self.name}'",
:post_number => "Post ##{self.post_number}",
}
end
end
Then, with the same instance as above, will output :
{
:name => "My name is 'test'",
:post_number => "Post #20"
}
This of course means you have to explicitly specify which attributes must appear.
Hope this helps.
EDIT :
Also you can check the Hashifiable gem.
Swanand's answer is great.
if you are using FactoryGirl, you can use its build
method to generate the attribute hash without the key id
. e.g.
build(:post).attributes
@object.as_json
as_json has very flexible way to configure complex object according to model relations
EXAMPLE
Model campaign belongs to shop and has one list
Model list has many list_tasks and each of list_tasks has many comments
We can get one json which combines all those data easily.
@campaign.as_json(
{
except: [:created_at, :updated_at],
include: {
shop: {
except: [:created_at, :updated_at, :customer_id],
include: {customer: {except: [:created_at, :updated_at]}}},
list: {
except: [:created_at, :updated_at, :observation_id],
include: {
list_tasks: {
except: [:created_at, :updated_at],
include: {comments: {except: [:created_at, :updated_at]}}
}
}
},
},
methods: :tags
})
Notice methods: :tags can help you attach any additional object which doesn't have relations with others. You just need to define a method with name tags in model campaign. This method should return whatever you need (e.g. Tags.all)
Official documentation for as_json
not sure if that's what you need but try this in ruby console:
h = Hash.new
h["name"] = "test"
h["post_number"] = 20
h["active"] = true
h
obviously it will return you a hash in console. if you want to return a hash from within a method - instead of just "h" try using "return h.inspect", something similar to:
def wordcount(str)
h = Hash.new()
str.split.each do |key|
if h[key] == nil
h[key] = 1
else
h[key] = h[key] + 1
end
end
return h.inspect
end
You could definitely use the attributes to return all attributes but you could add an instance method to Post, call it "to_hash" and have it return the data you would like in a hash. Something like
def to_hash
{ name: self.name, active: true }
end
You can get the attributes of a model object returned as a hash using either
@post.attributes
or
@post.as_json
as_json
allows you to include associations and their attributes as well as specify which attributes to include/exclude (see documentation). However, if you only need the attributes of the base object, benchmarking in my app with ruby 2.2.3 and rails 4.2.2 demonstrates that attributes
requires less than half as much time as as_json
.
>> p = Problem.last
Problem Load (0.5ms) SELECT "problems".* FROM "problems" ORDER BY "problems"."id" DESC LIMIT 1
=> #<Problem id: 137, enabled: true, created_at: "2016-02-19 11:20:28", updated_at: "2016-02-26 07:47:34">
>>
>> p.attributes
=> {"id"=>137, "enabled"=>true, "created_at"=>Fri, 19 Feb 2016 11:20:28 UTC +00:00, "updated_at"=>Fri, 26 Feb 2016 07:47:34 UTC +00:00}
>>
>> p.as_json
=> {"id"=>137, "enabled"=>true, "created_at"=>Fri, 19 Feb 2016 11:20:28 UTC +00:00, "updated_at"=>Fri, 26 Feb 2016 07:47:34 UTC +00:00}
>>
>> n = 1000000
>> Benchmark.bmbm do |x|
?> x.report("attributes") { n.times { p.attributes } }
?> x.report("as_json") { n.times { p.as_json } }
>> end
Rehearsal ----------------------------------------------
attributes 6.910000 0.020000 6.930000 ( 7.078699)
as_json 14.810000 0.160000 14.970000 ( 15.253316)
------------------------------------ total: 21.900000sec
user system total real
attributes 6.820000 0.010000 6.830000 ( 7.004783)
as_json 14.990000 0.050000 15.040000 ( 15.352894)
My solution:
Hash[ post.attributes.map{ |a| [a, post[a]] } ]
Old question, but heavily referenced ... I think most people use other methods, but there is infact a to_hash
method, it has to be setup right. Generally, pluck is a better answer after rails 4 ... answering this mainly because I had to search a bunch to find this thread or anything useful & assuming others are hitting the same problem...
Note: not recommending this for everyone, but edge cases!
From the ruby on rails api ... http://api.rubyonrails.org/classes/ActiveRecord/Result.html ...
This class encapsulates a result returned from calling #exec_query on any database connection adapter. For example:
result = ActiveRecord::Base.connection.exec_query('SELECT id, title, body FROM posts')
result # => #<ActiveRecord::Result:0xdeadbeef>
...
# Get an array of hashes representing the result (column => value):
result.to_hash
# => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
{"id" => 2, "title" => "title_2", "body" => "body_2"},
...
] ...
Source: Stackoverflow.com