RABL - Render collection as object with the ids as keys - ruby-on-rails

In Ruby on Rails 4, I'm trying to make an API for my website and instead of using an array like so:
[{id: 1, name: "John"}, {id: 2, name: "Foo"}, {id: 3, name: "Bar"}]
I want to render it like this, because it makes it much easier to search through in javascript (and for other reasons):
{"1": {id: 1, name: "John"}, "2": {id: 2, name: "Foo"}, "3": {id: 3, name: "Bar"}}
This works:
# users/index.rabl
#users.each do |user|
node(users.id.to_s) do
partial('api/v1/users/show', object: user)
end
end
But in the partial, I want another collection of elements (which belong to the user) and I can't get that working. Is there a more elegant way to do this?

To choose hash-map rather than array is definitely better option if have control on API backend codebase. From BigO notation hash lookup happens with constant O(1) time, not O(n) as for array.
Primary key lookup from the database is the most performant way to query data. Primary key is usually short and always indexed. In case of big data set use pagination.
Let's assume there is no RABL (you can always instantiate pure Ruby classes in RABL DSL code) but just an array:
array = [{id: 1, name: "John"}, {id: 2, name: "Foo"}, {id: 3, name: "Bar"}]
hash = {}
array.each{ |elem,i| hash[elem[:id].to_s] = elem }
# {"1"=>{:id=>1, :name=>"John"}, "2"=>{:id=>2, :name=>"Foo"}, "3"=>{:id=>3, :name=>"Bar"}}
To pass the Ruby hash to Javascript on client you probably want to encode it appropriately:
# hash.to_json
# {"1":{"id":1,"name":"John"},"2":{"id":2,"name":"Foo"},"3":{"id":3,"name":"Bar"}}
From Javascript you query hash by its key:
hash = {"1":{"id":1,"name":"John"},"2":{"id":2,"name":"Foo"},"3":{"id":3,"name":"Bar"}}
hash[1]
# Object { id=1, name="John"}

Related

similar to ampersand for accessing item in an array in ruby

I'm working with a class that is a subclass of JsonApiClient::Resource and can access like this (not ampersand / try sytax):
user&.professional_awards
which is like this with professional_awards being an array of Hash with Indifferent Access:
{id: 23
name: 'something',
professional_awards:[
{name: 'Best Smile'}, {name: 'Funniest'}
]
}
My question is how I access the internal professional awards in a safe way? Is there a way I could use ampersand like syntax? Or do I have to manually check? Would like to do (and options in Rails hence tagged as):
user&.professional_awards.first&['name'] # Best Smile
You're looking for Hash#dig:
hash = {
id: 23,
name: 'something',
professional_awards: [ { name: 'Best Smile' }, { name: 'Funniest' } ]
}
hash.dig(:id) # 23
hash.dig(:professional_awards) # [{:name=>"Best Smile"}, {:name=>"Funniest"}]
hash.dig(:professional_awards, 1) # {:name=>"Funniest"}

How to change already exposed module constant in ruby

I have a constant defined in my module and it is already exposed to all clients and many clients using this constant directly.
module MyModule
module Contants
FRUITS_MAP = [
{
id: 1,
name: 'Apple'
},
{
id: 2,
name: 'Coconut'
}
].freeze
end
end
Now the problem is, I want to add one more hash to FRUITS_MAP based on a condition.
if Account.enabled_feature?('Mango')
{
id: 5,
name: 'Mango'
}
Note: I don't have access to all the clients and I don't want to enforce a change in all clients but I want to add one more element to FRUITS_MAP conditionally.
For a client X with 'Mango' feature enabled, FRUITS_MAP should contain 3 elements, but for a client Y with 'Mango' feature disabled should have only 2 elements in FRUITS_MAP. How I can achieve this?
You can just return a new array by the concatenation of MyModule::Contants::FRUITS_MAP and the additional object you want in it if the condition is true:
p MyModule::Contants::FRUITS_MAP + [{ id: 5, name: 'Mango' }] if Account.enabled_feature?('Mango')
# [{:id=>1, :name=>"Apple"}, {:id=>2, :name=>"Coconut"}, {:id=>5, :name=>"Mango"}]

Show values from helper method in drop down

I am using a hash constant in my ROR application. I want to show the names from the hash constant to drop down.
helper.rb
PRODUCE_GROWING_METHODS = [
{id: 1, name: 'Conventional'},
{id: 2, name: 'Organic'},
]
def produce_growing_methods
PRODUCE_GROWING_METHODS
end
_produce.haml
= f.simple_fields_for :produce_details do |pd|
= pd.input :produce_growing_method, :collection => produce_growing_methods.collect { |x| [x[0], x[1]] }, :prompt => "Select Growing Method"
I tried as shown above in _produce.haml but i am getting the empty drop down. Names from the constant are not populated in drop down.
Can any one help me how to show the names from the PRODUCE_GROWING_METHODS hash constant to a drop down.
Thanks
You should map the hash by keys. In your case the keys are :id and :name:
produce_growing_methods.map { |x| [x[:id], x[:name]] }
In reality you are always better of using a generic solution rather then manual mapping.
Here is a better way of achieving the same, but it will work as well for array of thousand hashes:
ary = [
{id: 1, name: 'Conventional'},
{id: 2, name: 'Organic'},
]
ary.map(&:values)
#=> [[1, "Conventional"], [2, "Organic"]]

Ruby On Rails: How can I sort this hash by its keys in descending order

(rdb:60) p resultsHash
{ 1 => [#<Participant id: 6, username: "player2", online_rank: 7, created_at: "2011-05-14 04:49:22", updated_at: "2011-05-14 04:57:56", win_count: 1, device_type: "iPad">],
0 => [#<Participant id: 5, username: "player1", online_rank: 3, created_at: "2011-05-12 02:47:50", updated_at: "2011-05-12 02:47:50", win_count: 0, device_type: "iPad">,
#<Participant id: 4, username: "iPhone4Simulator", online_rank: 4, created_at: "2011-05-12 02:45:37", updated_at: "2011-05-12 02:45:37", win_count: 0, device_type: "iPad">]}
I've tried...
resultsHash.sort {|a,b| -(a[0]<=>b[0])}
but the results aren't sorted by the keys when I iterate through the hash using each_pair.
Thanks!
What you really want to do is add an ORDER BY clause to your query. If this is Rails 3, Participant.order(:id).all is one way to do it.
To answer your immediate question, though, you would say resultsHash.sort_by(&:id). But don't do it this way.
Side note, use snake_case for Ruby code, not camelCase.
Edit: See comments.
resultsHash.sort.reverse
Note that it'll return an Array of [key, value] pairs. But you can still iterate like with a Hash:
resultsHash.sort.reverse.each do |key, value|
....
end
Or you can retrieve only the values: resultsHash.sort.reverse.map { |key, value| value }, or resultsHash.sort.reverse.map(&:last)
I realize this is an old question, but one way you can do exactly what you want while still retaining a return type of Hash is to use .slice on the sorted keys:
resultHash.slice(*resultHash.keys.sort)

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