Hash to Active Model Serializer - ruby-on-rails

I have a hash #branches that is basically:
{1 => 5}, {2 => 6}
Is it possible for me to send this to a serializer and get output json like so:
{ branch_id: 1, branch_name: 'Hello', count_5}
I've made a custom serializer and calling it like so:
render json: #branches, serializer: AvilableStockBranchSerializer
How can I pass the hash data to the serializer?

Why do you need AMS?
You can use #branches.to_json instead

Related

Rails API JSON response, change name of method

I have a Rails 5 API that renders an object with some of it's methods to JSON.
render json: { rides: #rides }.to_json( :methods => [ :is_preferred ]), status: 200
So this returns something like:
{
id: 123,
is_preferred: true
}
But I would like to change the name of the attribute that refers to the is_preferred method.
The output I would like id:
{
id: 123,
preferred: true
}
I tried
render json: { rides: #rides }.to_json( :methods => [ preferred: :is_preferred ]), status: 200
But this does not work. Easiest would be to change the method name in the model, but that's not possible in this case.
Is there any way I can manipulate the name inside the response?
You can try with an ActiveModel::Serializer, then you can define the attribute key as you want
class RideSerializer < ActiveModel::Serializer
attribute :preferred, key: :is_preferred
end
or use a method to retrieve the attribute value
class RideSerializer < ActiveModel::Serializer
attribute :is_preferred
def is_preferred
object.preferred
end
end
Using serializers has a lot of bennefits and is so powerfull if you want to create custom responses.

Saving attribute from array of objects in Rails

I'm passing as array of JSON object as a param in Rails through an AJAX post/delete, like
[{'id': 3, 'status': true}, {'id': 1, 'status': true}]
How do I loop through the params[:list] to get each id and status value?
Do this:
params[:list].each do |hash|
hash['id'] # => 3
hash['status'] # => true
end
Try like follows:
params[ :list ][ 0 ][ :id ] # => 3, ...
params[ :list ].each {| v | v[ :id ] } # => 3, 1
In case if your hash is like ["0", {"id"=>"9", "status"=>"true"}]:
# a = ["0", {"id"=>"9", "status"=>"true"}]
h = Hash[[a]]
# => {"0"=>{"id"=>"9", "status"=>"true"}}
and access to it will be:
h['0'][ :id ] # => '9'
There were two steps to this, plus what #Agis suggested. (Thanks #Agis)
I had to first stringify the JSON:
'p': JSON.stringify(p),
So that it does not create that wierd array. There was another stackoverflow answer using this method to generate a correct data JSON object, but it was encapsulating the whole data... I need to have this on just p and not affect 'stringifying' the security token.
Understood this from here: Rails not decoding JSON from jQuery correctly (array becoming a hash with integer keys)
Next, I had to decode just that param, using
ActiveSupport::JSON.decode(params[:p])
a clue from here: How do I parse JSON with Ruby on Rails?
Lastly, can I then use the loop and access item['id']
Here's a loop:
params[:list].each do |item|
item.id
item.satus
end
If you want to create an array of id's or something:
list_ids = params[:list].map(&:id)

Code snippet for comparing hashes with similar keys

I am writing integration tests for rails and I want to compare the object created with the JSON object sent. The object returned is not exactly the same as the one sent, (i.e.) it has keys that the object sent doesn't have because I am using active model serializers to pull associations in the returned object. Basically, I just want to compare all the same keys between both objects to see if its the same. Let me know if there is a clean efficient code snippet that does this for me!
TL;DR
"Clever" test code is rarely useful. Each test should be as simple as possible, and it should be testing the behavior of some object rather than its composition. There are always ways to be clever, though.
Using Array Intersection
One unreadably-clever way to do this is to use Array#& to find the intersection of the keys, and then look for equality between the values. This will work on a relatively flat hash. For example:
hash1 = {:key1=>"value1", :key2=>"value2", :key3=>"value3", :key4=>"value4"}
hash2 = {:key1=>"value1", :key2=>"value2", :key5=>"value5"}
Array(hash1.keys & hash2.keys).map { |k| hash1[k] == hash2[k] }.uniq
#=> [true]
If you're using RSpec to test, you could say something like:
it 'has some matching key/value pairs' do
# ... populate hash1
# ... populate hash2
Array(hash1.keys & hash2.keys).
map { |k| hash1[k] == hash2[k] }.uniq.should == [true]
end
Of course, if the expectation is false, then you won't really know why, or which key/value pair was wrong. This is just one of the many reasons that you should always use fixed inputs and outputs for testing, rather than trying to do dynamic comparisons.
You could use Hash#slice, which is an Active Support core extension.
For example, if the keys you want to check are :a, :b, :c, but the result contains :a, :b, :c, :d, slice will reduce the result to just contain the keys you care about:
expected = { :a => 1, :b => 2, :c => 3 }
result = { :a => 1, :b => 2, :c => 3, :d => 4 }
result.slice(:a, :b, :c) == expected
# => true
If you get a NoMethodError: undefined method 'slice' exception, you need to require active_support/core_ext/hash/slice
First, find the keys that both hashes contain, and then compare the value for those keys:
hash1 = {:key1 => "value1", :key2 => "value2", :key3 => "value3", :key4 => "value4"}
hash2 = {:key1 => "value1", :key2 => "value2", :key5 => "value5"}
hash1_keys = hash1.keys
hash2_keys = hash2.keys
comparable_keys = hash1_keys.select{|key| hash2_keys.include?(key)}
comparable_keys.each do |key|
hash1[key].should == hash2[key]
end

Rails model to hash, exclude attributes

I am trying to form a json response that looks like this:
{
"user": {
"birthday": "2013-03-13",
"email": "example#example",
"id": 1,
"name": null,
"username": "example"
},
"other_data": "foo"
}
Before, when I was just returning the user, I used
render :json => #user, :except => [:hashed_password, :created_at, :updated_at]
to keep the hashed_password, created_at, and updated_at attributes from being sent. Is there a way to do this, but also allow additional data to be sent along with the user? Right now I'm just adding the attributes I want to send to the hash one by one, but this is obviously not ideal.
Rendering json data first automagically calls 'as_json' on your model, which returns a ruby hash. After that, 'to_json' is called on that to get a string representation of your hash.
To achieve what you wanted, you can call something like this:
render :json => {
:user => #user.as_json(:except => [:hashed_password]),
:some_other_data => {}
}
In this case, there is no object which responds to 'as_json', so the controller just calls 'to_json' to turn your hash to a string.
I would recommend to use this gem: https://github.com/fabrik42/acts_as_api

Active record result and transformed JSON

I need to transform active record JSON to something like this:
{
cols: [{id: 'task', label: 'Task', type: 'string'},
{id: 'hours', label: 'Hours per Day', type: 'number'}],
rows: [{c:[{v: 'Work'}, {v: 11}]},
{c:[{v: 'Eat'}, {v: 2}]},
{c:[{v: 'Commute'}, {v: 2}]},
{c:[{v: 'Watch TV'}, {v:2}]},
{c:[{v: 'Sleep'}, {v:7, f:'7.000'}]}
]
}
That is totally different from what to_json returns from activerecord. What is the most ruby way to transform JSON?
Override the to_json method in your model
# your_model.rb, implement an instance method to_json
def to_json(options = {})
{
'cols' => [{'id' => 'whateveryoulike'}],
'rows' => [{'id' => 'whateveryoulike'}]
}.to_json(options)
end
Remember, it is important to accept options as parameter to this method and pass it on to to_json of the hash (or any other to_json call you make inside this method, for that matter). Otherwise, the method may not behave as expected on collection JSON serialization. And of course since you haven't given any details as to what your model is and how it maps to the desired JSON response, you will have to implement the representation of cols and rows as you like.
This also applies to to_xml.

Resources