Error `undefined method `state' for "AK":String` for `collection_select` - ruby-on-rails

After adding the second method, uniq.pluck(:state) to the code below, I'm getting the following error message:
undefined method `state' for "AK":String.
I looked at all the posts on here and couldn't find anything related to this problem. Any insights or help you could offer would be greatly appreciated.
<%= f.collection_select :state, (Boli.order(:state).uniq.pluck(:state)), :id, :state, include_blank: true %>
Thank you #D-side, now having difficulties using grouped_collection. I'd like the user to be able to select a group of banks in a particular state. Getting the error message undefined method `map' for :id:Symbol, using the following code:
<%= f.grouped_collection_select :bank, :id, Boli.order(:bank), :id, :bank, include_blank: true%>

pluck with a single attribute name returns an array of attribute values. Strings, in your case.
collection_select, however, is built with model instances in mind, in that it accepts... well, the docs say it better anyway:
collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
<...>
The :value_method and :text_method parameters are methods to be called on each member of collection. The return values are used as the value attribute and contents of each <option> tag, respectively. They can also be any object that responds to call, such as a proc, that will be called for each member of the collection to retrieve the value/text.
Obviously, since you've already fetched values of the attribute state, calling state on the resulting values once more is nonsensical.
You can fix this in multiple ways.
PostgreSQL's DISTINCT ON (expr)
By replacing .uniq.pluck(:state) with .select("DISTINCT ON (state) id, state") you'll get ActiveRecord model instances, so every element of the resulting collection will have methods id and state, as collection_select expects.
Or use the query you have, with pluck
...by giving procs instead of :id and :state that take a string as an argument and produce appropriate values.
It all boils down to what you need.

Related

Simple form collection_radio_buttons show items from record model

I want to display all the available types in radio buttons in order to click and select on of them
= f.collection_radio_buttons :type_id, Type.all, :label => "Available Types"
but I'm getting the following error
wrong number of arguments (given 3, expected 4..6)
How can I fix the collection_radio_buttons in order to get the correct number of arguments?
Here is the api doc on collection_radio_buttons:
http://apidock.com/rails/v4.0.2/ActionView/Helpers/FormBuilder/collection_radio_buttons
it definitely indicates you need at least 4 arguments (minimum):
method, collection, value_method, text_method, options = {}
You have provided 3 - a method :type_id a collection Type.all
You've also provided options, but have missed the value_method and text_method That's what you need to add to make this work.
What method will be called on each Type for the text/values of the select-options? Usually this is something like: :id (for value_method) and something like :name (for text_method)

Gem countries select helper

I am trying to use the gem countries from hexorx. I really don't know how to bring that in the select helper. I tried to use the helper country_select like this
<%= f.country_select :country, :required => true %>
The GitHub location of gem is https://github.com/hexorx/countries.
The interface for this helper is defined as:
country_select(object, method, priority_countries = nil, options = {}, html_options = {})
I found this by going to the link you supplied, navigating through the source code to /lib/countries/select_helper.rb
Assuming this is in a form_for operating on an object instance, perhaps #user then an instance of User is what gets passed for the first parameter, object. The second argument, method is the attribute of the object that should receive the selected value, so in this example, your User model will need to have an attribute called country.
So the problem is likely that the third argument expects a list of priority_countries, but it's getting :required => true. If you pass nil, then pass your options, it should work, like this:
<%= f.country_select :country, nil, :required => true %>
For future posts, when something doesn't work, you should provide the complete error message or symptoms that you saw. Please refer to the FAQ

Can someone explain collection_select to me in clear, simple terms?

I am going through the Rails API docs for collection_select and they are god-awful.
The heading is this:
collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
And this is the only sample code they give:
collection_select(:post, :author_id, Author.all, :id, :name_with_initial, :prompt => true)
Can someone explain, using a simple association (say a User has_many Plans, and a Plan belongs to a User), what I want to use in the syntax and why?
Edit 1: Also, it would be awesome if you explained how it works inside a form_helper or a regular form. Imagine you are explaining this to a web developer that understands web development, but is 'relatively new' to Rails. How would you explain it?
collection_select(
:post, # field namespace
:author_id, # field name
# result of these two params will be: <select name="post[author_id]">...
# then you should specify some collection or array of rows.
# It can be Author.where(..).order(..) or something like that.
# In your example it is:
Author.all,
# then you should specify methods for generating options
:id, # this is name of method that will be called for every row, result will be set as key
:name_with_initial, # this is name of method that will be called for every row, result will be set as value
# as a result, every option will be generated by the following rule:
# <option value=#{author.id}>#{author.name_with_initial}</option>
# 'author' is an element in the collection or array
:prompt => true # then you can specify some params. You can find them in the docs.
)
Or your example can be represented as the following code:
<select name="post[author_id]">
<% Author.all.each do |author| %>
<option value="<%= author.id %>"><%= author.name_with_initial %></option>
<% end %>
</select>
This isn't documented in the FormBuilder, but in the FormOptionsHelper
I've spent quite some time on the permutations of the select tags myself.
collection_select builds a select tag from a collection of objects. Keeping this in mind,
object : Name of the object. This is used to generate the name of the tag, and is used to generate the selected value. This can be an actual object, or a symbol - in the latter case, the instance variable of that name is looked for in the binding of the ActionController (that is, :post looks for an instance var called #post in your controller.)
method : Name of the method. This is used to generate the name of the tag.. In other words, the attribute of the object you are trying to get from the select
collection : The collection of objects
value_method : For each object in the collection, this method is used for value
text_method : For each object in the collection, this method is used for display text
Optional Parameters:
options : Options that you can pass. These are documented here, under the heading Options.
html_options : Whatever is passed here, is simply added to the generated html tag. If you want to supply a class, id, or any other attribute, it goes here.
Your association could be written as:
collection_select(:user, :plan_ids, Plan.all, :id, :name, {:prompt => true, :multiple=>true })
With regards to using form_for, again in very simple terms, for all tags that come within the form_for, eg. f.text_field, you dont need to supply the first (object) parameter. This is taken from the form_for syntax.

String concatenation not possible?

So over the last 2 hours, I've been trying to fill a combobox with all my users. I managed to get all firstnames in a combobox, but I want their full name in the combobox. No problem you would think, just concatenate the names and you're done. + and << should be the concatenation operator to do this.So this is my code:
<%= collection_select(:user, :user_id, #users, :user_id, :user_firstname + :user_lastname, {:prompt => false}) %>
But it seems RoR doesn't accept this:
undefined method `+' for :user_firstname:Symbol
What am I doing wrong?
What you need to do is define a method on the User model that does this concatenation for you. Symbols can't be concatenated. So to your user model, add this function:
def name
"#{self.first_name} #{self.last_name}"
end
then change the code in the view to this:
<%= collection_select(:user, :user_id, #users, :user_id, :name, {:prompt => false}) %>
Should do the trick.
This isn't really rails giving you an error, it's ruby. You're trying to combine the symbols :user_firstname and :user_lastname
A symbol is a variable type, just like integer, string, or datetime (Well technically they're classes, but in this context we can think of them as variable types). They look similar to strings, and can function similarly to them, but there is no definition for the behavior of symbol concatenation. Essentially you're trying to send the method user_firstnameuser_lastname which is just as non-sensical as trying to concat two Symbols.
What you need to understand is that this parameter is looking for a method on your User object, and it won't understand the combination of two symbols. You need to define a method in your model:
def fullname
[user_firstname, user_lastname].reject{|v| v.blank?}.join(" ")
end
This'll return your first + last name for you, and then in the parameter you should send :fullname (because that's the method it'll call on each user object in the collection):
<%= collection_select(:user, :user_id, #users, :user_id, :fullname, {:prompt => false})%>
Also, it's considered poor practice to prefix every single column with the table name. user.user_firstname just looks redundant. I prefer to drop that prefix, but I guess it's mostly up to personal preference.
The arguments for value and display attribute are method names, not expressions on a user object.
To control the format more precisely, you can use the select tag helper instead:
select("user", "user_id", #users.each {|u| [ "#{u.first_name u.last_name}", u.user_id ] })
The docs are pretty useful.

Custom Formtastic Control with Multiple Parameters

I'm attempting to build a custom control for Formtastic that takes a latitude and a longitude, however, I'm not sure how to go about passing the method names through. Ideally I'd have the following in the semantic_form_for block:
f.input :latitude, :longitude, :as => :location
I've also tried passing with an array:
f.input [:latitude, :longitude], :as => :location
But in both cases, this fails - the first on the number of parameters, the second on the first parameter not being a symbol.
Is there any way of passing two methods into #input that I'm missing?
OK, I've sorted this out by writing a plugin for Formtastic.
I've added a multi_input function that can take any number of parameters and an (optional) options hash. I've also added a map_input type that outputs the map control and JS (framework agnostic).
More details at the above link.
You can't pass multiple methods to a single input. What you could do is have an accessor on your model called, uh, lat_long or similar, which could return a string or array or whatever in a format which your location_input recognises and knows how handle.

Resources