Rails passing in variables into view helper methods from form selection - ruby-on-rails

I have a bit of code in my user.rb model like this:
def self.aggregate(articles)
array = []
articles.each do |a|
array << {
:id => a.nid,
:views => a.daily_view_metrics.sum_views(a.nid),
:date => a.daily_view_metrics.latest_date(a.nid),
:title => a.daily_view_metrics.latest_title(a.nid),
:visits => a.daily_view_metrics.sum_visits(a.nid)
}
end
return array
end
In my user_controller I pass into the show method #metrics = User.aggregate(#articles) (#articles being simply a subset of articles for that user)
Now in my view (user#show) i call #metrics.each do |m| and then output all the different things in a table. Now according to this video it seems that the link_to method with a url parameter seems to be the best way to have users dynamically switch what they want to sort against.
How can I input that url parameter to sort the array? I tried calling #metrics.sort_by{|h| h[params[:sort]]}.each do |m| with :sort being the url parameter from my links (i.e. the views table header link click passes :sort => ":views" in. Essentially I am trying to do this sort_by{|h| h[:views]} since that works fine for sorting the array. However nothing happens. The array isn't sorted.
EDIT:
I solved it by making the aggregate method pass the key in as a string (i.e. "id" as opposed to :id). then the url params works beautifully.
<%= link_to "Views", :sort => "views"%> now sorts it by views in ascending order.

To order in descending mode you can negate - the element that you are using to sort by.
Ordering by ascending and then do revert to your collection is inefficient.
For instance
$> [{a: 'a1', b: 1}, {a: 'a2', b: 2}].sort_by{ |h| -h[:b] }
# => [{:a=>"a2", :b=>2}, {:a=>"a1", :b=>1}]
$> [{a: 'a1', b: 1}, {a: 'a2', b: 2}].sort_by{ |h| h[:b] }
# => [{:a=>"a1", :b=>1}, {:a=>"a2", :b=>2}]
In the form of your view, you will have something like this (a RadioButton e.g but it could be a Select or whatever you prefer):
<%= radio_button_tag 'radio_order', 'ascending', true %> Ascending
<%= radio_button_tag 'radio_order', 'descending' %> Descending
<%= submit_tag "Order" %>
Then in your helper get the value using params[:radio_order]:
aggregate('views', params[:radio_order])

Related

Simple form with collection select returning a hash

I am trying to get a dropdown list of projects organized according to the owner name. For doing this I am using SimpleForm and the grouped_select method. The issue is that for now it is a hash that is send back to the dropdown list and not the name of the project. How can I get solely the name of the project within this hash ?
This is how I define my collection
def option_clients_projects
unless self.contractor.nil?
opt_clients = self.contractor.construction_projects.map{ |cp| {:construction_project => cp.name, :client_name => cp.client.name} }
opt_clients << {:construction_project =>"Add a new project", :client_name => ""}
opt_clients
end
end
//This is what i get out this method//
[{:construction_project=>"Toilette fr", :client_name=>"Martine depouhon"}, {:construction_project=>"démolition Chateau", :client_name=>"Carla"}]
>
This is my input as grouped_select within my SimpleForm
<%= f.input :construction_project_id, collection: current_user.option_clients_projects.group_by{ |d| d[:client_name] }, as: :grouped_select, group_method: :last, group_label_method: :first %>
//The result of the group_by operation//
{"Martine depouhon"=>[{:construction_project=>"Toilette fr", :client_name=>"Martine depouhon"}], "Carla "=>[{:construction_project=>"démolition Chateau 2 2", :client_name=>"Carla "}]}
>
group_method - The name of a method which, when called on a member of collection, returns an array of child objects representing the tags.

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 undefined method for array

In my controller I am trying to run a query to get all of the id's not referenced in another table like so:
#vlan_numbers = ActiveRecord::Base.connection.execute("SELECT pop_vlans.id, vlan_number FROM pop_vlans WHERE pop_vlans.id NOT IN (SELECT logical_interfaces.vlan_id FROM logical_interfaces) AND pop_id != " + #pop_id.to_s)
Then in my view I am trying to use collection_select to show these in a dropdown menu:
but the error I get is undefined method 'vlan_number' for [2, "2"]:Array where those values are just the first row of results from the query.
This is a diagram of the two tables involved:
logical_interfaces | pop_vlans
-------------------|-----------
vlan_id-------|----->id
.... | vlan_number
and the relationships in the models are:
pop_vlan.rb
belongs_to :logical_interface
logical_interface.rb
# no relationship defined
Update
This is how the form is generated:
<%= form_tag :controller => "circuit", :action => "update" %>
# other inputs
<%= select_tag options_for_select(#vlan_numbers) %>
# other inputs
</form>
You can use select with options_for_select(#vlan_numbers) instead of collection_select
results from ActiveRecord::Base.connection.execute doesn't get loaded into a model
Instead you could try YourModelName.find_by_sql(...) If you want to play with your model
UPDATE
Assuming the name of your attribute you want this select_tag to populate is vlan_id so:
<%= form_tag :controller => "circuit", :action => "update" %>
# other inputs
<%= select_tag 'vlan_id', options_for_select(#vlan_numbers) %>
# other inputs
</form>
ActiveRecord::Base.connection.execute returns Mysql2::Result not array of object as Arel/AR query does.
So it returns array of arrays containing pop_vlans.id, vlan_number. So have to use first or last in your collection_select instead of something.vlan_number
So you have to use options_for_select instead of collection_select like:
options_for_select(#vlan_numbers.collect{|v| v.first})

How to display Rails select field values rather than stored integers in other views

I'm using a select field in a Rails app that is NOT tied to a related model, but stores integer values for a static series of options , i.e.,
<%= select (:this_model, :this_field, [['Option1',1],['Option2',2],['Option3',3],['Option4',4]] ) %>
In a show/ index view, if I want to display the option text (i.e. Option1, Option2, etc) rather than the integer value stored in the database, how do I achieve this?
Thanks for helping a noob learn the ropes!
EDIT
Based on Thorsten's suggestion below, I implemented the following. But it is returning nil, and I can't figure out why.
Invoice model:
##payment_status_data = { 1 => "Pending Invoice" , 2 => "Invoiced" , 3 => "Deposit Received", 4 => "Paid in Full"}
def text_for_payment_status
##payment_status_data[payment_status]
end
Invoice show view:
Payment Status: <%= #invoice.text_for_payment_status %>
In the console:
irb > i=Invoice.find(4)
=> [#<Invoice id: 4, payment_status: 1 >]
irb > i.text_for_payment_status
=> nil
I've tried defining the hash with and without quotes around the keys. What am I missing?
something like this would work:
<%= form_for #my_model_object do |form| %>
<%= form.label :column_name "Some Description" %>
<%= form.select :field_that_stores_id, options_for_select({"text1" => "key1", "text 2" => "key2"}) %>
<% end %>
Update
If you later want to display the text you can get it from a simple hash like this:
{"key1" => "text 1", "key2" => "text2"}[#my_object.field_that_stores_id]
But you better store this hash somewhere in a central place like the model.
class MyModel < ActiveRecord
##my_select_something_data = {"key1" => "text 1", "key2" => "text2"}
def text_for_something_selectable
##my_select_something_data[field_that_stores_id]
end
end
Then you can use it in your views like
#my_object.text_for_something_selectable
There are many possible variations of this. But this should work and you would have all information in a central place.
Update
Ok, I used something similar for our website. We need to store return_headers for rma. Those need to store a return reason as a code. Those codes are defined in an external MS SQL Server Database (with which the website exchanges lots of data, like orders, products, and much more). In the external db table are much more return reasons stored than I actually need, so I just took out a few of them. Still must make sure, the codes are correct.
So here goes he model:
class ReturnHeader < AciveRecord::Base
##return_reason_keys = {"010" => "Wrong Produc",
"DAM" => "Damaged",
"AMT" => "Wrong Amount"}
def self.return_reason_select
##return_reason_keys.invert
end
def return_reason
##return_reason_keys[nav_return_reason_code]
end
end
Model contains more code of course, but that's the part that matters. Relevant here is, that keys in the hash are strings, not symbols.
In the views i use it like this:
In the form for edit:
<%= form_for #return_header do |form| %>
<%= form.label :nav_return_reason_code "Return Reason" %>
<%= form.select :nav_return_reason_code, options_for_select(ReturnHeader.return_reason_select, #return_header.nav_return_reason_code) %>
<% end %>
(Maybe no the most elegant way to do it, but works. Don't know, why options_for_select expects a hash to be "text" => "key", but that's the reason, why above class level method returns the hash inverted.)
In my index action the return reason is listed in one of the columns. There I can get the value simply by
#return_headers.each do |rh|
rh.return_reason
end
If you have trouble to get it run, check that keys a correct type and value. Maybe add some debug info with logger.info in the methods to see what actual data is used there.

alphabetical pagination in rails

I'm searching a gem for Rails for alphabetical pagination. I wish I could have a list of first letters found in the result (I mean, if there is no row beginning with 'a', I don't want the 'a' to be display on the pagination links). Is this kind of gem already exists?
Thanks in advance!
This wouldn't be too hard to create at all, for example if you had a find, maybe like:
#all_words = Word.select("words.word")
…which returned a result a result set such as a list of words like this:
["alphabet", "boy", "day", "donkey", "ruby", "rails", "iPad"]
…the you could do this:
#all_words.collect {|word| word[0,1]}.uniq.sort
which would return:
["a", "b", "d", "r", "i"]
The .collect {|word| word[0,1]} stores the first letter of each word into a new array whilst uniq filters out the unique letters and sort sorts these alphabetically.
Simply assign this to a variable and you can use it in your view like so:
<ul>
<% #first_letters.each do |letter| %>
<%= content_tag :li, link_to(letter, words_pagination_url(letter), :title => "Pagination by letter: #{letter}") %>
<% end %>
</ul>
Your controller action could then decide what to do with the param from the pagination if one is passed in:
def index
if params[:letter]
#words = Word.by_letter(params[:letter])
else
#words = Word.all
end
end
And then the scope in your model would look something like:
scope :by_letter,
lambda { |letter| {
:conditions => ["words.word LIKE ?", "#{letter}%"]
}}
Your routes require something like:
match "words(/:letter)" => "words#index", :as => words_pagination
I haven't tested this all the way through but it should set you on the right path.
To get a dynamic select from the appropriate table, you can use a dynamic SQL finder.
In this example, we select from a table named 'albums', and fabricate a column 'name' to hold the values. These will be returned in the 'Album' model object. Change any of these names to suit your needs.
Album.find_by_sql("SELECT DISTINCT SUBSTR(name,1,1) AS 'name' FROM albums ORDER BY 1")
Note that you can't use the Album model objects for anything except querying the 'name' field. This is because we've given this object a lobotomy by only populating the 'name' field - there's not even a valid 'id' field associated!
I've created an alphabetical pagination gem here:
https://github.com/lingz/alphabetical_paginate
For anyone still having issues in this domain.

Resources