How to get just GET and POST params as hash in Rails? - ruby-on-rails

I would like to get any additional GET and POST params in Rails without Rails' own additions such as controller, format, etc...
params.inspect gave me what I want but it has some keys that Rails adds for me such as controller or format. I only want to get user input GET and POST params as hash. How can I do that? I cannot find anything.
URL:
http:/test.com/some/path?query1=1&query2=1
Run:
puts params.inspect
Expected:
{"query1"=>"1", "query2"=>"1"}
Actual:
{"query1"=>"1", "query2"=>"1", "format"=>":json", "controller"=>"get", "action"=>"index", "folder"=>"some/path"}
Also this can be combined with POST request. I only want to filter them and only get them as hash.
I execute this from inside of a controller. Rails 5 used.

You should have permitted params (Strong parameters).
In your controller have permitted params method.
def your_params
params.permit(:query1, :query2)
end
If you wish to have a hash of those you can do
your_params.to_h #This will return hash of permitted params
Update:
Incase you have multiple query* type of parameters you can select them and permit!. Here is a command line explanation
# passing the list of parameters
> params = ActionController::Parameters.new({query1: 'aa', query2: 'bb', query3: 'cc', controller: 'users', action: 'index'})
=> <ActionController::Parameters {"query1"=>"aa", "query2"=>"bb", "query3"=>"cc", "controller"=>"users", "action"=>"index"} permitted: false>
# Select all the parameters of type query<integer>
> all_queries = params.select {|i| i.match?(/query\d/) }
=> <ActionController::Parameters {"query1"=>"aa", "query2"=>"bb", "query3"=>"cc"} permitted: false>
# mark the selected as permitted
> all_queries.permit!
=> <ActionController::Parameters {"query1"=>"aa", "query2"=>"bb", "query3"=>"cc"} permitted: true>
# get them as hash
> all_queries.to_h
=> {"query1"=>"aa", "query2"=>"bb", "query3"=>"cc"}
Controller method will look like
# getting all query<integer> like params
def your_params
params.select {|param| param.match?(/query\d/}.permit!
end

In the controller, you have access to a request object. request has a method query_parameters, which will return a hash of just the explicitly supplied parameters.
`request.query_parameters`
http://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-query_parameters

Related

undefined method `map' for #<ActionController::Parameters Rails 6.0

Getting this error when trying to call the method validate_create_action. The error occurs on the line #element = klass.new(params[:element]).
NoMethodError in Analyses::MitigationMatricesController#validate_create_action
undefined method `map' for #<ActionController::Parameters {"0"=>{"phase"=>"Launch", "reason"=>"test"}} permitted: false>
Did you mean? tap
Extracted source (around line #7):
#5 class ManyEmbeddedProxy < EmbeddedCollection
#6 def replace(values)
*7 #_values = (values || []).compact.map do |v|
#8 v.respond_to?(:attributes) ? v.attributes : v
#9 end
#10 reset
In the post request the element object below is sent to the backend. The issue seems to be the array critical_phases.
element[applicable_during][critical_phases][0][phase] "Launch"
element[applicable_during][critical_phases][0][reason] "test"
If I add the method to_unsafe_h to params[:element].to_unsafe_h I get a different error involving the critical_phases array saying "TypeError: no implicit conversion of String into Integer". Any ideas on how to solve this?
element_controller.rb
def validate_create_action
rejigger_dynamic_lists
remove_associations_from_params
#element = klass.new(params[:element])
...
def klass
unless #klass
klass_str = params[:controller].split("/").last.singularize.camelize
namespace_str = SITE_CONFIG['adaptation_name'].camelize
#klass = "#{namespace_str}::#{klass_str}".constantize
end
#klass
end
UPDATE:
I updated my code to the working code
params[:element].permit!
params2 = ActionController::Parameters.new(params[:element][:applicable_during][:critical_phases].to_h)
params2.permit!
params[:element][:applicable_during][:critical_phases] = params2.values.map do |h|
ActionController::Parameters.new(h.permit!.to_h).permit(:phase, :reason) # use permit! instead if you want to live dangerously
test_mapping = {}
end
The problem here is that it the parameters aren't actually an array. Its a hash with the keys "0", "1" etc. This is done to avoid the potential ambigiuty thats happens when Rack parses form data keys containing arrays of hashes:
irb(main):015:0> Rack::Utils.parse_nested_query("foo[][bar]=1&foo[][bar]=2")
=> {"foo"=>[{"bar"=>"1"}, {"bar"=>"2"}]}
# should this be one or two hashes? Discuss
irb(main):016:0> Rack::Utils.parse_nested_query("foo[][bar]=1&foo[][baz]=2")
=> {"foo"=>[{"bar"=>"1", "baz"=>"2"}]}
irb(main):017:0> Rack::Utils.parse_nested_query("foo[0][bar]=1&foo[1][baz]=2")
=> {"foo"=>{"0"=>{"bar"=>"1"}, "1"=>{"baz"=>"2"}}}
Typically normalizing this is handled by accepts_nested_attributes_for. But what you're doing doesn't look the least bit ordinary.
If you want to jury rig a system that processes this and returns an array of whitelisted hashes you could do:
params = ActionController::Parameters.new("0"=>{"phase"=>"Launch", "reason"=>"test"}, "1"=>{"phase"=>"Foo", "reason"=>"bar"})
params.values.map do |h|
ActionController::Parameters.new(h)
.permit(:phase, :reason) # use permit! instead if you want to live dangerously
end
# [<ActionController::Parameters {"phase"=>"Launch", "reason"=>"test"} permitted: true>, <ActionController::Parameters {"phase"=>"Foo", "reason"=>"bar"} permitted: true>]

Rails: reshape params to desired structure

I have some params that I need to reshape?
I've got an old database I'm trying to build a new app on top of to access and do CRUD operations on.
I simply need to make this structure...
{
"volunteer_shift_attributes"=><ActionController::Parameters{
"volunteer_task_type_id"=>"41",
"roster_id"=>"7",
"program_id"=>"9",
"set_description"=>"ddddddddddd"
} permitted: true>,
"set_date"=>"2021-01-14",
"contact_id"=>"166574",
"closed"=>"0",
"start_time(4i)"=>"14",
"start_time(5i)"=>"00",
"end_time(4i)"=>"15",
"end_time(5i)"=>"00",
"notes"=>"nnnnnnnnnnnnn",
}
have this structure...
{
"volunteer_shift_attributes"=>{
"volunteer_task_type_id"=>"41",
"roster_id"=>"7",
"program_id"=>"9",
"set_description"=>"ddddddddddd"
},
"set_date"=>"2021-01-15",
"contact_id"=>"166574",
"closed"=>"0",
"start_time(4i)"=>"14",
"start_time(5i)"=>"00",
"end_time(4i)"=>"15",
"end_time(5i)"=>"00",
"notes"=>"aaaaaaaaaaaaaa"
}
NOTE: this is called inside a controller method like so
def create_shift
...
a.attributes = (params["assignment"])
...
end
I need to rebuild this param by hand.
It's not clear exactly why you need to do this - they're just params and you're not looking to change the structure, exactly. But it looks like it could be a case for strong params. Define a private method:
def volunteer_shift_params
params.require(:volunteer_shift_attributes)
.permit(:volunteer_task_type_id,
:roster_id,
:program_id,
:set_description)
end
Then, you should be able to do this, as you asked:
a.attributes = volunteer_shift_params

How can I access the data for the snake object sent through JSON in my params?

Using javascript, I make a fetch post.
const game = {name: this.player, snake: this.snake, score: this.score, apple: this.apple, skull: this.skull, completed: this.completed}
return fetch("http://localhost:3000/games", {
method: "POST",
headers: {
"Content-Type": "application/json"},
body: JSON.stringify(game)
})
.then(resp => resp.json())
.then(json => Game.appendHighScores(json));
I access the data via params on the Ruby on Rails end. The data for params[:snake][:body] are supposed to look like "body"=>[{"x"=>16, "y"=>15}, {"x"=>16, "y"=>14}, {"x"=>16, "y"=>15}]}, yet when I type them into the command line, they look like this:
[<ActionController::Parameters {"x"=>16, "y"=>15} permitted: false>, <ActionController::Parameters {"x"=>16, "y"=>14} permitted: false>, <ActionController::Parameters {"x"=>16, "y"=>15} permitted: false>]
It is accessible via indexing, but I get everything along with the data I'm looking for.
I was hoping it would look like the original params when I typed it in
<ActionController::Parameters {"name"=>"Don", "snake"=>{"x"=>16, "y"=>15, "direction"=>"down", "speed"=>0, "directions"=>{"left"=>{"x"=>-1, "y"=>0}, "up"=>{"x"=>0, "y"=>-1}, "right"=>{"x"=>1, "y"=>0}, "down"=>{"x"=>0, "y"=>1}}, "image"=>{}, "body"=>[{"x"=>16, "y"=>15}, {"x"=>16, "y"=>14}, {"x"=>16, "y"=>15}]}, "score"=>0, "apple"=>{"image"=>{}, "x"=>2, "y"=>10}, "skull"=>{"image"=>{}, "x"=>12, "y"=>12}, "completed"=>true, "controller"=>"games", "action"=>"create", "game"=>{"score"=>0, "skull"=>{"image"=>{}, "x"=>12, "y"=>12}, "apple"=>{"image"=>{}, "x"=>2, "y"=>10}, "completed"=>true}} permitted: false>
Anyway to get the params as an array without it looking so messy with ActionController::Parameters inside of the element?
The reason everything is wrapped inside ActionController::Parameters is for your security (mass assignment in particular). You should never trust data send from the internet. This class allows you to permit/whitelist what properties you trust and filter out everything that you don't trust.
snake_params = params.require(:snake).permit(body: [:x, :y])
You can then convert this into a hash with a simple to_h call, which will drill down into all other nested parameters that are also permitted.
snake_data = snake_params.to_h
#=> { "body" => [{"x"=>16, "y"=>15}, {"x"=>16, "y"=>14}, {"x"=>16, "y"=>15}] }
If you'd like to include other attributes as well you can add them to the permit list.
.permit(:direction, :speed, directions: {left: [:x, :y], up: [:x, :y], ...}, body: [:x, :y])
For more info about permit I suggest checking out the guide Action Controller Overview - 4.5 Strong Parameters.
If you don't care about permitting certain parameters you can permit everything with permit!.
Note that you don't have to permit parameters if you extract the values directly. The code below would work perfectly fine without permitting anything.
body = params[:snake][:body]
body.each |body_part|
x = body_part[:x]
y = body_part[:y]
// do stuff with x and y
end
Because Rails utilize "strong parameters" you have to list and allow the parameters in the controller you want to use.
Since you have a list of parameters with dynamic keys the easiest way to do, while you are in development mode is to permit them like this:
# in your controller
def game_params
params.require(:game).tap do |permitted|
permitted[:name] = params[:name].permit!
permitted[:snake] = params[:snake].permit!
permitted[:apple] = params[:apple].permit!
permitted[:skull] = params[:skull].permit!
permitted[:completed] = params[:completed].permit!
end
end
and then you will be able to access your params:
game_params[:snake][:body]
In production tho, I will encourage you to whitelist all of the keys, for example like this.

How do you assign a value to a key from a parameter into a ActionController::Parameters object

How do you assign a value to a key from a parameter into a ActionController::Parameters object?
What I tried:
(byebug) foo_inquiry_params
<ActionController::Parameters {"email"=>"fds#fsdgf.com", "subject"=>"fds", "description"=>"fds"} permitted: true>
(byebug) name_inquiry_params["user_id"] = 5
5
(byebug) foo_inquiry_params
<ActionController::Parameters {"email"=>"fds#fsdgf.com", "subject"=>"fds", "description"=>"fds"} permitted: true>
(byebug)
ActionController::Parameters can't be treated like a normal hash, so if you want to call .merge on it, you'll have to convert your params to a hash first:
ActionController::Parameters.new({foo: 'bar'}).to_h.merge({bar: 'baz'})
A workflow that I find myself using when I have to do this kind of thing is:
def update
attrs_to_update = thing_params.to_h.merge(one_more_attribute: 'some value')
#thing.update(attrs_to_update)
end
def thing_params
params.require(:thing).permit(:some_attribute, :another_attribute)
end

Why Strong Params contains permitted: false

I put in a binding.pry at the top of my controller's update action. Once at that break point, I put in params[:foo_bar] to examine the params hash. Here is what I get:
<ActionController::Parameters {"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"123==", "foobar"=><ActionController::Parameters {"barbazz_attributes"=>{"start_date"=>"08/27/2016", "end_date"=>"08/29/2016", "id"=>"89"}, "bazz_id"=>"3", "abc_id"=>"330", "bazzbazz_attributes"=>{"0"=>{"_destroy"=>"1", "city_id"=>"1669", "id"=>"26"}, "1"=>{"city_id"=>"1681", "id"=>"27"}, "2"=>{"city_id"=>"1672"}}} permitted: false>, "cat_id"=>["1", "1", "1"], "commit"=>"Update FooBar", "controller"=>"foo_bars", "action"=>"update", "id"=>"52"} permitted: false>
I assumed permitted: false is there because I did not whitelist some attributes. I looked over the attributes and it appears to me that I did whitelist everything.
I am using Rails 5 if that happens to make any difference.
Question: What is an easy way to find out why the strong parameters are returning params: false.
Don't access params directly with params instead use the name you gave your permitted params, for example: foobar_params.
If foobar_params is defined:
def foobar_params
params.require(:foobar).permit ...
end
The easiest way is to read the source code for ActionController::Parameter, permitted = false is the default unless you call permit! to allow all, but that defeats the purpose of strong parameters.

Resources