similar to ampersand for accessing item in an array in ruby - ruby-on-rails

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"}

Related

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"}]

Rails Strong Parameters either in root or nested object

I want to be able to support a set of parameters in either the root params object:
?foo=a
params.permit(:foo)
Or in a nested object:
?bar[foo]=a
params.require(:bar).permit(:foo)
I'm passing parameters into that controller from a form object, so the nested object happens naturally, but I'd like to also be able to pass the params in the root object in the query string and support both.
Is there an elegant, non-hacky way to accomplish this?
require is like [] with an exception when the value is missing (much like Hash#fetch). You can instead just use [] directly to get the nested structure if it's there, and fall back to the root params otherwise:
(params[:bar] || params).permit(:foo)
remember that require is different as permit.
The require method ensures that a specific parameter is present. is not necessary to use on nested ones, it makes sure that the required one come in the params or it will throw an error.
you can work with nested attributes like this
params = ActionController::Parameters.new({
person: {
name: "Francesco",
age: 22,
pets: [{
name: "Purplish",
category: "dogs"
}]
}
})
permitted = params.permit(person: [ :name, { pets: :name } ])
permitted.permitted? # => true
permitted[:person][:name] # => "Francesco"
permitted[:person][:age] # => nil
permitted[:person][:pets][0][:name] # => "Purplish"
permitted[:person][:pets][0][:category] # => nil
you can check the documentation on this here

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"]]

RABL - Render collection as object with the ids as keys

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"}

Allow an array of hashes with a dynamic hash (hstore) inside

I'm stuck with strong_parameters and this array of hashes with a dynamic hash (hstore) inside.
The structure is the following:
{ contact_sources: [
{ id: 1, filled_fields: { randomstuff: 'randomdata', dunno: 123 } },
{ id: 2, filled_fields: { blah: 'blabla', dunno: 9043 } }
] }
So, my main attempt is the following:
params.permit(contact_sources: [{:filled_fields => []}, 'id'])
Which doesn't return filled_fields. Any suggestion on how to deal with it?
Update 1:
I have the following model:
class ContactSource < ActiveRecord::Base
# Fields: id:integer, filled_fields:hstore
end
In my action, I'm submitting multiple records at once (mass update), so I have an array of contact_source, but actually they don't belong to anything, it's just a mass update.
Looks like it's not possible to do it with "plain" strong_parameters syntax. The only option you have is to actually, after filtering, re-add those values with a loop. I know it's terrible but it's the only way right now. I submitted a bug to Rails actually.

Resources