Rails 3 - how to implement changing case of descriptions based on a check_box_tag (not part of model) in before_save callback in external class - ruby-on-rails-3

I have an rails 3 application where there are multiple registrations (diagnosis, patient, laboratory test, service, client, user, supplier). Initially these will be populated by seeding the database. The requirement is for the description codes to be mixed case (capitalised first word) when either
1. specified by the application (some configuration setting - yet to be determined)
2. specified by data entry user
At present I have a model, view & controller for Diagnosis which contains two fields:
1. code (always to be capitalised)
2. description (First word capitalised based on check_box_tag value)
Presently I am using a before_save callback in the model to implement the conversion, but I cannot get it to only work when the check_box_tag is not selected i.e. its ignoring the check_box_tag.
I have tried changing the check_box_tag to a check_box adding an attr_assessor to the model (but not the sqlite3 db as it is not required to be stored).
This didn't work either.
How do I accomplish this? How do I override the option to use a checkbox from an internal application configuration file which results in either the checkbox being 'unavailable' or not visible if the application configuration specifies not user selectable?
Model (diagnosis.rb)
require 'DescriptionHelper'
class Diagnosis < ActiveRecord::Base
attr_accessible :code, description
string_correct_case = DescriptionHelper.new([:code, :description])
validates :code, :presence => true, :length => { :minimum => 4, :maximum => 4 }
validates :description, :presence => true
before_save string_correct_case
end
Callback in DescriptionHelper.rb
class DescriptionHelper
def initialize(attribute)
#attrs_to_manage = attribute
end
def before_save(record)
#attrs_to_manage.each do |attribute|
record.send("#{attribute}=", capitaliseWords(record.send("#{attribute}")))
end
end
private
def capitaliseWords(value)
value = value.mb_chars.downcase.to_s.gsub(/\b\w/) { |first| first.upcase }
end
end
Controller (diagnoses_controller.rb)
class DiagnosesController < ApplicationController
def new
#diagnosis = Diagnosis.new
end
def create
#diagnosis = Diagnosis.new(params[:diagnosis])
if #diagnosis.save
flash[:notice] = "Diagnosis created with params [#{#diagnosis.attributes.inspect}" #for debugging, once fixed will be just 'Diagnosis created.'
redirect_to #diagnosis
else
flash[:alert] = "Diagnosis not created."
render :action => "new"
end
end
.. other controller actions - edit, show, destroy
end
View (_form.html.erb)
<%= form_for(#daignosis) do |f| %>
<div class="field">
<%= f.label :code %>
<%= f.text_field :code %>
</div>
<div class="field">
<%= f.label :description %>
<%= f.text_field :description %>
</div>
<div class="field">
<%= check_box_tag("diagnosis_desc_dont_convert", 1, false) %><%= f.label "Leave as entered" %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
When this runs currently the check_box_tag is ignored.
When adding in the model an attar_assessor :description_correctcase and changing the view to use f.check_box 'description_correctcase' this is still ignored.
How does one get this to work?
Thanks in advance from a rails aspiring developer.

Finally got a solution to the problem, after reading and re-reading various SO solutions to component parts of my question. I'm not sure its correct in terms of rails, but it works.
If you can offer me a better solution I would certainly learn from this.
Here is my solution.
Model (diagnosis.rb)
require 'DescriptionHelper'
class Diagnosis < ActiveRecord::Base
attr_accessor :do_not_correctcase
attr_accessible :code, :description, :do_not_correctcase
before_save DescriptionHelper.new([:code, :description]), :if =>
lambda { |d| d.do_not_correctcase.to_s == '0' }
validates :code, :presence => true, :length => { :minimum => 4, :maximum => 4 }
validates :description, :presence => true
end
This I referenced from the following SO solution - https://stackoverflow.com/a/6388691/1108010
Controller (diagnoses_controller.rb)
class DiagnosesController < ApplicationController
def new
#diagnosis = Diagnosis.new
end
def create
#diagnosis = Diagnosis.new(params[:diagnosis])
#diagnosis.do_not_correctcase = params[:diagnosis][:do_not_correctcase]
logger.debug "New diagnoses: #{#diagnosis.attributes.inspect}"
logger.debug "Diagnosis should be valid: #{#diagnosis.valid?}"
logger.debug "code has value #{params[:code]}"
if #diagnosis.save
flash[:notice] = "Diagnosis created with params [#{#diagnosis.attributes.inspect}" #for debugging
redirect_to #diagnosis
else
flash[:alert] = "Diagnosis not created."
render :action => "new"
end
end
.. other controller actions - edit, show, destroy
end
I also changed the view to replace the check_box_tag with a check_box.
View (_form.html.erb)
<%= form_for(#daignosis) do |f| %>
<div class="field">
<%= f.label :code %>
<%= f.text_field :code %>
</div>
<div class="field">
<%= f.label :description %>
<%= f.text_field :description %>
</div>
<div class="field">
<%= f.check_box 'do_not_correctcase' %><%= f.label "Leave as entered" %><br />
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
So despite getting this to work I'm not clear on are the following:
When inspecting the attributes with "#{#diagnosis.attributes.inspect}".
I assume that the reason the attr_accessor variable is not included in the New diagnosis output is that it is not part of the database table and therefore Active Reocrd does not instanciate it as part of the new record with #diagnosis.new
Could someone be kind enough to confirm that.
Why does the log have no value for logger.debug "code has value #{params[:code]}"? What causes the params[:code] to be null in the logger output?
Logfile contained the following entry:
Started POST "/diagnoses" for 127.0.0.1 at 2012-03-05 09:36:38 +0000
Processing by DiagnosesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"RW/mzkhavGeaIW0hVLn0ortTnbCDlrX+FfzH4neLLsA=", "diagnosis"=>{"code"=>"tt02", "description"=>"description for tt02", "do_not_correctcase"=>"1"}, "commit"=>"Create Diagnosis"}
New diagnosis: {"code"=>"tt02", "created_at"=>nil, "description"=>"description for tt02", "updated_at"=>nil}
Diagnosis should be valid: true
code has value
I would dearly like to know what is the correct way to do all this, as I feel this is not very DRY or clean.

Related

undefined method `model_name' for Mailer - Rails 5

I am trying to make a contact me form in Rials 5 with the built in Mail features.
I put it together a lot like a regular controller and views with a Mailer as the model. But when I have it all written out I get the error:
undefined method `model_name' for #ContactMailer
Contact is the name of the Mailer.
The error comes up when I try to view the new.html.erb page with rails server running. It looks like this:
Screen Shot
Here is the code I am working with:
contacts_controller.rb
class ContactsController < ApplicationController
def new
#contact = ContactMailer.new
end
def create
#contact = ContactMailer.new(params[:name, :email, :message])
if #contact.deliver
flash[:success] = "Your message has been sent."
else
flash[:error] = "There was a problem sending your message."
render 'new'
end
end
And
new.html.erb
<%= form_for #contact do |f| %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control', required: true %></div>
<div class="form-group">
<%= f.label :message %>
<%= f.text_area :message, class: 'form-control' %>
</div>
<%= f.submit "Send", class: 'btn btn-default' %>
<% end %>
There is also the generated Mailer and views, but I have not changed anything with those.
From what I have been able to research the error could be that I have not initialized the #contact variable, but I believe that I have. And now I am lost. Any help would be appreciated.
As additional information:
My plan is for this contact form to send an email using send grid on Heroku.
You are trying to build a mailer.Thats not how they are used. They are not models.
Instead you would do something like:
ContactMailer.some_method_you_added_to_your_mailer(params[:name, :email, :message]).deliver_now
You don't use new on them.
Suggest you read over http://guides.rubyonrails.org/action_mailer_basics.html .

Rails Undefined Method 'model_name'

I have the following model:
class Contact
attr_accessor :name, :emails, :message
def initialize(attrs = {})
attrs.each do |k, v|
self.send "#{k}=", v
end
end
def persisted?
false
end
end
I am calling to a contact form in my view like so:
<div class="email_form">
<%= render 'form' %>
</div>
Here is the controller:
class ShareController < ApplicationController
layout "marketing_2013"
respond_to :html, :js
def index
#contact = Contact.new
end
end
Here is the Form:
<%= form_for(#contact) do |f| %>
<%= f.label :name, "Your Name" %>
<%= f.text_field :name %>
<%= f.label :text, "Send to (separate emails with a comma)" %>
<%= f.text_field :emails %>
<%= f.label :message, "Email Text" %>
<%= f.text_area :message %>
<%= f.submit %>
<% end %>
For some reason I keep getting this error:
undefined method model_name for Contact:Class
Any reason why what I have currently wouldn't work?
Besides the correct route in your config/routes.rb, you will also need these two instructions on your model:
include ActiveModel::Conversion
extend ActiveModel::Naming
Take a look at this question: form_for without ActiveRecord, form action not updating.
For the route part of these answer, you could add this to your config/routes.rb:
resources :contacts, only: 'create'
This will generate de following route:
contacts POST /contacts(.:format) contacts#create
Then you can use this action (contacts#create) to handle the form submission.
add include ActiveModel::Model to your Contact file
your route probably doesn't go where you think it's going and therefore #contact is probably nill
run "rake routes" and check the new path.. if you are using defaults, the route is
new_contact_path.. and the erb should be in file: app/views/contacts/new.html.erb
def new
#contact = Contact.new
end

Rails nested form with multiple entries

I have a Sezzion model:
attr_accessible :description
has_many :session_instructors, :dependent => :destroy
has_many :instructors, :through => :session_instructors
accepts_nested_attributes_for :session_instructors
accepts_nested_attributes_for :instructors
Instructor model:
attr_accessible :bio
has_many :sezzions, :through => :session_instructors
has_many :session_instructors, :dependent => :destroy
SessionInstructor model:
attr_accessible :instructor_id, :sezzion_id
belongs_to :sezzion
belongs_to :instructor
Lastly, User model:
has_many :sezzions
has_many :instructors
I'm trying to create a form for Sezzion with nested form for SessionInstructor which has multiple select option for Instructors.
How can I do the following:
nested form for SessionInstructor
use multiple select option to get all the selected Instructor's instructor_id
hidden field to pass in the created/updated session_id with each select instructor
I have the following code as of now:
<%= form_for(#sezzion) do |f| %>
<%= f.label :description %>
<%= f.text_area :description %>
<%= f.label :instructors %>
<%= fields_for :session_instructors do |f| %>
<select multiple>
<% current_user.instructors.each do |instructor| %>
<option><%= instructor.name %></option>
<% end %>
</select>
<% end %>
<%= f.submit %>
<% end %>
Thank you so much!
This is something that seems ridiculously hard in Rails.
I think something like this might work:
<%= f.fields_for :session_instructors do |si| %>
<%= si.collection_select :instructor_ids, current_user.instructors, :id, :name, multiple: true>
<% end %>
This should create a form element which will set sezzion[session_instructors_attributes][instructor_ids].
Although I'm not sure if that's actually what you want. I've never tried this using a multi select. If it doesn't work, you could also try getting rid of the fields_for and just using f.collection_select. If you're willing to use a checkbox, I can show you how to do that for sure.
I hope that helps.
Edit:
Here's how I would usually do it with a check_box:
<%= f.fields_for :session_instructors do |si| %>
<%= si.hidden_field "instructor_ids[]" %>
<% current_user.instructors.each do |i| %>
<%= si.check_box "instructor_ids[]", i.id, i.sezzions.include?(#sezzion), id: "instructor_ids_#{i.id}" %>
<%= label_tag "instructor_ids_#{i.id}", i.name %>
<% end %>
<% end%>
There are a couple "gotchas!" with this method. When editing a model, if you deselect all checkboxes then it won't send the parameter at all. That's why the hidden_field is necessary. Also, you need to make sure each form element has a unique id field. Otherwise only the last entry is sent. That's why I manually set the value myself.
I copy pasted and then edited. Hopefully I got the syntax close enough where you can get it to work.
FINAL EDIT:
Per Sayanee's comment below, the answer was a bit simpler than I thought:
<%= f.collection_select :instructor_ids, current_user.instructors, :id, :name, {}, {:multiple => true} %>
#Sayanee, can you post how your instructors, sezzions table look like. Also for note, you can not get instructor_ids from Instructor object, hence you are getting "undefined method" error. With the current association that you shared, you can get instructor_ids from a Sezzion object. So you need to loop through current_user.sezzions in stead of current_user.instructors.
This is a way to implement fields_for nested form with html multiple_select in case of a has_many :through association. Solved it by doing something like this. The form view:
<%= form_for(#sezzion) do |f| %>
...
<%= fields_for :session_instructors do |g| %>
<%= g.label :instructor, "Instructees List (Ctrl+Click to select multiple)" %>:
<%= g.select(:instructor_id, Instructor.all.collect { |m| [m.name, m.id] }, {}, { :multiple => true, :size => 5 }) %>
<%= g.hidden_field :your_chosen_variable_id, value: your_chosen.id %>
<% end %>
...
<%= f.submit %>
<% end %>
Note:Since the #sezzion would not be saved at the time of generating the form you cannot pass that id (#sezzion.id) in place of your_chosen.id through the form. You could handle that save in the controller.
Make sure that your controller Initializes the Variables while generating the form: Your def new could look something like this:
def new
#sezzion = Sezzion.new
#sezzion.session_instructor.build
#sezzion.instructors.build
end
Now the create controller has to be able to accept the strong params required for the multiple select, so the sezzion_params method may look something like this:
def sezzion_params
params.require(:sezzion).permit(:description, :any_other_fields,
:session_instructors_attributes =>
[:instructor_id => [], :your_chosen_id => Integer])
end
In the create function, the first session_instructor variable is linked to the #sezzion instance variable through our new function. The other session_instructors in our multiple select must be built after the Sezzion instance is saved, if you want to pass in the created #sezzion.id with each select instructor. .
def create
#sezzion = Sezzion.new(sezzion_params)
#startcount=1 #The first element of the array passed back from the form with multiple select is blank
#sezzion.session_instructors.each do |m|
m.instructor_id = sezzion_params["session_instructors_attributes"]["0"]["instructor_id"][#startcount]
m.your_chosen_variable_id = your_chosen.id
#startcount +=1
end
if #sezzion.save
sezzion_params["session_instructors_attributes"]["0"]["instructor_id"].drop(#startcount).each do |m|
#sezzion.session_instructors.build(:instructor_id => sezzion_params["session_instructors_attributes"]["0"]["instructor_id"][#startcount],
:your_chosen_variable_id => your_chosen.id).save
#startcount += 1
end
flash[:success] = "Sezzion created!"
redirect_to root_url
else
flash[:danger] = "There were errors in your submission"
redirect_to new_sezzion_path
end
end

Simple form issue with Rails 3

I've been trying recently to show a list of the fields modified with success on submitting a form. The only problem is that my form (I use simple form) doesn't show the errors when there are some and the form can't be submitted.
Here's my code simplified :
def update
#wizard.assign_attributes(params[:wizard])
# Get changed attributes
if #wizard.save
# Set the success flash with fields modified
redirect_to #wizard
else
#title = "Edition du profil"
render 'edit'
end
end
The view :
<%= simple_form_for #wizard do |f| %>
<%= f.input :email %>
<%= f.input :story %>
<%= f.submit "Modifier", :class => "btn success small" %>
<% end %>
The model :
class Wizard < ActiveRecord::Base
has_secure_password
attr_accessible :email, :story, :password, :password_confirmation, :password_digest
serialize :ranks, Array
validates_presence_of :email, :first_name, :last_name, :gender, :story
validates_presence_of :password, :password_confirmation, :unless => Proc.new { |w| w.password_digest.present? }
# Other validations here
has_one :subject, :foreign_key => "teacher_id"
ROLES = %w[teacher]
scope :with_role, lambda { |role| {:conditions => "roles_bitmask & #{2**ROLES.index(role.to_s)} > 0"} }
# Other functions here
end
Has anyone an idea ?
Thank you in advance !
It has probably something to do with how you overwrote AR. I remember some plugin getting in trouble with assign_attributes. Meanwhile you can try :
#wizard.assign_attributes(params[:wizard], :without_protection => true)
If that works it will at least narrow down the problem to mass assignment.
you perhaps missing this part in edit/new view.Where #wizard is your model_name.
Write this piece of code in the form tag.
<% if #wizard.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#wizard.errors.count, "error") %> prohibited this task from being saved:</h2>
<ul>
<% #wizard.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>

Voting app in rails 3: how do I link to vote method?

To teach myself Rails, im building an extremely simple Voting app.
There are 2 models, Question and Option. Question has_many Options and Option belongs_to Question.
Using the standard scaffolding, I have reached a stage where you can add a question, view it, and add options to it and see these options.
What I would like to do now is add the code that increases an option.count value by one when clicking on a link. I have a vote_up method in the Option model:
class Option < ActiveRecord::Base
validates :text, :presence => :true
belongs_to :question
def vote_up
self.count += 1
end
end
My Options controller looks like:
class OptionsController < ApplicationController
def create
#question = Question.find(params[:question_id])
#option = #question.options.create(params[:option])
redirect_to question_path(#question)
end
end
My Question model looks like:
class Question < ActiveRecord::Base
validates :text, :presence => {:message => 'A question normally has text...'}
has_many :options, :dependent => :destroy
def vote
# Maybe the vote code will go here???
end
end
And my Question controller has the usual new, create, edit, destroy methods that the scaffold creates. V little customisation here.
My show.html.erb view where I would like to put the link to the vote method looks like:
<p id="notice"><%= notice %></p>
<p>
<b>Question <%= #question.guid %></b>:
<%= #question.text %>
</p>
<% if #question.options.count == 0 %>
<p>Shame! there are currently no options to vote on. Add some! </p>
<% elsif #question.options.count == 1 %>
<p>One option in a vote is a dictatorship... Maybe add some more?</p>
<% end %>
<h2>Options:</h2>
<% #question.options.each do |option| %>
<p>
<%= option.text %>: ** Link to vote here!
</p>
<% end %>
<h2>Add an option to vote on</h2>
<%= form_for([#question, #question.options.build]) do |f| %>
<div class="field">
<%= f.label :text %><br />
<%= f.text_field :text %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% if #question.options.count == 0 # Only show edit if no options saved. %>
<%= link_to 'Edit', edit_question_path(#question) %> |
<% end %>
<%= link_to 'Back', questions_path %>
So what I am trying to do is add a "vote" link next to each option that calls the vote_up method in the options model. This is probably laughably easy, but i've hit a wall and would really appreciate any help.
Also, any suggestions on how to do this better would be welcome!
Thanks
Simon
I think #oded-harth has showed a right way, but I have two remarks:
First of all, Rails is a beautiful language and is written to simplify our developer lives ;) When programming in Rails you must never forget that. With this in mind, I want to point you to "increment()" method. So you can simply up-vote without unnecessary += 1. To down-vote use decrement(). I believe you can use it like this: option.increment(:count)
Second, I think it's a little dirty to have a whole form for a simple vote action. You can use something like this
<%= link_to "Vote Up", :url => { :action => :vote_up, :option_id => option.id }, :method => :put %>
To make it work you'll have to set your route something like this:
resources :votes
put :vote_up
end
What I would do is make the vote_up method in the controller:
def vote_up
option = Option.find(params[:option_id])
option.count += 1
redirect (to where do you want...)
end
And in the view I would call that method this way:
<%= form_for( option, :url => { :action => "vote_up", :option_id => option.id} ) do |f| %>
<%= f.submit("vote up") %>
<% end %>