How to require an Array in Rails when using strong parameters? - ruby-on-rails

Is there a way to require an array when using strong parameters in Rails 4?
> params = ActionController::Parameters.new(contacts: [])
=> {"contacts"=>[]}
> params.require(:contacts)
ActionController::ParameterMissing: param not found: contacts

As Steve Wilhelm noted, it works if the array is non-empty. It only fails on your example because the contacts array is empty. But that's usually the desired behavior.
If you don't care what's in the array, just use permit.
That said, I'd imagine the most common case is that you want an array of hashes with known keys. I would do that this way:
# Returns an array of contacts after checking the params shape
# Use instead of params[:contacts]
def contacts_params
params.permit(contacts: %i(id name phone address))
params.require(:contacts)
end

It appears you can have arrays of Scalars, This works
> params = ActionController::Parameters.new(contacts: [nil])
=> {"contacts"=>[nil]}
> params.require(:contacts)
=> [nil]
> params = ActionController::Parameters.new(contacts: [1])
=> {"contacts"=>[1]}
> params.require(:contacts)
=> [1]
Here is the description from documentation
The permitted scalar types are String, Symbol, NilClass, Numeric, TrueClass, FalseClass, Date, Time, DateTime, StringIO, IO, ActionDispatch::Http::UploadedFile and Rack::Test::UploadedFile.
To declare that the value in params must be an array of permitted scalar values map the key to an empty array:
params.permit(id: [])

Have you tried this
params.permit(contacts: []).require(:contacts)

Related

How to pass parameter as hash in Rails api with GET?

I try to pass params as a hash in url with postman like this
http://localhost:3000/api/v1/bill?album=3&song=4&song=7&album=6
I use this code to get param
def param_hash
params.permit(:album, :song)
end
and print this value param_hash.to_h
This is a value i want {"album"=3, "song"=>4, "album"=>6, "song"=>7}
But in reality that's what i got {"album"=>6, "song"=>7} , just have only 1 hash in last.
Is there anyway to take all hash value in url?
def param_hash
params.permit(album: [], song: [])
end
http://localhost:3000/api/v1/bill?album[]=3&song[]=4&song[]=7&album[]=6
According to https://github.com/rails/strong_parameters
The permitted scalar types are String, Symbol, NilClass, Numeric, TrueClass, FalseClass, Date, Time, DateTime, StringIO, IO, ActionDispatch::Http::UploadedFile and Rack::Test::UploadedFile.
To declare that the value in params must be an array of permitted scalar values map the key to an empty array:
params.permit(:id => [])
http://localhost:3000/api/v1/bill?data={album: 3,song: 4,song: 7,album: 6 }
Use the above logic and permit the 'data' object.

How to pass an array of arrays in GET API in Ruby on Rails

I am using a GET API, currently passing an array as a string:
def fetch_details ids
url = "#{url}/api/v1/get-info?ids=#{ids.join(',')}"
response = Net::HTTP.get_response(URI.parse(URI.encode(url)))
if response.code.to_i == 200
return Oj.load(response.body)
else
return {}
end
end
On the server-side I am extracting id from this method:
def self.get_details(ids)
ids = ids.split(",").map {|x| x.gsub( " ", "")}
end
For each id, I want to send an array of UUIDs:
ids = [100,21,301]
uuids= {["abc","bca"],["Xyz"],["pqr","345"]}
Something like this
hash=[
100=>[abc,bca],
21=>[xyz],
301=>[pqr,345]
]
The endpoint uses the id and corresponding UUIDs to join two tables in database query so I should be able to extract the id and corresponding UUID at the end.
How do I pass both these values?
To pass an array in the parameters in Rails/Rack you need to add brackets to the name and repeat the parameter:
/api/v1/get-info?ids[]=1&ids[]=2&ids[]=3
You can use Hash#to_query from ActiveSupport to generate the query string:
irb(main):001:0> { ids: [1,2,3] }.to_query
=> "ids%5B%5D=1&ids%5B%5D=2&ids%5B%5D=3"
As pointed out by #3limin4t0r you should only use this for one-dimensional arrays of simple values like strings and numbers.
To pass a hash you use brackets but with keys in the brackets:
/api/v1/get-info?foo[bar]=1&foo[baz]=2
Again you can generate the query string with #to_query:
irb(main):002:0> { foo: { bar: 1, baz: 2 } }.to_query
=> "foo%5Bbar%5D=1&foo%5Bbaz%5D=2"
The keys can actually be numbers as well and that should be used to pass complex structures like multidimensional arrays or an array of hashes.

Pluck doesn't return a bother records they have the same name

I have a pluck that is turned into a hash and stored in a variable
#keys_values_hash = Hash[CategoryItemValue.where(category_item_id: #category_item.id).pluck(:key, :value)]
If 2 records have the same :key name then only the most recent record is used and they aren't both added to the hash. But if they have the same value and different keys both are added to the hash.
This also occurs if I swap :key and :value around (.pluck(:value, :key)). If they have now the same value it only uses the most recent one and stores that in the hash. But having the same key is now fine.
I'm not sure of this is being caused by pluck or from being sorted in a hash. I'm leaning towards pluck being the culprit.
What is causing this and how can I stop it from happening. I don't want data being skipped if it has the same key name as another record.
I'm not sure why you need convert pluck result into a Hash, because it was an Array original.
Like you have three CategoryItemValue like below:
id, key, value
1, foo, bar1
2, foo, bar2
3, baz, bar3
when you pluck them directly, you will get a array like:
[ ['foo', 'bar1'], ['foo', 'bar2'], ['baz', 'bar3'] ]
but when you convert it into a hash, you will get:
{'foo' => 'bar2', 'baz' => 'bar3' }
because new hash value will override the old one if key ( foo in the example above) exists.
Or you could try Enumerable#group_by method:
CategoryItemValue.where(...).pluck(:key, :value).group_by { |arr| arr[0] }

Get Model's Parameters in Ruby

I'm trying to build a generator in rails but I'm getting stuck at trying to access an existing model's parameters. Basically I want to do something like this:
# user is a model the has the parameters "id: integer, name: string, and email: string"
User.parameters.each do |parameter|
# do something with id, name, email
parameter.key
# do something with integer, string, string
parameter.value
end
Any ideas?
I think what you want is this
User.columns_hash.each do |key, value|
key
value.type
end
value.type will give you the type as a symbol. You can convert it to a string if you want it as a string
I think you are looking for attributes, rather than parameters.
user = User.first
user.attributes.each do |key, value|
# do something here with keys and values
puts key if key
puts value if value
end
Notice I'm grabbing an actual instance of the model as well as checking for nils (the if key/ if value part)
Got it! Need to use columns_hash like this:
Event.columns_hash.each {|k,v| puts "#{k} => #{v.type}"}
Credit to Getting types of the attributes in an ActiveRecord object

Rails group by id uses a string for hash key

My code looks like this:
hash = MyModel.count(:group => 'id', :conditions => 'bla = "bla"')
The returned Hash has keys that are strings. I want them to be ints. I know it would be possible to convert the Hash manually using something like a map construct.
Edit:
Thanks for the responses. Have realised it was a json conversion process that was turning the ids into Strings and rails does in fact use the Fixnum as one might expect.
hash = MyModel.count(group: 'id', conditions: 'bla = "bla"')
should have Fixnum keys by default since id is an instance of Fixnum.
What happens is that ActiveRecord always fetch result as strings and then Rails takes care of converting them to other datatypes according to the type of the database column (we say that they are typecast).
So it's maybe a Rails bug or the 'id' column is not set as integer(which would be surprising).
If you can't fix it, convert them manually:
hash.each_with_object({}) do |(key, value), hash|
hash[key.to_i] = value
end
When I use your code I get integer keys (rails 3.07), what's the column type of id?
If you want to do it manually:
new_hash = hash.inject({}){|h,a| h[a.first.to_i] = a.last; h}
new_hash = Hash[hash.map { |k, v| [k.to_i, v] }

Resources