Rails undefined method for array - ruby-on-rails

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})

Related

Search through .select().group().distinct possible?

I want to create a search select that works on unique names through grouping...
The goal is to find duplicates to then use as a search parameter.
I want to find LineItem's :store_title's that match so I can create a select drop down for a way to search through LineItem's that match the specific :store_title.
Example:
LineItem DB:
line_item.title = "Hello"
line_item.title = "Hello"
line_item.title = "Okay"
line_item.title = "Bar"
I want to have a drop down select_tag in a search for the following:
[select]
"Hello"
"Okay"
"Bar"
And append all results that match LineItem.title of those which is selected.
I tried a few ways:
I so far have been able to get the unique drop down select field to "work" but not sure if it is the right way because it creates an array:
Controller:
#vendor_line_items = LineItem.where(vendor_id: #vendor.id).select(:store_title).group(:store_title).distinct
if params[:search]
#orders = Order.line_item_search(params[:search]).joins(:line_items).where(line_items: {vendor_id: #vendor.id})
end
ORders Model:
def self.line_item_search(search)
scope = joins(:line_items)
line_items = scope.where(line_items: { id: LineItem.where(store_title: "#{search.downcase}") })
line_items
end
View:
<%= form_tag vendor_orders_path, :method => 'get' do %>
<%= collection_select(:search, params[:search], #vendor_line_items, :store_title, :store_title, {}, {class: "form-control-sm col-5"})%>
<%= submit_tag "Search", :name => nil, class: "btn btn-primary btn-sm" %>
<% end %>
Error:
Undefined method 'downcase' for ["store_title"]:Array:
Can I alter my model to allow the array, or should i be finding the unique store titles another way?
If you just want to downcase the strings in that array, you could do something like this:
line_items = scope.where(line_items: { id: LineItem.where(store_title: search.map(&:downcase)) })
That will provide a list of downcased strings to your query.
I tried finding a way to remove the brackets before searching so the brackets were not present in the search parameters but couldn't figure it out.
Doing...:
line_items = scope.where(line_items: { id: LineItem.where(store_title: "#{search.join(', ')}") })
Solved it. This removes the brackets before searching the DB and works.
**
UPDATE:
**
View for response and answer to my issue:
Rails search form through arrays doesn't work on second search attempt

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.

Rails passing in variables into view helper methods from form selection

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])

Rails how to call helpers such as .humanize on an attribute of collection_select

I have this collection_select <%= collection_select(:template, :template_id, #templates, :id, :name) %> and I want to basically call :name.humanize, but obviously that doesn't work.
How can i call a method such as .humanize on an attribute of collection_select (a hash attribute?)
EDIT
Here's a snippet from my controller:
#templates = Template.select(:name).group(:name)
p "templates = #{#templates}"
Here's what appears in the console:
"templates = #<ActiveRecord::Relation:0x007f84451b7af8>"
With Rails 4
Just like this:
<%= collection_select(:template, :template_id, #templates, :id, Proc.new {|v| v.name.humanize}) %>
In the Proc block, v will be your models, iterated through a each loop.
collection_select works with anything that inherit from Enumerable and have no limit in the content of the Proc.
With Rails 3
You must do it your self, by preparing your data before passing it to collection_select
# This will generate an Array of Arrays
values = #templates.map{|t| [t.id, t.name.humanize]}
# [ [record1.id, record1.name.humanize], [record2.id, record2.name.humanize], ... ]
# This will use the Array previously generated, and loop in it:
# - send :first to get the value (it is the first item of each Array inside the Array)
# - send :last to get the text (it is the last item of each Array inside the Array)
# ( we could have use :second also )
<%= collection_select(:template, :template_id, values, :first, :last) %>

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.

Resources