So I created a drop-down form for my Sinatra project, and I want it to prepopulate the option. I was able to do that but it turned into a big case statement! Any Idea how to refactor this? Thanks!
get '/animes/:id/edit' do
if is_logged_in?
#anime = Anime.find_by_id(params[:id])
case #anime.rating
when 1
#oneselect = "selected"
when 2
#twoselect = "selected"
when 3
#threeselect = "selected"
when 4
#fourselect = "selected"
when 5
#fiveselect = "selected"
when 6
#sixselect = "selected"
when 7
#sevenselect = "selected"
when 8
#eightselect = "selected"
when 9
#nineselect = "selected"
when 10
#tenselect = "selected"
end
erb :'animes/edit'
else
redirect to '/'
end
end
And this is my .erb view file form!
<label for="rating">Rating:</label>
<select name="rating" id="rating" value="<%=#anime.rating%>">
<option value="10"<%=#tenselect%>>10 (Masterpiece)</option>
<option value="9"<%=#nineselect%>>9 (Great)</option>
<option value="8"<%=#eightselect%>>8 (Very Good)</option>
<option value="7"<%=#sevenselect%>>7 (Good)</option>
<option value="6"<%=#sixselect%>>6 (Fine)</option>
<option value="5"<%=#fiveselect%>>5 (Average)</option>
<option value="4"<%=#fourselect%>>4 (Bad)</option>
<option value="3"<%=#threeselect%>>3 (Very Bad)</option>
<option value="2"<%=#twoselect%>>2 (Horrible)</option>
<option value="1"<%=#oneselect%>>1 (Appalling)</option>
</select><br>
Apologies, I don't know anything about Sinatra, so this answer may be off base.
If I were looking at this code for myself, I would be thinking along the lines of doing something like (not tested, and my erb is a bit rusty):
<label for="rating">Rating:</label>
<%
options = [
[10, '10 (Masterpiece)'],
[9, '9 (Great)'],
[8, '8 (Very Good)'],
[7, '7 (Good)'],
[6, '6 (Fine)'],
[5, '5 (Average)'],
[4, '4 (Bad)'],
[3, '3 (Very Bad)'],
[2, '2 (Horrible)'],
[1, '1 (Appalling)']
]
%>
<select name="rating" id="rating">
<% options.each do |option| %>
<option value="<%= option[0].to_s %>"<%= #anime.rating == option[0] ? ' selected' : '' %>><%= option[1] %></option>
<% end %>
</select><br>
and then remove the case statement.
A useful object might be a map from the numerical rating to its string representation:
#ratings_map = {
1 => "1 (Appalling)",
2 => "2 (Horrible)",
3 => "3 (Very Bad)",
etc...
}
Rather than passing a separate variable for every rating (#oneselect, #twoselect, etc), you can simply use #anime itself for the selected value. Then, you can use the options_for_select helper function:
<label for="rating">Rating:</label>
<%= options_for_select(#ratings_map.map{|key, value| [value, key]}, #anime.rating) %>
Some useful documentation on the options_for_select helper:
https://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/options_for_select
Edit:
My apologies, had my head in Rails land. If you're using Sinatra, you would have to explicitly require 'active_support' at the top of your Sinatra application for this solution to work. You would also want to make sure active_support is included in your Gemfile, and bundle install
Related
In rails 4 and ruby 2, with the idea to have more room for a form, what is the way to put the title of the label in the input?
Thanks
Like Kirti said you can use a placeholder, but if you really want a label you can use label_tag or label
You can use the "label_tag", this would be what you want:
label_tag 'name', 'Your name'
# will output => <label for="name">Your name</label>
label_tag
Given an html file:
<div>
<div class="NormalMid">
<span class="style-span">
"Data 1:"
1
2
</span>
</div>
...more divs
<div class="NormalMid">
<span class="style-span">
"Data 20:"
20
21
22
23
</span>
</div>
...more divs
</div
Using these SO posts as reference:
How do I integrate these two conditions block codes to mine in Ruby?
and
How to understand this Arrays and loops in Ruby?
My code:
require 'nokogiri'
require 'pp'
require 'open-uri'
data_file = 'site.htm'
file = File.open(data_file, 'r')
html = open(file)
page = Nokogiri::HTML(html)
page.encoding = 'utf-8'
rows = page.xpath('//div[#class="NormalMid"]')
details = rows.collect do |row|
detail = {}
[
[row.children.first.element_children,row.children.first.element_children],
].each do |part, link|
data = row.children[0].children[0].to_s.strip
links = link.collect {|item| item.at_xpath('#href').to_s.strip}
detail[data.to_sym] = links
end
detail
end
details.reject! {|d| d.empty?}
pp details
The output:
[{:"Data 1:"=>
["http://www.site.com/data/1",
"http://www.site.com/data/2"]},
...
{:"Data 20 :"=>
["http://www.site.com/data/20",
"http://www.site.com/data/21",
"http://www.site.com/data/22",
"http://www.site.com/data/20",]},
...
}]
Everything is going good, exactly what I wanted.
BUT if you change these lines of code:
detail = {}
[
[row.children.first.element_children,row.children.first.element_children],
].each do |part, link|
to:
detail = {}
[
[row.children.first.element_children],
].each do |link|
I get the output of
[{:"Data 1:"=>
["http://www.site.com/data/1"]},
...
{:"Data 20 :"=>
["http://www.site.com/data/20"]},
...
}]
Only the first anchor href is stored in the array.
I just need some clarification on why its behaving that way because the argument part in the argument list is not being used, I figure I didn't need it there. But my program doesn't work correctly if I delete the corresponding row.children.first.element_children as well.
What is going on in the [[obj,obj],].each do block? I just started ruby a week ago, and I'm still getting used to the syntax, any help will be appreciated. Thank You :D
EDIT
rows[0].children.first.element_children[0] will have the output
Nokogiri::XML::Element:0xcea69c name="a" attributes=[#<Nokogiri::XML::Attr:0xcea648
name="href" value="http://www.site.com/data/1">] children[<Nokogiri::XML::Text:0xcea1a4
"1">]>
puts rows[0].children.first.element_children[0]
1
You made your code overly complicated. Looking at your code,it seems you are trying to get something like below:
require 'nokogiri'
doc = Nokogiri::HTML::Document.parse <<-eotl
<div>
<div class="NormalMid">
<span class="style-span">
"Data 1:"
1
2
</span>
</div>
<div class="NormalMid">
<span class="style-span">
"Data 20:"
20
21
22
23
</span>
</div>
</div
eotl
rows = doc.xpath("//div[#class='NormalMid']/span[#class='style-span']")
val = rows.map do |row|
[row.at_xpath("./text()").to_s.tr('"','').strip,row.xpath(".//#href").map(&:to_s)]
end
Hash[val]
# => {"Data 1:"=>["http://site.com/data/1", "http://site.com/data/2"],
# "Data 20:"=>
# ["http://site.com/data/20",
# "http://site.com/data/21",
# "http://site.com/data/22",
# "http://site.com/data/23"]}
What is going on in the [[obj,obj],].each do block?
Look the below 2 parts:
[[1],[4,5]].each do |a|
p a
end
# >> [1]
# >> [4, 5]
[[1,2],[4,5]].each do |a,b|
p a, b
end
# >> 1
# >> 2
# >> 4
# >> 5
The array of strings values are not getting populated in the option list. I am not sure where I might have gone wrong. Here goes the code that I am stuck at.
<html>
Status : <select id = 'status_update'>
<%=
array = ["Submitted", "Replied", "Answered", "Assigned", "Started", "Closed","Reopened", "Canceled"]
status = '';
for index in 0 .. (array.size-1)
status << "<option value = '#{puts (eval(array[index].inspect))}'>'#{(array[index].inspect)}'</option>"
end
%>
</select>
</html>
In Ruby instead of
for i in 0...ary.size do
ary[i]
end
you can use Array#each:
ary.each do |item|
item
end
This is how I would do it in ERB (more or less):
<% %w(Submitted Replied Answered Assigned Started Closed Canceled).each do |status| %>
<option value="<%= status %>"><%= status %></option>
<% end %>
Output:
<option value="Submitted">Submitted</option>
<option value="Replied">Replied</option>
<option value="Answered">Answered</option>
<option value="Assigned">Assigned</option>
<option value="Started">Started</option>
<option value="Closed">Closed</option>
<option value="Canceled">Canceled</option>
Explanation:
%w(Submitted Replied Answered Assigned Started Closed Canceled) creates the array
<% ary.each do |status| %> ... <% end %> is a loop that's executed for each element, the variable status refers to the current element within the loop
<option value="<%= status %>"><%= status %></option> is the output for each element, <%= status %> is replaced with the corresponding element (Submitted, Replied, ...)
I have a constant called PAYMENT_METHODS in venue.rb.
PAYMENT_METHODS = ['Visa', 'MasterCard', 'American Express', 'Diners', 'JCB', 'Bankomat', 'sodexo', 'MA-Gutscheine']
You can check/uncheck the payment types in a form (payment_options is an integer):
<%= hidden_field_tag "venue[payment_options][]", nil %>
<% Venue::PAYMENT_METHODS.each do |category| %>
<%= check_box_tag "venue[payment_options][]", category %>
<%= label_tag category %>
<% end %>
Now I want to save the selection, but the value of each check box is the name of the payment option. I think I have to somehow add a key an store only the keys.
How do I set keys and save the collection to the database?
Thanks in advance
Make the constant a hash or an array, e.g.
PAYMENT_METHODS = {'Visa' => 1, 'MasterCard' => 2, 'American Express' => 3, 'Diners' => 4, 'JCB' => 5, 'Bankomat' => 6, 'sodexo' => 7, 'MA-Gutscheine' => 8 }
These will now be in a format that you can pass to options_for_select http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-options_for_select.
If you (really) want checkboxes, an array is fine, just loop over using PAYMENT_MTHODS.each_index do |index| to get an iterator that's the value, and then use PAYMENT_METHODS[index].
I have always found it odd that the hash key is the part displayed in the list, but I guess it makes sense that the value is what is associated with the option's value :-).
Save the string value itself nothing wrong with that.
Its better to create a model like PaymentType and just keep id,name and in view render all payment types.This way you can better manipulate all available payment options in future from an admin panel (if needed) rather than going to a code level and changing at the constant.
You can use an element's index as a key. Use Array#index to your advantage.
PAYMENT_METHODS.index("Visa") #=> 0
PAYMENT_METHODS[0] #=> "Visa"
PAYMENT_METHODS.index("Diners") #=> 3
PAYMENT_METHODS[3] #=> "Diners"
A word of caution: This will break if you reorder PAYMENT_METHODS. You are keying an element to it's relative position in the array. Change the array and you change the keys. Avoid trouble by keeping your constants constant.
In regards to
I have always found it odd that the hash key is the part displayed in the list, but I guess it makes sense that the value is what is associated with the option's value :-).
you can get that done by (it was bugging me a bit as well)
<% Post::TECH_CATEGORY.each do |category| %>
<%= label_tag 'name', category[0] %>
<%= check_box_tag 'tech_cat', category[1] %>
<% end %>
It makes sense now that I can see it on the screen. Each object has two values, so...
category[0]
will always be the key you supplied for that specific object you are currently enumerating over
category[1]
will be the value of that same object. Looks way better on the screen.
which looks like in your example above
<label for="name"> Visa </label>
<input id="tech_cat" name="tech_cat" type="checkbox" value="1" />
Cheers,
I'm trying to set the value of a select list using Mechanize with Ruby. I can navigate to the page with the select list, grab the form using the .form method, and find the select list.
report_form =page.form('form1')
pp report_form.field_with(:name => "report_type")
Correctly returns the right object.
However, I'm still unable to set the value of this field! I've tried:
report_form.field_with(:name => "report_type").options.first.select
report_form.field_with(:name => "report_type").options[1].select
report_form.field_with(:name => "report_type").value = "Foo"
But when I then do:
pp report_form.field_with(:name => "report_type")
The value field is still empty.
Is there something I'm missing? Tips? Tricks? Better Mechanize docs than what live at http://mechanize.rubyforge.org?
Thanks!
Edit: The relevant HTML is:
The relevant HTML is:
<TD>
<select id="report_type" name="report_type">
<option value="Foo1">Opt 1</option>
<option value="Foo2">Opt 2</option>
<option value="Foo3">Opt 3</option>
</select></TD>
Try this
report_form.field_with(:name => "report_type").option_with(:value => "Foo").click
# now report_form.field_With(:name => "report_type").value should bee "Foo"
(via 1, 2)
It's usually good enough to do:
report_form["report_type"] = "Foo"
i ran into this same issue, nothing works for me either, but id like to clarify that im able to set the value to anything besides select options.
report_form.field_with(:name => "report_type").value = "Foo1"
report_form["report_type"]
=> "Foo1"
report_form.field_with(:name => "report_type").value
=> "Foo1"
report_form.field_with(:name => "report_type")
=> [selectlist:0x7c08ada type: name: "report_type" value: []]
after submiting the form, the select is treated as empty, however if i do
report_form.field_with(:name => "report_type").value = "anything not in the options"
report_form.field_with(:name => "report_type")
=> [selectlist:0x7c08ada type: name: "report_type" value: ["anything not in the options"]]
Foo is not in the select list, i think if you change it to Foo1 (or the others) it should work!?
It actually turned out to be a bug in the Mechanize gem. Make sure you're using v 0.6.0 or newer.