Permitting array of hashes rails 5 - ruby-on-rails

My rails version is 5 and I have request param like this,
{ "segment": {
"name": "test",
"new_filters": [
{"criteria": "sad",
"other_keys": [{"key": "value"}]
},
{"criteria": "sad",
"other_keys1": [{"key1": "value1"}]
}]
}
}
I am stuck in permitting the new_filter params in rails controller,
I am trying below code,
params.require(:segment).permit(:name, :people_count, new_filters: [])
and still getting the error. But this is not the case while having array of strings in new_filter key.
Eg: ["sad", "asdasd"]. How to get the nested structure as whitelisted attribute?

This worked for me when testing with your attributes:
params.require(:segment).permit(:name, :people_count, new_filters: [:criteria, other_keys: [:key], other_keys1: [:key1]])

In your model file for segment try adding the line
accepts_nested_attributes_for :new_filters
this should allow you to pass those attributes.
https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

Related

How can I store random nested variables with strong params methods in Rails?

I have the following class:
class ArticlesController < ApplicationController
def create
article = Article.new(article_params)
end
private
def article_params
params.permit(:name, :age, book: [])
end
end
and I have a field called book that contains a collection followed by a hash [{...}], inside the hash object it can contain any random attribute, for example:
book_1 =
[
{
"id": "a1",
"type": "Color",
"title": "Live life cicle",
"content": "image_intro.png"
},
]
book_2 =
[
{
"id": "a2",
"email": "example#gmail.com",
"domain": "http://ddd.com"
}
]
...
book_7
[
{
"id": "a23",
"width": "3px",
"heigth": "5px",
"exist": true
}
]
What I would like is that every time I save a book, it can go through article_params no matter what attributes it contains within the hash, if you could help me please I would be grateful.
ActionController::Parameters does not have a "wildcard" syntax to allow any nested hash keys. But it does have #permit! which is an acknowledgement that strong parameters is not the solution for every possible problem.
permit! completely circumvents whitelisting by setting the permitted attribute on the ActionController::Parameters instance.
It will also set the permitted attribute on any nested instances of ActionController::Parameters - ie nested hashes in the parameters.
This is a very sharp tool which should be used with care.
In this case you might want to just use it on the nested attributes:
params.permit(:name, :age).merge(
books: params.dup.permit!.fetch(:books, [])
)

strong params JSON API specification

I am working to an REST API in Rails 5.2 and I'm following the JSON API specification. My JSON are like this:
{
"data":{
"attributes":{
"name":"Gleydson",
"age":"30"
},
"relationships":{
"occupation":{
"data":{
"attributes": {
"name": "Teacher",
}
}
}
}
}
}
I have the following method user_params:
params.require(:data).require(:attributes)
.permit(:name, :age,
relationships: { occupation: { data: { attributes: [:name] } } })
but when I print the return of the method nothing is returned
Have you looked into using a gem to go from Ruby objects to JSON:API serialized ones. JSONAPI::Resources helps a lot with getting the params right since you just define a JSONAPI:Resource and tell it what to allow.
I think part of your problem is that in JSON:API the attributes and relationships keys are siblings
user_params: {data:{attributes:{name:x,age:y,relationships:{z}}}}
jsonapi is: {data:{attributes:{name:x,age:y},relationships:{z}}}
In the JSON API spec "attributes" and "relationships" are siblings.
In your example you have nested "relationships" inside of "attributes".
Instead try:
params.require(:data).permit({
attributes: [:name, :age],
relationships: {
occupation: {
data: {
attributes: [:name]
}
}
}
})
According to the spec, attributes should not show up under a relationship's data member. A relationship's data member is a resource identifier object, meaning it only (and must only) contain type and id.
For JSON:API compliance checking, you may want to check out the new ruby gem easy-jsonapi. It has a middleware and response validator that is super easy to use with intuitive error messages to tell you why your documents, requests, and responses are not compliant. It also pairs well with JSONAPI-SERIALIZER, which as #robertoplancarte was suggesting, could take your ruby objects and serialize them into JSON:API compliant JSON documents.

Rails strong params with "dot" in name

I need to permit a parameter in Rails 4 which has a dot in it's name:
My params hash looks like the following:
{
"dictionary_objects.id" => [
"102", "110", "106"
]
}
I can get param value:
>> params['dictionary_objects.id']
=> [
[0] "102",
[1] "110",
[2] "106"
]
But when I try to permit it, it returns an empty hash:
>> params.permit('dictionary_objects.id')
Unpermitted parameters: dictionary_objects.id
=> {}
Does anybody know how can I permit params with a dot in it's name?
Thanks.
I think it's just failing to permit it because you've got a collection and you're telling it to permit a single value parameter. If you use:
params.permit(:'dictionary_objects.id' => [])
then all should be well.
for edge cases I recommend a very useful workaround:
params.slice('dictionary_objects.id').permit!
So you do whitelist keys and dont become crazy because of strong params.
sidenote:
rails is builtin to receive args like dictionary_object_ids for has_many relationships, you could leverage this instead.

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.

Rails - Strong Parameters - Nested Objects

I've got a pretty simple question. But haven't found a solution so far.
So here's the JSON string I send to the server:
{
"name" : "abc",
"groundtruth" : {
"type" : "Point",
"coordinates" : [ 2.4, 6 ]
}
}
Using the new permit method, I've got:
params.require(:measurement).permit(:name, :groundtruth)
This throws no errors, but the created database entry contains null instead of the groundtruth value.
If I just set:
params.require(:measurement).permit!
Everything get's saved as expected, but of course, this kills the security provided by strong parameters.
I've found solutions, how to permit arrays, but not a single example using nested objects. This must be possible somehow, since it should be a pretty common use case. So, how does it work?
As odd as it sound when you want to permit nested attributes you do specify the attributes of nested object within an array. In your case it would be
Update as suggested by #RafaelOliveira
params.require(:measurement)
.permit(:name, :groundtruth => [:type, :coordinates => []])
On the other hand if you want nested of multiple objects then you wrap it inside a hash… like this
params.require(:foo).permit(:bar, {:baz => [:x, :y]})
Rails actually have pretty good documentation on this: http://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-permit
For further clarification, you could look at the implementation of permit and strong_parameters itself: https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/strong_parameters.rb#L246-L247
I found this suggestion useful in my case:
def product_params
params.require(:product).permit(:name).tap do |whitelisted|
whitelisted[:data] = params[:product][:data]
end
end
Check this link of Xavier's comment on github.
This approach whitelists the entire params[:measurement][:groundtruth] object.
Using the original questions attributes:
def product_params
params.require(:measurement).permit(:name, :groundtruth).tap do |whitelisted|
whitelisted[:groundtruth] = params[:measurement][:groundtruth]
end
end
Permitting a nested object :
params.permit( {:school => [:id , :name]},
{:student => [:id,
:name,
:address,
:city]},
{:records => [:marks, :subject]})
If it is Rails 5, because of new hash notation:
params.permit(:name, groundtruth: [:type, coordinates:[]]) will work fine.

Resources