Retrieve complex parameter from view Rails 3.1 - ruby-on-rails

I'm creating a Rails 3.1 app and I've the following big issue: I must retrieve some parameter from view to controller
This is my view:
<tr class="<%= cycle("odd", "even") %>">
<td><%= text_field_tag("bulk_warehouse_serial#{#count}_page#{params[:page]}", bulk_warehouse.serial, :disabled => true) %></td>
<td><%= text_field_tag("bulk_warehouse_asset#{#count}_page#{params[:page]}", bulk_warehouse.asset, :disabled => true)%></td>
<td><%= check_box_tag "enable_record#{#count}_page#{params[:page]}",1,false,{:onclick => "bulk_warehouse_serial#{#count}.disabled =
bulk_warehouse_asset#{#count}.disabled =
!this.checked;"}%></td>
<td class="last">
<%= link_to "#{t("web-app-theme.delete", :default => "Delete")}", bulk_warehouse_path(bulk_warehouse), :method => :delete, :confirm => "#{t("web-app-theme.confirm", :default => "Are you sure?")}" %>
</td>
</tr>
</div>
<% #count = #count +1 %>
And in my controller I've something such as:
...
#count = 0
...
and this what is generated by web server log:
"warehouse"=>{"asset"=>"turiturira", "serial"=>"caricarira", "project_id"=>"1", "hardware"=>{"brand_id"=>"21"}, "hardware_id"=>"60", "state_id"=>"4", "position_id"=>"1", "logicalwarehouse_id"=>"3", "extra_id"=>"3"}, "bulk_warehouse_serial270"=>"t", "bulk_warehouse_asset270"=>"test", "enable_record2_page0"=>"1", "bulk_warehouse_serial2"=>"uela2", "bulk_warehouse_asset2"=>"bela2", "enable_record3_page0"=>"1"}_
e
Now, in my controller I need an action where I must check first all "enable_record#{#count}_page#{params[:page]}" values and then doing some actions. How can I make if statement in my controller? I was thinking something such as:
#count=0
#page = params[:page]
#count_max = 10
until #count == #count_max
if params[:warehouse][:enable_record#{#count}_page#{params[:page]}] == 1
...doing something...
end
#count=#count+1
end
but it gives me an surror; any suggestions?

This seems quite complicated for whatever it is you're trying to do, but I'll refrain from refactoring your approach. There is one standout in your code however -- it is the dynamic construction of the key you're using. You may want to construct it with a string then 'symbolize' it:
key = "enable_record#{#count}_page#{params[:page]}".to_sym
if params[:warehouse][key] == 1
...doing something...
end
Note: you also might reconsider the equality condition. The hash's value may not be an integer 1 as your code suggests and could actually be "1" (a string). Ensure both values are the same type (using either .to_s or .to_i) if there's any uncertainty.

Related

DRY and Elegant way to represent all search combinations without growing exponentially?

Right now I can search the following
1) leaving_from location
2) going_to location
3) leaving_from &
going_to location
if params[:leaving_from].present? && params[:going_to].present?
#flights = Flight.where(:source => params[:leaving_from]).where(:destination => params[:going_to])
elsif params[:leaving_from].present?
#flights = Flight.where(:source => params[:leaving_from])
elsif params[:going_to].present?
#flights = Flight.where(:destination => params[:going_to])
end
Is there a dry way to represent this code above? Basically its a for search function compromised of 2 drop down search boxes. One for leaving from location and another for going to location. With the option of narrowing it down by both locations or just one location.
It works fine now but it isn't very scalable. If I added more search parameters say price and time, it would grow exponentially in order to be able to represent all the states.
For example if I added price my new combinations would be
1) leaving_from location
2) going_to location
3) leaving_from &
going_to location
4) price
5) leaving_from location & price
6) going_to location & price
7) leaving_from location & going_to location & price
I need help to figure out a better way to represent this, or else it would make my controller incredibly bloated.
EDIT FORM CODE --
=form_tag '/flights', :method => :get
%h4
Leaving From:
=select_tag 'leaving_from', content_tag(:option,'select one...',:value=>"")+options_for_select(#flights_source, 'source'), { :class => 'form-control' }
%h4
Going To:
=select_tag 'going_to', content_tag(:option,'select one...',:value=>"")+options_for_select(#flights_destination, 'destination'), { :class => 'form-control' }
%h4=submit_tag "Search", :name => nil, :class => 'btn btn-success btn-md btn-block'
In place of using leaving_from or going_to use source and destination instead and Move all the required parameters under a key, e.g., this solution will work for any no. of keys
'required' => { 'source' => value, 'destination' => value, 'price' => value }
Now in the controller define this method in private
def get_flights(params)
possible_combination = []
conditions = {}
key_array = params['required'].keys
1.upto(key_array.length) { |i| possible_combination + key_array.combination(i).to_a }
possible_combination.reverse.each do |comb|
if comb.collect{ |key| params['required'][key].present? }.inject(:&)
comb.map { |key| conditions[key] = params['required'][key] }
break
end
end
Flight.where(conditions)
end
Call this method from any action
#flights = get_flights(params)
Hope this works! Its an overall idea to make this thing dynamic, you can refactor the code according to your need!
First things first: your code does not do what you think it does, since there is no way for it to execute the third if (every time the third if is true, the first if is as well). On to your question:
#flights = Flight
#flights = #flights.where(:source => params[:leaving_from]) if params[:leaving_from].present?
#flights = #flights.where(:destination => params[:going_to]) if params[:going_to].present?
Or
conditions = {}
conditions[:source] = params[:leaving_from] if params[:leaving_from].present?
conditions[:destination] = params[:going_to] if params[:going_to].present?
#flights = Flight.where(conditions)
How about using ransack which adds your rails to search function very easily.
You just write below, if you use ransack.
# View (Search Form)
<%= search_form_for #q do |f| %>
From: <%= f.text_field :leaving_from_cont %>
To : <%= f.text_field :going_to_cont %>
Price:
<%= f.text_field :price_gteq %> 〜 <%= f.text_field :price_lteq %>
<%= f.submit %>
<% end %>
# Controller
def index
#q = Flight.ransack(parmas[:q])
#flights = #q.result(distinct: true)
end
If a user don't input any fields, ransack don't use the non-input fields value. It means don't add WHERE conditions in DB.
column_name_cont means contain (Like in DB).
column_name_eq means equal (== in DB).
column_name_gteq means greater than equal (<= in DB).
column_name_lteq means less than equal (>= in DB).
etc...
Also you can sort the search result easily by using sort_link methods of ransack.
Please look in ransack.
I was not able to get #RSB's code to work but I was able to use his example to create a method that did work. I call the below code in my action.
#flights = get_flights(search_params)
The search_params method is as follows:
def search_params
params.permit(:leaving_from, :going_to)
params_hash = {'required' => { 'source' => params[:leaving_from], 'destination' => params[:going_to]}}
end
And finally the get_flights method is:
def get_flights(params)
possible_combination = []
conditions = {}
key_array = params['required'].keys
possible_combination = (possible_combination + key_array.combination(key_array.length).to_a).flatten
possible_combination.each do |comb|
conditions[comb] = params['required'][comb] if params['required'][comb].present?
end
Flight.where(conditions)
end
I am still pretty new to ruby and rails so any feedback or suggestions for improvements would be greatly welcome. Thanks!

How to get name of foreign key

In my Rails app I have a list view of device ports and each device port belongs to a device:
device.rb
has_many :device_ports, :foreign_key => "device_id"
device_port.rb
belongs_to :device
device_port_controller.rb
def list
#device_ports = DevicePort.paginate(:all, :page => params[:page], :per_page => 100, :order => "name")
#devices = Device.find(:all)
end
list.rhtml
<table width="100%">
<tr>
<th>Name</th>
<th>Device</th>
</tr>
<%= render :partial => 'device_port/list_item', :collection => #device_ports %>
</table>
_list_item.rhtml
<tr>
<td><a href='/device_port/update/<%= list_item.id %>'><%= list_item.name %></a></td>
<td><%= list_item.device_id %></td>
</tr>
So I am displaying the device_id from the device_ports table but I actually want to display the name column of the devices table instead.
I have done something very similar and probably slightly more difficult elsewhere in my app where I have a preselected dropdown with the correct device name selected but I can't seem to figure out how to do it outwith the context of a select menu.
I have the devices in the #devices variable in the controller above and was trying to look that up using the list_item.device_id but it didn't seem to work.
It's probably quite a straightforward thing but I just can't seem to find the proper way of getting the desired result.
as device_ports belongs_to device so list_item.device.name Thanks!
I just tested this out in the console but it should work. You can call columns on your model which returns an array.
So, what I just used my User model:
User.columns[0].name #=> 'id'
Presumably yours will be
Device.columns[x].name
EDIT: I read the the question as "name [of the] column"

Rails re-factor: How can I refactor this to work?

These two lists are only slightly different as I need to treat the first 3 items a little differently than the rest. The only reason this isn't working now is because the variable with the regex is called twice. Any idea how to get the first 3 items (as shown in the first %li) separated from the rest without having to repeat everything like this?
%ul.list_container
- #links.sort_by { |link| link.votes.where(:up => true).count - link.votes.where(:up => false).count }.reverse.first(3).each do |link|
%li
.various_containers
%p
= link_to link.title, "http://youtube.com/embed/#{link.url.to_s.match(/\/\/youtu.be\/(\S+)$/)[1]}/?rel=0", :class => "youtube title_link"
= link.url_html
- #links.sort_by { |link| link.votes.where(:up => true).count - link.votes.where(:up => false).count }.reverse.drop(3).each do |link|
%li{:style => 'margin-bottom: 50px;'}
.various_containers
%p
= link_to link.title,"http://youtube.com/embed/#{link.url.to_s.match(/\/\/youtu.be\/(\S+)$/)[1]}/?rel=0", :class => "youtube title_link"
A few things. You have a lot of code, including ActiveRecord lookups, in the view where it does not belong. By the time it gets to the view, #links should already be transmogrified into whatever simple collection you want to iterate over.
So the view should look like this:
-#prepared_links.each do |link, url_html|
%p
=link_to link.title, link.embedded_url, :class => "youtube title_link"
=url_html
This means you need a few additional methods: embedded_url can go in your Link model and can essentially just be that "http://youtube.com/embed/.../?rel=0" string you have in link_to now.
As for displaying url_html for the first three, try something like this in your controller:
def my_method
#prepared_links = Link.sorted_by_vote_count # you'll have to add this method as well
url_htmls = #prepared_links[0..2].map(&:url_html)
#prepared_links = #prepared_links.zip(url_htmls)
end
This will give you an array of prepared links as paired values, in which the first three will be [some_link, url_html], and everything after the first three will be [some_link, nil], meaning nothing will be displayed for url_html unless it's actually there.
You can use the each_with_index method and customize each loop depending of the index value.
%ul.list_container
- #links.sort_by { |link| link.votes.where(:up => true).count - link.votes.where(:up => false).count }.reverse.each_with_index do |link, index|
%li{ :style => "#{index < 3 ? 'margin-bottom: 50px;' : ''}" }
.various_containers
%p= link_to link.title, "http://youtube.com/embed/#{link.url.to_s.match(/\/\/youtu.be\/(\S+)$/)[1]}/?rel=0", :class => "youtube title_link"
- if index < 3
= link.url_html

Why scan method for strings prints out directly in rails and ingnore the rest of code

I have a field (string) in DB that stores a list of values gotten from checkbox if they are checked:
Example:
checkbox1 [X] //Value 1
checkbox2 [ ] //Value 2
checkbox3 [X] //Value 3
checkbox4 [X] //Value 4
checkbox5 [ ] //Value 5
It stores in database the string "--- -'1' -'3' -'4'" (w/o the double quotes) I don't know why because in my form I have:
<% SoProfile::LANGUAGES.each do |key, value| %>
<div class="fluid">
<%= f.label :languages,class: "checkbox" do %>
<%= key %> <%= f.check_box :languages, {:multiple => true}, value, nil %>
<% end %>
</div>
<% end %>
in the model (I don't want to use a DB table for this):
LANGUAGES = { Espanol: 1, Ingles: 2, Portugues: 3, Italiano: 4, Mandarin: 5 }
anyways, it stores that string "--- -'1' -'3' -'4'" for the example above, I want to show a country flag according to the language instead of putting the language name.
I've created a helper method for this:
def insert_languages(string)
string.scan(/\d/).each do |i|
case i
when i == "1"
content_tag(:div,"",class: 'flag flag-co')
end
end
end
that is called in my view:
<tr>
<td>Idiomas <br />
<%= insert_languages(#so_profile.languages) %>
</td>
</tr>
But the helper method won't pass from .scan and will print out the hash ["1" "3" "4"] directly, it is, it never reaches the case code, like if there were a return in the string.scan(.. line of code.
How can I prevent rails from returning the hash and instead print the flag?
Try changing your method to following:
def insert_languages(string)
result = ""
string.scan(/\d/).each do |i|
case i
when "1"
result += content_tag(:div,"",class: 'flag flag-co')
end
end
result
end
You're getting hash since ruby returns values from .each block. Ruby returns last value from function implicitly. About your storing method - I suggest you reconsider your storage method. In mongoid, for example, you can use arrays. For active record something like that could solve your problem.
The each iterator doesn't return more than one value. If no return is explicitly declared, it will return the last object accessed in the method, which in this case is string.scan(/\d/). You can change .each to .map, which will return an array of content_tags, which would would iterate over in the view:
<% insert_languages(#so_profile.languages).each do |lang| %>
<%= lang %>
<% end %>

Passing parameter back to Model to refine Random action

I'm creating an application that'll display a random picture based upon a defined letter in a word.
Images are attached to a Pictures model (containing another "letter" field) using Paperclip, and will be iterated through in an each block.
How would I go about passing the letter back from the each block to the model for random selection.
This is what I've come up with so far, but it's throwing the following error.
undefined method `%' for {:letter=>"e"}:Hash
Model:
def self.random(letter)
if (c = count) != 0
find(:first, :conditions => [:letter => letter], :offset =>rand(c))
end
end
View:
<% #letters.each do |a| %>
<%= Picture.random(a).image(:thumb) %>
<% end %>
Thanks
One problem is your conditions has a syntax error. The hash notation is wrong:
:conditions => [:letter => letter]
should be
:conditions => {:letter => letter}
Also, it seems to me that your random scope will always exclude the first Picture if you don't allow an offset of 0. Besides that, do you really want to return nil if the random number was 0?
Picture.random(a).image(:thumb) would throw "undefined method 'image' for nil:NilClass" exception every time c==0. Can probably just use:
def self.random(letter)
find(:first, :conditions => {:letter => letter}, :offset =>rand(count))
end
EDIT: You'll either need to guarantee that your db has images for all letters, or tell the user no image exists for a given letter.
<% #letters.each do |a| %>
<% if pic = Picture.random(a).image(:thumb) %>
<%= pic.image(:thumb) %>
<% else %>
No image available for <%= a %>
<% end %>
<% end %>
Or the like...
EDIT: Actually I don't think your offset strategy will work. One other approach would be to return the set of images available for the given letter and randomly select from that collection, something like:
def self.random(letter)
pics = find(:all, :conditions => {:letter => letter})
pics[rand(pics.size)] if !pics.blank?
end

Resources