Slicing params hash for specific values
I changed by mind. The previous one doesn't seem to be any good.
class Hash
def slice1(*keys)
keys.each_with_object({}){|k, h| h[k] = self[k]}
end
def slice2(*keys)
h = {}
keys.each{|k| h[k] = self[k]}
h
end
end
Editing params nested hash
still keep the output as a params hash (still containing nested hashes arrays
Sure.
You'll have to manipulate the params
hash, which is done in the controller.
Whilst I don't have lots of experience with this I just spent a bunch of time testing -- you can use a blend of the ActionController::Parameters
class and then using gsub!
-- like this:
#app/controllers/your_controller.rb
class YourController < ApplicationController
before_action :set_params, only: :create
def create
# Params are passed from the browser request
@model = Model.new params_hash
end
private
def params_hash
params.require(:x).permit(:y).each do |k,v|
v.gsub!(/[regex]/, 'string')
end
end
end
I tested this on one of our test apps, and it worked perfectly:
--
There are several important points.
Firstly, when you call a strong_params
hash, params.permit
creates a new hash out of the passed params. This means you can't just modify the passed params with params[:description] =
etc. You have to do it to the permitted params.
Secondly, I could only get the .each
block working with a bang-operator (gsub!
), as this changes the value directly. I'd have to spend more time to work out how to do more elaborate changes.
--
Update
If you wanted to include nested hashes, you'd have to call another loop:
def params_hash
params.require(:x).permit(:y).each do |k,v|
if /_attributes/ ~= k
k.each do |deep_k, deep_v|
deep_v.gsub!(/[regex]/, 'string'
end
else
v.gsub!(/[regex]/, 'string')
end
end
end
More-efficient way to pass the Rails params hash to named route
Refactored the helper function:
def options(hash)
o={:employer=>params[:employer],:location=>params[:location],:posted=>params[:posted],:starting=>params[:starting],:sort=>params[:sort],:dir=>params[:dir],:fav=>params[:fav]}
# add the contents of hash, overwriting entries with duplicate keys
o.merge!(hash)
end
Refactored the view code to pass hash instead of key/value pair; greater flexibility:
<%= link_to_unless params[:employer]==employer, employer, jobs_path( options({:employer=>employer}) ) %>
and
<%= link_to_unless_current '✗', jobs_path( options({:employer=>nil}) ) %>
Rails: except vs slice to take out certain parameter?
Using except
with parameter hashes is dangerous because you're saying "allow any parameter other than this one" so it opens your model up to mass-assignment attacks.
Unless I'm not thinking of another attack vector you should be fine, because you're filtering your acceptable parameter hash with strong_parameters in your whiteList_function
. So, you're fine further filtering that hash with either slice
or except
, whichever is more appealing to you.
Rails 5: unable to retrieve hash values from parameter
take a look to this. Very weird since ActionController::Parameters
is a subclass of Hash, you can convert it directly to a hash using the to_h
method on the params hash.
However to_h
only will work with whitelisted params, so you can do something like:
permitted = params.require(:line_item).permit(: line_item_attributes_attributes)
attributes = permitted.to_h || {}
attributes.values
But if instead you do not want to whitelist then you just need to use the to_unsafe_h
method.
Update
I was very curious about this issue, so I started researching, and now that you clarified that you are using Rails 5, well that's the cause of this issue, as @tillmo said in stable releases of Rails like 4.x, ActionController::Parameters
is a subclass of Hash, so it should indeed respond to the values
method, however in Rails 5 ActionController::Parameters
now returns an Object instead of a Hash
Note: this doesn’t affect accessing the keys in the params hash like params[:id]
. You can view the Pull Request that implemented this change.
To access the parameters in the object you can add to_h
to the parameters:
params.to_h
If we look at the to_h
method in ActionController::Parameters
we can see it checks if the parameters are permitted before converting them to a hash.
# actionpack/lib/action_controller/metal/strong_parameters.rb
def to_h
if permitted?
@parameters.to_h
else
slice(*self.class.always_permitted_parameters).permit!.to_h
end
end
for example:
def do_something_with_params
params.slice(:param_1, :param_2)
end
Which would return:
{ :param_1 => "a", :param_2 => "2" }
But now that will return an ActionController::Parameters
object.
Calling to_h
on this would return an empty hash because param_1 and param_2 aren’t permitted.
To get access to the params from ActionController::Parameters
, you need to first permit the params and then call to_h
on the object
def do_something_with_params
params.permit([:param_1, :param_2]).to_h
end
The above would return a hash with the params you just permitted, but if you do not want to permit the params and want to skip that step there is another way using to_unsafe_hash
method:
def do_something_with_params
params.to_unsafe_h.slice(:param_1, :param_2)
end
There is a way of always permit the params from a configuration from application.rb, if you want to always allow certain parameters you can set a configuration option. Note: this will return the hash with string keys, not symbol keys.
#controller and action are parameters that are always permitter by default, but you need to add it in this config.
config.always_permitted_parameters = %w( controller action param_1 param_2)
Now you can access the params like:
def do_something_with_params
params.slice("param_1", "param_2").to_h
end
Note that now the keys are strings and not symbols.
Hope this helps you to understand the root of your issue.
Source: eileen.codes
Ruby: Easiest Way to Filter Hash Keys?
Edit to original answer: Even though this is answer (as of the time of this comment) is the selected answer, the original version of this answer is outdated.
I'm adding an update here to help others avoid getting sidetracked by this answer like I did.
As the other answer mentions, Ruby >= 2.5 added the Hash#slice
method which was previously only available in Rails.
Example:
> { one: 1, two: 2, three: 3 }.slice(:one, :two)
=> {:one=>1, :two=>2}
End of edit. What follows is the original answer which I guess will be useful if you're on Ruby < 2.5 without Rails, although I imagine that case is pretty uncommon at this point.
If you're using Ruby, you can use the select
method. You'll need to convert the key from a Symbol to a String to do the regexp match. This will give you a new Hash with just the choices in it.
choices = params.select { |key, value| key.to_s.match(/^choice\d+/) }
or you can use delete_if
and modify the existing Hash e.g.
params.delete_if { |key, value| !key.to_s.match(/choice\d+/) }
or if it is just the keys and not the values you want then you can do:
params.keys.select { |key| key.to_s.match(/^choice\d+/) }
and this will give the just an Array of the keys e.g. [:choice1, :choice2, :choice3]
Is there some method or an easy way so that I can return a hash that includes only the given keys?
I think what you are looking for is slice
, which will return a hash with only the given keys.
How Can I extract the compete hash of parameters sent along with controler call in rails?
Rails parameters are instances of HashWithIndifferentAccess
, a subclass of Hash
.
params.class
# => HashWithIndifferentAccess
params.is_a? Hash
# => true
You can get the entire Hash
with params
:
params
# => {"id"=>"aom7v66e309yjkd2x0aq", "video_type"=>"trailer"}
Or with #to_hash
if you need a plain Hash
:
params.to_hash
# => {"id"=>"aom7v66e309yjkd2x0aq", "video_type"=>"trailer"}
params.to_hash.class
# => Hash
The keys with #keys
:
params.keys
# => ["id", "video_type"]
And the values with #values
:
params.values
# => ["aom7v66e309yjkd2x0aq", "trailer"]
To extract specific keys, you can use #slice
:
params = {"a"=>1, "b"=>2, "c"=>3}
params.slice("a", "c")
# => {"a"=>1, "c"=>3}
Or its counterpart #except
:
params = {"a"=>1, "b"=>2, "c"=>3}
params.except("b")
# => {"a"=>1, "c"=>3}
Related Topics
Error Installing SQLite3 Gem via Bundler
If 'Main' Is an Instance of 'Object', Why Can't I Call It
Missing File in Gem After Build
Which Equality Test Does Ruby's Hash Use When Comparing Keys
Capistrano 3 Change Ssh_Options Inside Task
Rails Cannot Load Such File -- MySQL2/Mysql2 (Loaderror)
Access Current Git Commit Number from Within Heroku App
What Are the Ruby Win32API Parameters | How to Pass a Null Pointer
Override "Show" Resource Route in Rails
Is There Some Kind of Unseen Array Termination in Ruby
How to Specify the Location of the Chromedriver Binary
Ruby: Building a Plot of Function
How to Print Stdout Immediately
How to Access the Current Node from a Library in a Chef Cookbook