Passing hash as values in hidden_field_tag - ruby-on-rails

I am trying to pass some filters in my params through a form like so:
hidden_field_tag "filters", params[:filters]
For some reason the params get changed in the next page. For example, if params[:filters] used to be...
"filters"=>{"name_like_any"=>["apple"]} [1]
...it gets changed to...
"filters"=>"{\"name_like_any\"=>[\"apple\"]}" [2]
note the extra quotations and backslashes in [2] when compared to [1].
Any ideas? I'm attempting to use this with searchlogic for some filtering, but I need it to persist when I change change objects in forms. I would prefer not to have to store it in session.

My solution was just to re-create each of param with key-value pair:
<% params[:filters].each do |key,value| %>
<%= hidden_field_tag "filters[#{key}]",value %>
<% end %>

You actually want/need to 'serialize' a hash using hidden fields.
Add this to your ApplicationHelper :
def flatten_hash(hash = params, ancestor_names = [])
flat_hash = {}
hash.each do |k, v|
names = Array.new(ancestor_names)
names << k
if v.is_a?(Hash)
flat_hash.merge!(flatten_hash(v, names))
else
key = flat_hash_key(names)
key += "[]" if v.is_a?(Array)
flat_hash[key] = v
end
end
flat_hash
end
def flat_hash_key(names)
names = Array.new(names)
name = names.shift.to_s.dup
names.each do |n|
name << "[#{n}]"
end
name
end
def hash_as_hidden_fields(hash = params)
hidden_fields = []
flatten_hash(hash).each do |name, value|
value = [value] if !value.is_a?(Array)
value.each do |v|
hidden_fields << hidden_field_tag(name, v.to_s, :id => nil)
end
end
hidden_fields.join("\n")
end
Then, in view:
<%= hash_as_hidden_fields(:filter => params[:filter]) %>
This should do the trick, even if you have a multilevel hash/array in your filters.
Solution taken http://marklunds.com/articles/one/314

I just wrote a gem to do this called HashToHiddenFields.
The core of the gem is this code:
def hash_to_hidden_fields(hash)
query_string = Rack::Utils.build_nested_query(hash)
pairs = query_string.split(Rack::Utils::DEFAULT_SEP)
tags = pairs.map do |pair|
key, value = pair.split('=', 2).map { |str| Rack::Utils.unescape(str) }
hidden_field_tag(key, value)
end
tags.join("\n").html_safe
end

Here's how I managed to pass a parameter value through my view - that is, from View A through View B and on to the controller:
In View A (index):
<%= link_to 'LinkName', {:action => "run_script", :id => object.id} %>
In View B (run_script):
<%= form_tag :action => 'index', :id => #object %>
<%= hidden_field_tag(:param_name, params[:id]) %>
In the controller:
Just reference params[:param_name] to make use of the value.
The key transition that wasn't documented anywhere I could find is where {... :id => object.id} from View A is passed on to View B as <%... :id => #object %>, which View B then passes on to the controller as (:param_name, params[:id]) through the hidden_field_tag construct.
I didn't see this documented anywhere but after perusing several posts across several sites including this post (whose syntax provided the key inspiration), the solution finally gelled. I've seen the caveats on hidden fields pertaining to security but have found no other way to do this given my current design, such as it is.

it's because when you convert in HTML with your hidden_field_tag, the backquote is add. After when you received it like a string not a Hash.
The Hash type can't exist in HTML. You have only string. So if you want pass your hash (not recommend by me), you need eval it when you received it. But can be a big security issue on your application.

As a caveat to Vlad's answer, I had to use raw:
<%= raw hash_as_hidden_fields(:filter => params[:filter]) %>
to get it to work in Rails 3.1.1. Essentially, the text being output was being escaped, eg., "<" becoming "&lt".

Assuming the hash is strings, symbols, numbers, and arrays, you can call eval to convert the params string of the hash from the hidden_fields form back into a hash in the controller. Then the backslash escape characters for the quotes added are no longer an issue:
hash = eval(params["hash_string"].to_s)
Credit to the following article for helping identify this simple solution for my case:
How do I convert a String object into a Hash object?
Keep in mind the contents of the params should be cleaned with .require and .permit.

Related

How to accept multiple values into the same key parameter, as an array?

After reading through quite a few threads here on SO as well as various book/web resources, I'm still at a loss for how to properly set up a parameter key so that it will take in multiple values on a single form.
The readme for strong_parameters seems to indicate this is possible by declaring the item in the params hash as an empty array, like this params.permit(:id => []).
However, when I set up my params hash this way, it still manages to return only the last value for the key that was set in the form, rather than an array of all the values submitted by the user.
I notice too that the param key is showing to be a string, rather than an array. Running params[:document][:key].class returns String rather than Array. Also, when I experimented with including a "name=key[]" option in the form's text_field it complains of an undefined method 'merge' for "key[]":String.
Any idea where I'm going wrong? I admit some of the controller code may be a bit rough, but at this point, I'm just trying to get the params hash to return an array of k/v pairs. Thank you in advance for your help!
documents_controller.rb
before_action :get_key_array, only: [:create, :update]
before_action :get_value_array, only: [:create, :update]
before_action :combine_key_array_and_value_array_to_create_jsonb_object, only: [:create, :update]
before_action :save_jsonb_object_to_user_defined_attributes_field, only: [:create, :update]
...
private
params.require(:document).permit(:key => [], :value => [])
# FIXME: Update to use push << ?
def get_key_array
unless params[:document].nil?
#key_array = [params[:document][:key]]
# #key_array << params[:document][:key]
end
end
def get_value_array
unless params[:document].nil?
#value_array = [params[:document][:value]]
# #value_array << params[:document][:value]
end
end
def combine_key_array_and_value_array_to_create_jsonb_object
unless #document.nil? || #key_array.nil? || #value_array.nil?
#jsonb_object = jsonb_object(#key_array, #value_array)
end
end
def save_jsonb_object_to_user_defined_attributes_field
params[:document][:user_defined_attributes] = #jsonb_object
end
_form.html.erb
<%= form_for #document do |document_form| %>
<%= document_form.label :key %>
<%= document_form.text_field :key %>
<%= document_form.label :value %>
<%= document_form.text_field :value %>
<%= document_form.label :key %>
<%= document_form.text_field :key %>
<%= document_form.label :value %>
<%= document_form.text_field :value %>
<% end %>
After inputting test data like Key1, Value1, Key2, Value2, the params hash indicates that only the second set of input values are being added to the params hash
Parameters: { ..., "document"=>{"key"=>"key2", "value"=>"value2"}, "commit"=>"Create Document"}
UPDATE: If it matters, the :key and :value arrays are, themselves, not part of the document model. I used attr_accessor :key, :value to give the array values a "temporary" home to be used in the form and params hash, with the intention of transferring their values to the user_defined_attributes field, which is part of the document model. Just throwing this out there in case it's part of the issue.
Thank you to #TarynEast and #mu_is_too_short for their comments on this! You both are so kind with your time and StackOverflow is better for having you in its community! :) Thank you thank you!
Now, on to the solution...
Since I was setting up array variables (ie key and value) to temporarily store parameters that were not directly part of the Documents table, the solution was two-fold.
First I had to properly initialize these variables before using them in the controller.
At first, I experimented in document.rb with
def initialize
#key = Array.new
#value = Array.new
end
but this threw some argument errors. I'm not sure why exactly, but I suspect Rails 5 frowns on the use of initialize since ActiveRecord uses it further up the chain to automatically set up attr_accessor methods...
So then I modified the array creation to use callbacks, and that did the trick!
document.rb
after_initialize :create_key, :create_value
def create_key
#key = Array.new
end
def create_value
#value = Array.new
end
Then, with the variables properly set up as arrays, the form now let me name the fields with array references (previously, it rightly complained about key and value being strings).
_form.html.erb
<%= document_form.text_field :key, name: "key[]" %>
<%= document_form.text_field :value, name: "value[]" %>
Thoughts? Critiques? Improvements? I'm all ears and willing to learn :)

How to extract info from input field in ruby

I'm a frontend + PHP dev, trying to fix [] in a project built in Rails.
[] = Fetch color, show a slightly darker color.
This row:
<%= f.text_field attribute %>
creates an input field with a value that can be translated into a color. I'm at loss as to where to look for how it adds that value. I'm trying to use the value that this input field generates.
this is code from the file select_a_color_input.html.erb inside the app/views/shared folder. Any ideas on where to continue my treasure hunt? :)
update: I found this!
def app_text_field(attribute, args = {})
render_field 'text_field', field_locals(attribute, args)
end
Does that help? ^__^
update:
The form builder
class AppFormBuilder < ActionView::Helpers::FormBuilder
def form_fields(partial = nil , options = {})
partial ||= 'form'
fields = ''
unless options.delete(:without_error_messages)
fields << #template.render('shared/error_messages', :target => Array(#object).last)
end
fields << #template.render(partial, options.merge(:f => self))
end
def app_text_field(attribute, args = {})
render_field 'text_field', field_locals(attribute, args)
end
def app_file_field(attribute, args = {})
render_field 'file_field', field_locals(attribute, args)
end
private
def render_field(name, locals)
#template.render field_path(name), locals
end
def field_locals(attribute, args = {})
help_options = args[:help_options] || {}
field_options = args[:field_options] || {}
html_options = args[:html_options] || {}
{ :f => self, :attribute => attribute, :help_options => help_options, :field_options => field_options, :html_options => html_options, :object => object }
end
def field_path(value)
"shared/app_form/#{value}"
end
end
update:
When I tried to add
<%= content_tag(:p, attribute) %>
It does not give me the values, but instead the id/name of the item, not the colour.
<%= f.text_field attribute %>
This by itself is not very useful to help us gather context. What's the surrounding markup look like? attribute is a ruby variable in this instance. If it were f.text_field :attribute, then :attribute is now a symbol instead of a variable and this would indicate that it maps to the attribute method on X model. This all depends on what your form_for looks like though. I'll give an example:
<%= form_for #user do |f| %>
<%= f.text_field :attribute %>
In this case, we have a form for the User model, and our text_field maps to #user.attribute. The field itself looks something like this:
<input type='text' name='user[attribute]'>
And in the controller's #update or #create action (depending on if this is a new record or an existing record you're editing) the value would be accessible in this fashion:
params[:user][:attribute]
However, it's impossible to say what exactly the params will look like in your particular case. What action is being run? What's the name of the file that this is being loaded from? "app/views/users/new" would indicate the #new action handles this page, and the #create action will handle the form submission.
Things we need to know to fully solve your problem:
Name and relevant code of the controller that's handling this action.
Full view path that this is being rendered from
The rest of the markup starting at form_for and ending at this field attribute
What value does attribute hold? It's a variable, so it must be holding a symbol value or something that will indicate which field is being mapped to this input.

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)

Rails issue with params hash

I need to export a previously rendered table from my view to pdf. When I build the array of hashes as follows:
__index = 0
#people.each do |p| %>
#pdfdata[__index] = {
[:name] => p.name.to_s,
[:surname] => p.surname.to_s
__index += 1
end
end
and send it to the controller in order to export it on a pdf as follows:
hidden_field_tag(:pdfdata, #pdfdata)
when I get the params[:pdfdata] I cannot find a way unless I build a string parser to map the data accordingly... is there a better way to do this?
Modifying your code a little bit to get
#people.each_with_index do |p,i| %>
#pdfdata[i] = {
[:name] => p.name.to_s,
[:surname] => p.surname.to_s}
end
and use this gem to create the hidden has field
https://github.com/brianhempel/hash_to_hidden_fields

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