Error displaying select field from enum on model - ruby-on-rails

I'm getting errors when I try to display a select dropdown of enums from my model.
I have defined an enum in my model Plants:
class Plant < ActiveRecord::Base
belongs_to :garden
enum life_cycle: [ :annual, :perennial, :biennial ]
enum sun: [ :full_sun, :part_shade, :full_shade ]
enum sow_method: [ :direct, :indoor, :direct_indoor ]
end
I want the corresponding input to display those enum options. I see from Saving enum from select in Rails 4.1 it can be approached like this (in _form.html.haml):
= simple_form_for(#plant) do |f|
= f.error_notification
.form-inputs
= f.input :name
= f.input :scientific_name
= f.input :height
= f.input :width
= f.input :spacing
= f.input :life_cycle, :as => :select, :collection => Plant.life_cycle.keys.to_a
= f.input :sun
= f.input :sow_method
= f.input :direct_seed_start
= f.input :direct_seed_stop
= f.input :indoor_seed_start
= f.input :indoor_seed_stop
= f.input :transplant_start
= f.input :transplant_stop
= f.association :garden
.form-actions
= f.button :submit
When I try to visit the edit page I get an "undefined method" error. I'm very new to ruby so I'm probably misunderstanding something simple...
Thanks

Few examples before the answer
# Assume life_cycle was set to 'annual'
puts p.life_cycle
#=> "annual"
p.life_cycle = 0 # set life_cycle as 0
puts p.life_cycle
#=> "annual"
p.life_cycle = 2 # we set to biennial
puts p.life_cycle
#=> "biennial"
You could also do this to see if life_cycle is annual by p.annual? results in true or false
And when you do this, using a plural of the enum, you can access it on class as a class method.
puts Plant.life_cycles
{"annual" => 0, "perennial" => 1, "biennial" => 2}
So yeah as #razr describes you would use the class method to get the hash and extract keys to form your select menu which is Plant.life_cycles.keys

I have done something like this in one of my forms (using simple_form):
= f.select :life_cycle, Plant.life_cycles.keys

Related

Adding new field in RoR simple-form gives ActionView::Template::Error

We are looking to add a new hidden field into our form but every time I add in a new field (even copy and pasting a field that works and just adding a number to the end) we get an error,
ActionView::Template::Error (undefined method `hidden_gclid' for #<Wizard::Lead::Step2:0x00000004d859d8>):
Please let me know which code you would like to see. I tried reading some documentation here and thought, I might need to add a database column but I'm not sure.
Thank you all for the help and I apologize for the delay. I didn't receive
notification on the comments. I have a new problem though. After getting the new field created we are getting this error,
F, [2019-01-09T05:08:28.887941 #26599] FATAL -- : [437ed38a-c36c-4121-9608-385c43d0449e]
F, [2019-01-09T05:08:28.888395 #26599] FATAL -- : [437ed38a-c36c-4121-9608-385c43d0449e] NameError (undefined local variable or method `hidden_gclid' for #<Lead:0x0000000527c4a0>):
F, [2019-01-09T05:08:28.888449 #26599] FATAL -- : [437ed38a-c36c-4121-9608-385c43d0449e]
F, [2019-01-09T05:08:28.888487 #26599] FATAL -- : [437ed38a-c36c-4121-9608-385c43d0449e] app/models/lead.rb:91:in `sales_force_info'
lead.rb
# This model represents an User's (customer) submission through the form
class Lead < ApplicationRecord
validates :address, :lat, :lng, presence: true
belongs_to :user, optional: true
has_and_belongs_to_many :characteristics
has_many :offers, inverse_of: :lead, dependent: :destroy
scope :newest_first, (-> { order(created_at: :desc) })
enum pool_type: %i[
in_ground above_ground none_or_community
]
enum kitchen_condition: %i[
great_kitchen typical_kitchen needs_work_kitchen
]
enum bathroom_condition: %i[
great_bathroom typical_bathroom needs_work_bathroom
]
enum timeline_to_sell: %i[
asap 2_4_weeks 4_6_weeks few_months just_curious
]
enum looking_for_another: %i[
yes already_found no
]
enum reasons_for_selling: %i[
upgrading relocating downsizing retiring selling_investment
]
enum offer_status: %i[
pending sent accepted declined closed
]
attr_accessor :hidden_gclid
def has_basic_information?
if bedrooms.present? || bathrooms.present? ||
built_surface.present? || pool_type.present? ||
kitchen_condition.present? || bathroom_condition.present? ||
renovated.present? || renovated_spent.present? ||
renovated_description.present?
return true
end
false
end
def street
(address.remove(', USA')&.split(",").count == 3) ? address.remove(', USA')&.split(",").first : "#{address.remove(', USA')&.split(",").first}, #{address.remove(', USA')&.split(",").second}"
end
def city
address.remove(', USA')&.split(",").second_to_last
end
def state_code
address.remove(', USA')&.split(",").last
end
def sales_force_info
form = { 'oid' => 'xxxxxx', # roganization id
'retURL' => 'http://placeholder.com',
'00No0000009e5Lb' => 'Cash Offer',
'country_code' => 'US',
'first_name' => user&.first_name,
'last_name' => user&.last_name,
'phone' => user&.phone,
'street' => street, #address
'city' => city,
'state_code' => state_code,
'zip' => zip,
'description' => "Check more lead info at www.placeholder.com/leads/#{id}",
'email' => user&.email,
'lead_source' => user&.how_about_us || "Web",
'00N1N00000Oko3t' => bedrooms,
'00N1N00000Oko3y' => bathrooms,
'00N1N00000Oko43' => built_surface,
'00N1N00000Oko48' => air_conditioner,
'00N1N00000Oko4N' => roof_age,
'00N1N00000Oko4h' => timeline_to_sell,
'00N1N00000Oko4m' => looking_for_another,
'00N1N00000Oko4r' => reasons_for_selling,
'00N1N00000Oko4S' => renovated,
'00N1N00000Oko4X' => renovated_spent,
'00N1N00000Oko4c' => renovated_description,
'00N1N00000Oko4w' => own_valuation,
'00N1N00000PMKAo' => hidden_gclid
}
form
end
def send_info_to_sales_force
puts "SENDING INFO TO SALES FORCE!"
url = URI('https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8')
form = sales_force_info
res = Net::HTTP.post_form(url, form)
puts res.body
end
end
form
:javascript
window.onload = function getGclid() {
document.getElementById("00N1N00000PMKAo").value = (name = new RegExp('(?:^|;\\s*)gclid=([^;]*)').exec(document.cookie)) ? name.split(",")[1] : ""; }
.block.block-fill-height.px-0
= render 'application/header', logo: 'logo-blue.png'
.container.mt-2.pt-5
.card.card-outline-primary
%h3.card-header
%ul.nav.nav-bordered
%li.nav-item
%a.nav-link{:href => "#"} Basics
%li.nav-item
%a.nav-link.active{:href => "#"} Sale
%li.nav-item
%a.nav-link{:href => "#"} Offer
.card-block
%h4.card-title Where should we send your offer?
%p.card-text
%strong Save your progress
and join thousands of home owners who work with us every month.
%hr
%h5.mt-5 Create your account below:
= simple_form_for User.new, url: '/users', method: 'POST', html: { class:'mt-4' } do |f|
.form-group
= f.label(:phone, 'Phone')
= f.input_field(:phone, class: 'form-control', placeholder: 'Phone', required: true)
.form-group
= f.label(:first_name, 'First Name')
= f.input_field(:first_name, class: 'form-control', placeholder: 'First Name')
.form-group
= f.label(:last_name, 'Last Name')
= f.input_field(:last_name, class: 'form-control', placeholder: 'Last Name')
.form-group
= f.label(:email, 'Email')
= f.input_field(:email, class: 'form-control', placeholder: 'Enter your Email')
.form-group
= f.label(:password, 'Password')
= f.input_field(:password, class: 'form-control', placeholder: '6 characters minimum')
.form-group
= f.label(:how_about_us, 'Where did you hear about us?')
%br
= f.select :how_about_us, ['TV Commercial', 'Word of Mouth', 'Radio', 'Web', 'Letter/Postcard/Doorhanger',
'Social Media', 'Telephone Call'], class: 'form-control', prompt: 'Please Select', required: true
%hr
= hidden_field_tag(:hidden_gclid, "", :id => "00N1N00000PMKAo")
= hidden_field_tag(:current_step, 'step3')
= f.submit 'Next', class: 'btn btn-success btn-lg'
It looks like we have the form going without any errors now but we need the hidden field ID 00N1N00000PMKAo to send to salesforce but it isn't.
Since simple_form relies on the model instance you have two options here:
add a database column to the corresponding table
use a virtual attribute
Looks like you don't want to store the value in the db, in this case just add attr_accessor :hidden_gclid to your model. After it you can use it like a normal attribute inside the controller action.

Ruby on Rails error when validates_uniqueness_of using collection_select

First, sorry for my bad English. I'm still learning.
I have 3 tables in my DB:
Problem
has_many :registers
has_many :solutions, through : :registers
Solution
has_many :problems
has_many :problems, through : :registers
Register
belongs_to: problem
belongs_to :solution
The system is working well. I am able to insert new data in all of the 3 tables.
In the views for the table/model Register, to select problems and solutions, I make use of collection_select, like this:
= form_for #register do |f|
.field
= f.label :problem_id
= collection_select( :register, :problem_id, #problems, :id, :name, {}, { :multiple => false })
.field
= f.label :solution_id
= collection_select( :register, :solution_id, #courses, :id, :name, {}, { :multiple => false })
.field
= f.label :entry_at
= f.datetime_select :entry_at
.actions = f.submit
The problem only appears when I try to add this validation to Register:
validates_uniqueness_of :student_id , scope: :course_id
Then I get:
> undefined method `map' for nil:NilClass
> = collection_select( :register, :problem_id, #problems, :id, :name, {}, { :multiple => false })
And I dont know why.
So, I tried to do the validation by the controller:
def create
#register = Register.new(register_params)
problem_id = #register.problem_id
solution_id = #register.solution_id
if Register.exists?(['problem_id LIKE ? AND solution_id LIKE ?', problem_id, solution_id ])
render 'new'
else
#register.save
respond_with(#register)
end
end
But the error remains.
I believe that the cause is the collection_select, but I don't know how to solve it.
Saying one more time, I am able to persist date in all the 3 DB tables. But when I try to avoid duplication, the error appears.
This is how I solve this problem:
def create
#register = register.new(register_params)
#if #register.save
# respond_with(#register)
#else
# #register = register.all
# render :new
#end
problem_id = #register.problem_id
solution_id = #register.solution_id
if register.exists?(['problem_id LIKE ? AND solution_id LIKE ?', problem_id, solution_id ])
#register = register.new
#solutions = Solution.all
#problems = Problem.all
flash[:error] = "Problem alread in the register for this solution"
render 'new'
else
#register.save
respond_with(#register)
end
end

Passing dynamic subject to Rails mail_form

I am using gem mail_form to handle contact in a Rails app, and I have 6 different contact forms.
I put a hidden_field_tag in the form and pass the desired subject as a variable. In the html, the value is there but the email arrives with (no subject). What can I be doing wrong?
In controllers
def where_to_buy
#contact = Contact.new
#the_subject = "Where to buy"
end
In contact form
= form_for #contact do |f|
= render "form", f: f
= f.text_area :message
.hide
= f.text_field :nickname, hint: 'Leave this field empty!'
= hidden_field_tag :mail_subject, #the_subject
= f.submit "Send Message"
In model
class Contact < MailForm::Base
attribute :mail_subject
attribute :first_name, validate: true
attribute :last_name, validate: true
attribute :message, validate: true
attribute :nickname, captcha: true
def headers
{
subject: %(#{mail_subject}),
to: "jorge#email123.com",
from: %("#{first_name} #{last_name}" <#{email}>)
}
end
end
Output html in chrome:
<input type="hidden" name="mail_subject" id="mail_subject" value="Where to buy">
Instead of:
= hidden_field_tag :mail_subject, #the_subject
You will want to use:
= f.hidden_field :mail_subject, value: #the_subject
If you inspect the parameters that are logged into your development.log you will see why.
When you use hidden_field_tag the mail_subject is defined as its own independent parameter and will not be included in the contact hash. You'll have something like:
params = { "contact" => { "message" => "text here", ... }, "mail_subject => "Where to buy" }
But when you use f.hidden_field the mail_subject will be included in the contact hash. You'll have something like:
params = { "contact" => { "message" => "text here", "mail_subject => "Where to buy", ... } }
And then when you call Contact.new(params[:contact]) the new contact object will get the mail_subject value.

calling a model function in the views in rails raising error

Here is the model
class Admin::Filter < ActiveRecord::Base
validates_uniqueness_of :name
has_many :filter_values,class_name: "Admin::FilterValue",foreign_key: "admin_filter_id"
enum field_type: [:select_tag,:select_tag_without_search,:check_box_tag]
def underscore_filter_name
if self.name.split.size > 1
self.name.replace(self.name.scan(/[A-Z][a-z]*/).join("_"))
else
"#{self.name.downcase}_filter"
end
end
end
The function I am talking about is underscore_filter_name. Now I am calling this inside the rails console like this: Admin::Filter.first.underscore_filter_name which returns a value but when I try similarly inside the view it throws an error. Here is the view:
-#filters.each do |filter|
%legend
=filter.name
-case filter.field_type
-when "select_tag"
= simple_form_for :"#{filter.underscore_field_name(filter)}",:url=> admin_requests_path,:method => "get",html: {:"data-filter"=>"#{filter.underscore_field_name}"} do |f|
= f.select("#{filter.name}", filter.filter_values.all.collect {|p| [ p.name, p.id ] }, {:include_blank => "Please select a #{filter.name}"},{:multiple => true,class: "form-control chosen-select select_tag_filter"})
-when "select_tag_without_search"
= select_tag "#{filter.name}", options_for_select(filter.filter_values.all.collect{ |u| [u.name, u.id]}), { :multiple => true,class: "search-free-chosen-select"}
-when "check_box_tag"
= simple_form_for :priority,:url=> admin_requests_path,:method => "get",html: {id: "priority_filter"} do |f|
= f.collection_check_boxes "#{filter.name}", filter.filter_values,:id,:name, :item_wrapper_class => 'inline'
The below line is the error I am getting:
undefined method `underscore_field_name' for #<Admin::Filter:0x007f07a322f068>
Why is this? I am using Rails 4.1
You have defined underscore_filter_name as the method name and you have typed in the view as underscore_field_name. So is the error.
Change it to
= simple_form_for :"#{filter.underscore_filter_name(filter)}",:url=> admin_requests_path,:method => "get",html: {:"data-filter"=>"#{filter.underscore_field_name}"} do |f|
This should resolve the error.
You've got in the view...
underscore_field_name
but the name of the method is
underscore_filter_name

Pass a parameter into collection select method for text_method

<%= collection_select(:catgory, :id, #categories, :id, :title, {}, data: { behavior: 'category_dropdown' }) %>
In the above code I need to pass a parameter to the title method. Is there any way to do this with collection_select?
<%= collection_select(:catgory, :id, #categories, :id, (:title, #program), {}, data: { behavior: 'category_dropdown' }) %>
Edit:
Looking at the internals for collection_select the text_method. It is eventually passed to a .send method which should allow for element.send(:title, #program). However, I think the issue why I still can't pass the param is that collection select is reading (:title, #program) as two params instead of one.
Use select instead:
select "catgory", "id", #categories.map{|c| [c.title(#program), c.id]}, {}, data: { behavior: 'category_dropdown' }
Should be working.
This can be done with collection_select if your model has an existing parameter you can overwrite:
f.collection_select( :your_model_id,
YourModel.all.map{|ym| ym.name = ym.custom_name(your_parameter); ym},
:id, :name,
{:selected => #model_instance.logic},
{:class => 'your class', :other => "..." } )
For instance I do this to conditionally pluralize my model's name attribute
class MyModel < ActiveRecord::Base
DEFAULT_NAME_COUNT = 99
def pluralized_name(n = DEFAULT_NAME_COUNT)
begin
return name.pluralize(n)
rescue
end
name
end
end

Resources