Better way to default Rails form instead of using ternary? - ruby-on-rails

I have some code like this:
<input type="text" value="<%= params[:first_name] ? params[:first_name] : 'John' %>>
Obviously this is not ideal as it repeats params[:first_name] twice. Is there a better way to have a form default to param, but if there is no param value for it use a default value?

How about:
params[:first_name] || 'John'

Not sure why you aren't using Rails' form helpers, but I suggest you start using them. You can use :value => 'John' to set the default value of a text_field to "John":
<%= form_for #object do |f| %>
<%= f.text_field :attribute_name, :value => 'John' %>
...
<% end %>
If for some reason it's not possible to use the form helpers then you should go with shioyama's answer.

You can use ||= operator in such situations as below -
a = "Rafa"
a.empty? ? "Nadal" : a # => "Rafa"
a ||= "Nadal" # => "Rafa"
If your variable is undefined and you use ternary, NameError exception will be raised
b ? "Rafa" : b # => NameError: undefined local variable or method `b' for main:Object
However ||= operator will initialise it to "Nadal"
b ||= "Nadal" # => "Nadal"
Basically b ||= "Nadal" evaluates to b = b || "Nadal" i.e. if b already exists in the current scope use its value whatever it may be, else initialise it to "Nadal".

Related

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.

Optional parameters for Rails Image Helper

In my current Rails (Rails 2.3.5, Ruby 1.8.7) app, if I would like to be able to define a helper like:
def product_image_tag(product, size=nil)
html = ''
pi = product.product_images.first.filename
pi = "products/#{pi}"
pa = product.product_images.first.alt_text
if pi.nil? || pi.empty?
html = image_tag("http://placehold.it/200x200", :size => "200x200")
else
html = image_tag(pi, size)
end
html
end
...and then call it from a view with either:
<%= product_image_tag(p) %>
...or:
<%= product_image_tag(p, :size => 20x20) %>
In other words, I'd like to be able to have this helper method take an optional size parameter. What would be the best way to go about this?
You're on the right track. I would do this:
def product_image_tag(product, options = {})
options[:size] ||= "200x200"
if img = product.product_images.first
image_tag("products/#{img.filename}", :alt => img.alt_text, :size => options[:size])
else
image_tag("http://placehold.it/#{options[:size]}", :size => options[:size])
end
end
Explanations:
Setting the final parameter to an empty hash is a common Ruby idiom, since you can call a method like product_image_tag(product, :a => '1', :b => '2', :c => '3', ...) without explicitly making the remaining arguments a hash with {}.
options[:size] ||= "200x200" sets the :size parameter to 200x200 if one wasn't passed to the method.
if img = product.product_images.first - Ruby lets you do assignment inside a condition, which is awesome. In this case, if product.product_images.first returns nil (no image), you fall back to your placehold.it link, otherwise display the first image.

How to make optional :conditions for a find

Hello I have the followong struggle in my head. I want a text-field in which the use can type in some parameters, which will be used as filter-criteria for the :conditions hash in my find method.
I have created a helper, with takes an option and merge the hash to the options:
In my controller:
#bills = adminbill_filter(:limit=>params[:limit] || 50,:offset=>params[:offset] || 0, :conditions=>params[:options])
In my helper:
def link_to_with_current(text, link, condition, *args)
options = args.first || {}
options[:class] = condition ? 'current' : nil
link_to text, link, options
end
In my view:
<%= text_field :filter ,:criteria, :class=>'roundRect',:id=>'name', :value=>12009%>
<%= button_to_with_filter 'Start Filter', 'index', :filter_condition=>true, :options=>{:id=>81}%>
Is it somehow possible to pass the value of text_field into the :option=>{...} of the button_to_with_filter? I find this solution (if it is working) quite unhandy. Your comments are as always very helpful.
Greetings
Matthias
It seems kind of terrifying to put in the contents of user-submitted params without vetting them in any capacity. You're probably going to run into all kinds of exceptions if the data doesn't come in as expected, or is formulated to be malicious.
I've found it's often easier to use a chained scopes approach:
def index
bills_scope = Bill
# Use an example Bill.with_id scope
if (params[:with_id])
bills_scope = bills_scope.with_id(params[:with_id])
end
# Repeat as required
# Finally, use the scope to retrieve matching records
#bills = bills_scope.paginated
end
Using something like will_paginate can help with your offset and limit values.
If the text field and button were encapsulated in a form, and the button was the submit button, the text field's value would automatically be brought into the params hash. Then you wouldn't have to deal with it. I can't recall at the moment the exact Rails helpers that will do this for you, but you want the resulting form to probably be something like this:
<% form_for :options, :url => {:action => :index}, :html => { :method => :get } do |f| %>
<%= f.text_field :filter ,:criteria, :class=>'roundRect',:id=>'name', :value=>12009%>
<%= f.submit 'Start Filter' %>
<% end %>
Which may change some, since I don't know the underlying code behind your methods.
Otherwise, the only thing I can think of is using a Javascript event on the button that grabs the value of the text field before it submits.
Thanks for your help, I came across named_scope and solved the problem with the following code:
Bill model:
class Bill < ActiveRecord::Base
# named_scope werden fuer Filterfunktionen bei Adminbill benoetigt
named_scope :standard, :order => "created_at DESC"
named_scope :limit, lambda {|*args| {:limit=>(args.first)|| 50}}
named_scope :offset, lambda {|*args| {:offset=>(args.first || 10)}}
named_scope :paid, :conditions=>"paid IS NOT NULL"
named_scope :not_paid, :conditions=>{:paid=>nil}
named_scope :test_bill, :conditions => {:test_bill=>true}
named_scope :no_test_bill, :conditions => {:test_bill=>false}
named_scope :find_via_bill_id, lambda {|*args|{:conditions=>{:id=>(args.first || 210)}}}
named_scope :find_via_email, lambda {|*args| {:conditions=>{:buyer_id=>args.first}}}
controller:
def index
logger.debug "The object is #{current_user}"
if params[:filterInput] != nil && !params[:filterInput].empty?
filter_array = params[:filterInput].split('&')
bill_scope = Bill.scoped({})
bill_scope = bill_scope.standard
# Filtere via Regexp-Matching die Zahlen der Eingabe heraus
filter_array.each do |el|
if el =~ /limit\([0-9]+\)/
number =
bill_scope = bill_scope.limit(el.scan(/\d+/)[0])
elsif el =~ /offset\([0-9]+\)/
bill_scope = bill_scope.offset(el.scan(/\d+/)[0])
elsif el == 'paid'
bill_scope = bill_scope.paid
elsif el == 'not_paid'
bill_scope = bill_scope.not_paid
elsif el == 'test_bill'
bill_scope = bill_scope.test_bill
elsif el =~ /find_via_bill_id\([0-9]+\)/
bill_scope = bill_scope.find_via_bill_id(el.scan(/\d+/)[0])
elsif el =~ /find_via_email\([A-Za-z0-9.#-]+\)/
email = el.scan(/\([A-Za-z0-9.#-]+\)/)[0]
# TODO geht bestimmt auch eleganter durch besseres Matching
email = email.gsub("(", "")
email = email.gsub(")", "")
user = User.find_by_email(email) unless User.find_by_email(email).blank?
bill_scope = bill_scope.find_via_email(user.id)
end
end
#bills = bill_scope
else
#bills = Bill.standard.limit.offset
end
And in the view:
<% form_tag(:action => 'index') do %>
<%= text_field_tag 'filterInput', nil, :size => 40 %>
<%= submit_tag 'Start Filter'%>
<% end %>
Now you can pass in the tex-field e.g.the following valid expression: paid&limits(20)
I know that the controller solution isn't very elegant but for me it was the fastest way to solve this problem.

If statement with form field select in rails

I'm trying put an if statement directly into a select field in rails, with no success.
Here is what I've tried:
<%= f.select (:book_id,{
if #a!=1
"Harry Potter", 1,
end
if #b!=2
"Lord of the Rings", 2,
end
end %>`
Any ideas?
Don't do this. It's ugly, and not fun for you to maintain. Also, no good trying to put if-statements or anything other than hash values inside a hash declaration. How about a helper?
Helper code (untested):
def book_select(f)
options = {}
options['Harry Potter'] = 1 unless #a == 1
options['Lord of the Rings'] = 2 unless #b == 2
f.select :book_id, options
end
View code:
<%= book_select(f) %>

Passing hash as values in hidden_field_tag

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.

Resources