Pass array of parameters to a link - ruby-on-rails

Consider the following rails link:
search_path(:query => params[:query], type: params[:type], sort: params[:sort])
There is a lot of duplication here. Is it possible to define these parameters in an array and they pass into the link? Eg.
params: [:query, :type, :sort] # -> pass each into the link like "key: value"

I can't think of how you could do it exactly passing it as an array like you show, however you could do something like:
search_path(params.slice(:query, :type, :sort))
This will give you the same hash you're passing in. In my opinion, it's a little cleaner.
parameters = ActionController::Parameters.new(query: 'query', type: 'type', sort: 'sort', other: 'other')
=> {"query"=>"query", "type"=>"type", "sort"=>"sort", "other"=>"other"}
parameters.slice(:query, :type, :sort)
=> {"query"=>"query", "type"=>"type", "sort"=>"sort"}

Related

Rails Params: require key, then permit array of hashes

Can someone help me figure out how to require and permit a set of parameters that looks like this:
<ActionController::Parameters {
"people"=>[
{"id"=>"1", "first"=>"Jane", "last"=>"Doe"},
{"id"=>"2", "first"=>"John", "last"=>"Doe"}
]
} permitted: false>
The data is being prepared in Javascript with this:
const formData = new FormData()
people.forEach(person => {
formData.append('people[][id]', person.id)
formData.append('people[][first]', person.first)
formData.append('people[][last]', person.last)
})
I've tried some different formats (is there a more Railsish way to structure that?), and lots of different inputs to permit with no luck. My current attempt is
params.require(:people).each { |person| person.permit(:id, :first, :last) }
This doesn't crash, but also doesn't seem actually permit anything.
I've also tried setting up my data with people[][person][id] so I could do
params.require(:people).permit(person: [:id, :first, :last])
but that didn't work either.
params.require(:people)
params.permit(people: [:id, :first, :last])
The first line is just to raise an error if the key is missing. .permit(people: [:id, :first, :last]) permits the people key and an array of hashes with the keys :id, :first, :last.

Rails 4 How to assign value to dropdowns in a hash?

For example I need to create a dropdown with options "Sedentary", "Lightly Active", "Active", "Very Active" and assign those to a user as an integer(0, 1,..3). How can I make a hash
activity_level = {"Sedentary" => 0,
..............,
"Very Active" => 3}
and store an integer value instead of a string?
Try this
f.input :fieldname, as: select, collection: activity_level.keys
When saving it
your_model_object.attribute = activity_level[params[:selected_value]]
your_model_object.save
Best option IMO would be using the Rails enum feature, which maps integer to string values:
class Activity < AR::Base
enum :activity_level => [ :sedentary, :lightly_active, :active, :very_active ]
def self.activity_levels # or store in an I18n.
{ "sedentary" => "Sedentary", "lighty_active" => "Lightly Active", ...}
end
AR will store that columns internally as integer, but you have never to deal with this. You can also use the convenient query methods, like Activity.sedentary.where... or if #activity.sedentary?
And the form (When I remember correctly, the collection needs to be pairs of [v, k] not [k, v], so "Lightly Active" => "lightly_active"):
= f.input :activity_level, as: :select, collection: Activity.activity_levels.map(&:reverse)

Ruby on Rails: How to chain strong parameter if some of the parameters are nested attributes?

Suppose I have the following parameters:
"struct"=> {"content" => nil}, "name" => "structA"
When I try to build a strong parameters filter around it:
params = ActionController::Parameters.new("struct"=> {"content" => nil}, "name" => "structA")
params.permit(:struct, :name)
It only accept name:
=> {"name"=>"structA"}
I read some of the post that for the nested attribute, I need to use "require":
params.require("struct").permit!
But how can I chain the nested and non-nested attribute as one filter?
Try this
params.permit(:struct => [:content], :name)

Get key names from key value pairs

I'm running Rails 4 on Ruby 2.0
I'm trying to populate a select tag with a key value pair array I have setup in my model. However, I am having trouble figuring out how to grab the key. Here is what I have so far:
Model
class Store
Colors = ['blue', 'green', 'red', 'yellow', 'orange', 'pink', 'purple', 'lime', 'magenta', 'teal']
SearchParams = {'isbn' => 'ISBN', 'intitle' => 'Title', 'inauthor' => 'Author', 'inpublisher' => 'Publisher', 'subject' => 'Subject', 'lccn' => 'LCCN', 'oclc' => 'OCLC'}
end
Controller
def index
#search_params = Store::SearchParams.map { |param| [param, param.key] }
end
note: I am aware that .key does not exist - I added that hoping it would better communicate what I am trying to do.
View
<%= form_tag do %>
<%= select_tag :param_name, #search_params, prompt: 'choose' %>
<% end %>
I would like the value of each <option> to be the key, and for the user to see the value. I hope that makes sense.
You generally use options_for_select to provide the options for select_tag. Conveniently, you can hand a Hash to options_for_select and it will do the right thing:
options_for_select(container, selected = nil)
Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. [...]
The problem is that options_for_select wants the Hash's keys to be what the human sees and the values to be the value attributes for the <option>s. Your Hash is backwards but you can use Hash#invert to flip the keys and values around:
invert → new_hash
Returns a new hash created by using hsh’s values as keys, and the keys as values.
So you could just do this in your controller:
#search_params = Store::SearchParams.invert
and then this in the ERB:
<%= select_tag :param_name, options_for_select(#search_params), prompt: 'choose' %>
I think, this itself will work
def index
#search_params = Store::SearchParams.to_a
//it will return the array you want, but the values will be [[key1,value1],[key2,value2]]
// if you want to reverse itm then Store::SearchParams.to_a.collect{|arr| arr.reverse} will give that
end
That will do it:
def index
#search_params = Store::SearchParams.map { |key, val| [val, key] }
end
UPD: consider also Hash#invert (thanks to #mu is too short)

Ruby on Rails: Interpreting a form input as an integer

I've got a form that allows the user to put together a hash.
The hashes desired end format would be something like this:
{1 => "a", 2 => "x", 3 => "m"}
I can build up something similar by having lots of inputs that have internal brackets in their names:
<%= hidden_field_tag "article[1]", :value => a %>
However, the end result is that builds a hash where all the keys are strings and not integers:
{"1" => "a", "2" => "x", "3" => "m"}
I'm currently fixing this by generating a new hash in the controller by looping over the input hash, and then assigning that to params. Is there a cleaner, DRYer way to do this?
Your params will always come in with string keys and values. The easiest way to fix this is to either write your own extension to Hash or simply inject as required:
numeric_keys = params['article'].inject({ }) do |h, (k, v)|
h[k.to_i] = v
h
end
Then you have a hash with the keys converted to integer values, as you like.
A simple extension might be:
class Hash
def remap_keys
inject({ }) do |h, (k, v)|
h[yield(k)] = v
h
end
end
end
This is much more generic and can be used along the lines of:
params['article'].remap_keys(&:to_i)
That depends a bit what you want to use it for. Maybe it is easier to just use strings as keys, and do the "conversion" when accessing the array (or not at all)?
It is also possible to build an array using something like
<%= hidden_field_tag "article[]", :value => "x" %>
this will return "article" as an array, and you can access it directly by index. However, there is no way to influence the position - the array will contain all values in order of appearance.
Lastly, you can make your own version of Hash or just modify the keys, as has been explained.

Resources