calling a model function in the views in rails raising error - ruby-on-rails

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

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.

Error displaying select field from enum on model

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

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

search items by category_id with select_tag drop down in rails

I have edited my first code and now it's better and cleaner thanks to #FunTimeFreddie, but the issue it's not yet properly solved. I'll come back with the right answer sooner.
In a search form I need to filter all menuitems:
1. per category
2. per category and search “query”
3. per min price && || max price
… and so on, with all possible combinations
I’ve manage to make a search in all menuitems with a “query”, min_price and max_price --all possible combinations from the search form. I can NOT manage to have the list of results of the chosen category, what am I doing wrong?
This is my model(edited):
class Menuitem < ActiveRecord::Base
belongs_to :menu_category
include Filterable
scope :newest_first, lambda { order('menuitems.created_at DESC') }
scope :last_one, lambda { order('menuitems.created_at ASC').last }
scope :search_keyword, lambda { |query|
where(["title LIKE ? or body LIKE ?", "%#{query}%", "%#{query}%"]) if query != ""
}
scope :menu_category_id, lambda { |menu_category_id|
where( "menu_category_id = ?", menu_category_id ) if menu_category_id != ""
}
scope :min_price, lambda { |price|
where("price > ?", price) if price != ""
}
scope :max_price, lambda { |price|
where("price < ?", price) if price != ""
}
end
This is my controller(edited):
class MenuitemsController < ApplicationController
def index
#menuitems = Menuitem.newest_first.filter(params.slice(:menu_category_id, :search_keyword, :min_price, :max_price))
end
And this is my view:
<%= simple_form_for :menuitem, :method => 'get', :url => {:action => 'index'} do |f| %>
<p>
<%= f.select :menu_category_id, options_for_select(#menucategories.map {|s| [s.title, s.id]}, params[:menu_category_id]), :selected => params[:menu_category_id], :onchange => "this.form.submit();", prompt: "Select category" %>
</p>
<p>
<%= f.input :search_keyword, input_html: { name: 'search_keyword', :value => params[:search_keyword]}, label: 'search recipe title', :required => false %>
<%= f.input :min_price, input_html: { name: 'min_price', :value => params[:min_price]}, label: 'min price:', :required => false %>
<%= f.input :max_price, input_html: { name: 'max_price', :value => params[:max_price]}, label: 'max price:', :required => false %>
<%= f.button :submit, "search" %>
</p>
<% end %>
You can save yourself the trouble of all the IF statements in your controller for all of the combinations by adding an IF statement within the scopes. For example, and this can similarly be applied to the four scopes associated to your form,
# menuitem model
scope :search_keyword, lambda { |query|
where(["name LIKE ?", "%#{query}%"]) if query != ""
}
This will allow to include only a single line in your controller, the line beneath your first IF statement, as this will handle the blank parameters.
There seems to be two issues with the category parameter. The first is it is a nested parameter within params[:menuitem], so in order to access it we need to call params[:menuitem][:menu_category_id]. Not too sure why this is happening to be honest, though I would recommend in this instance using a form_tag as opposed to form_for, given that we are not adding or editing the menuitems table itself.
The second issue is the category parameter is passed as a string, whereas we need it as an integer. We'll need to convert to parameter before applying it.
Now I'm not familiar with the .filters method (is this part of a gem?) but I got this to work the old fashioned way just by concatenating all the scopes together in one line as follows...
# menuitems controller
def index
#menuitems = Menuitem.newest_first.min_price(params[:min_price]).max_price(params[:max_price]).search_keyword(params[:search_keyword]).menu_category_id(params[:menuitem][:menu_category_id].to_i)
end
Note, another way of changing the data type would be to do so in the scope. You could do this as follows
# menuitem model
scope :menu_category_id, lambda { |menu_category_id|
where( "menu_category_id = ?", menu_category_id.to_i ) if menu_category_id != ""
}

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