Accessing POST params from different controller methods - ruby-on-rails

I inherited a Rails 2.2.2 project that has a form with no server-side form validation so I am trying to add it.
Is there a way to pass the POST variables from the submit_request method below back to the signup method (also below)?
All I am trying to do is repopulate the form with whatever was entered, along with the form validation error message:
class LoginController < ApplicationController
## The controller that displays & processes the form
# Form view
def signup
#hide_logout = "YES"
end
#Form validator/processor
def submit_request
#hide_logout = "YES"
#name = (params[:name] ? params[:name] : "")
#email = (params[:email] ? params[:email] : "")
...
## Validate posted values here
## Build error message, if needed
if(error_str !="")
flash[:warning] = error_str
redirect_to :controller => "login/signup" and return
end
end
end
Then, in the form view grab the POSTED values and populate the form:
<%= text_field_tag("name", (#name !=nil ? #name.to_s : nil), :size => 40) %><br><br>

I guess you just want to display the signup page so do:
def submit_request
#hide_logout = "YES" #why don't you use boolean here?
#name = params[:name] || ""
#email = params[:email] || ""
...
## Validate posted values here
## Build error message, if needed
unless error_str.blank?
flash[:warning] = error_str
render :signup and return
end
end
Bonus: I corrected some code syntax to make it lighter.

Related

confirmation token Automatically generated NIL

I have an issue with an automatically generated token. In a model, I generate the token automatically using:
class User < ApplicationRecord
before_create :generate_confirm_token
def generate_confirm_token
self.confirm_token = generate_token
end
def generate_token
loop do
token = SecureRandom.hex(10)
break token unless User.where(confirm_token: token).exists?
end
end
After creating of user, the token is generated correctly, but the issue is in a controller:
class Companies::StudentsController < ApplicationController
def create
#company = Company.find(params[:company_id])
#student = #company.students.create(student_params)
raise #student.inspect
if #student.save
StudentMailer.with(student: #student).welcome_email.deliver_now
redirect_to company_students_path
else
render :new
end
end
student contains confirm_token BUT in params the confirm token is empty.
I need the token in params because in the mailer I use Find_by(params[:confirm_token]).
Here is how I use a confirm_token in my view. I assume I need the confirm_token in params so I have to have it in a view also:
<%= f.hidden_field :confirm_token %>
The process which is described above is OK.
The issue was in mailer.
student should be in mailer created like this:
#student = params[:student]
but I did it like this:
#student = Student.find_by(confirm_token: :confirm_token)
Which is not correct according to the mailer documentation:
Any key value pair passed to with just becomes the params for the
mailer action. So with(user: #user, account: #user.account) makes
params[:user] and params[:account] available in the mailer action.
Just like controllers have params.

Rails - send hidden parameter to new form

I want to build feedback form to send it body to some e-mail box. Each form belongs to many pages (but page has only one form), has one title and body and can have one e-mail. Now it works like this:
app/views/shared/_custom_form.html.slim
= form_for #feedback, method: :post, url: feedback_path, remote: true do |f|
fieldset.b-form-field style='display:none'
= f.text_field :email, value: #feedback.email
fieldset.b-form-field
= f.label :title, "Title"
= f.text_field :title, value: #feedback.title
fieldset.b-form-field
= f.text_field :body, value: #feedback.body
fieldset.b-form-field
= f.submit "Send"
app/controllers/pages_controller.rb
class PagesController < FrontendController
def show
#feedback = Feedback.new
end
end
app/controllers/feedback_controller.rb
class FeedbackController < FrontendController
def new
#feedback = Feedback.new
end
def create
#feedback = Feedback.new(feedback_params)
respond_to do |format|
if #feedback.valid?
eml_sett =
if feedback_params[:email]
feedback_params[:email]
else
setting_value(:main_email)
end
if eml_sett.present?
FeedbackMailer.feedback_message(#feedback, eml_sett).deliver
end
end
end
end
def feedback_params
params.require(:feedback).permit(:title, :body, :email)
end
So, #feedback is created in my view, and then it goes from page controller to feedback_controller where it will be sended to form owner or to default email address. It work perfectly (i know, it is better to use hidden_field, but it is raw project), but i want to totally hide e-mail address from html code. So, i tried in my view:
= form_for #feedback(email: 'test#test.com')...
= form_for #feedback...
- #feedback.email = 'test#test.com'
= form_for Feedback.new(email: 'test#test.com')
But nothing helped. If I run '- puts #feedback.email' in my view, it returns email value, but in feedback controller 'puts feedback_params[:email]' returns nothing and form is sended to default email box. What am I doing wrong? Where is the mistake? Using Rails 4.1.8/Ruby 2.2.0.
I am guessing that your pages#show action displays a generic page with a 'sign up for newsletter' type of form. However, since you want to capture more data, right now you want to direct the user flow to a feedback page, which is served at feedback#new.
If so, what you can do is make the pages#show form submit a GET request on feedback#new, something like this:
= form_for :feedback, url: new_feedback_path, method: :get do |f|
= f.input :email
= f.submit
In your feedback#new action, you can do something like this:
def new
#feedback = Feedback.new(feedback_params)
end
If you use require(:feedback) in your strong params, then this will be a problem if you want to allow access to the new action without first supplying an email, but I'll leave that for you to decide if you want to support that.
What you are saying about using hidden fields, well you can't avoid it since it is user input. So whether or not you enable it as an editable input field, or keep it hidden, the user can supply any value whatsoever on the first page, which is pages#show.
If what you need is to hide the email, then yes you don't need a form field anywhere. This is what you do in your controller:
def create
#feedback = Feedback.new(feedback_params)
FeedbackMailer.feedback_message(#feedback).deliver
end
And in your mailer:
class FeedbackMailer < ApplicationMailer
def feedback_notification(feedback)
#feedback = feedback
mail(to: admin_email, from: #feedback.email, subject: #feedback.title)
end
private
def admin_email
ENV['ADMIN_EMAIL']
end
end
try this:
form_for #feedback... do |f|
= f.hidden_field :email

How do I display a validation error properly if my date format is not correct in Rails?

I’m using Rails 4.2.7. I would like to throw a validation error if a user doesn’t enter their date of birth field in the proper format, so I have
def update
#user = current_user
begin
#user.dob = Date.strptime(params[:user][:dob], '%m/%d/%Y')
rescue ArgumentError => ex
end
if #user.update_attributes(user_params)
and I have this in my view
<%= f.text_field :dob, :value => (f.object.dob.strftime('%m/%d/%Y') if f.object.dob), :size => "20", :class => 'textField', placeholder: 'MM/DD/YYYY' %>
<% if #user.errors[:dob] %><%= #user.errors[:dob] %><% end %>
However, even if someone enters a date like “01-01/1985”, the above doesn’t return a validation error to the view. What do I need to do to get the validation error to be returned properly?
Edit: Per one of the answers given, I tried
#user = current_user
begin
#user.dob = Date.strptime(params[:user][:dob], '%m/%d/%Y')
rescue ArgumentError => ex
puts "Setting error."
#user.errors.add(:dob, 'The birth date is not in the right format.')
end
if #user.update_attributes(user_params)
last_page_visited = session[:last_page_visited]
if !last_page_visited.nil?
session.delete(:last_page_visited)
else
flash[:success] = "Profile updated"
end
redirect_to !last_page_visited.nil? ? last_page_visited : url_for(:controller => 'races', :action => 'index') and return
else
render 'edit'
end
And even though I can see the "rescue" branch called, I'm not directed to my "render 'edit'" block.
Triggering an exception doesn't add anything to the errors list. If you just want to tweak this code slightly, you should be able to call errors.add inside the rescue block. Something like #user.errors.add(:dob, 'some message here').
Keep in mind that this will only validate the date of birth when using this controller method. If you want to validate the date of birth whenever the user is saved, you'll want to explicitly add the validation to the model. You can write your own custom validation class or method, and there are also some gems that add date validation.
Calling update_attributes clears out the errors that you set in the rescue. You should check for errors, and if none, then continue on, something like this:
#user = current_user
begin
#user.dob = Date.strptime(params[:user][:dob], '%m/%d/%Y')
rescue ArgumentError => ex
puts "Setting error."
#user.errors.add(:dob, 'The birth date is not in the right format.')
end
if !#user.errors.any? && #user.update_attributes(user_params)
last_page_visited = session[:last_page_visited]
if !last_page_visited.nil?
session.delete(:last_page_visited)
else
flash[:success] = "Profile updated"
end
redirect_to !last_page_visited.nil? ? last_page_visited : url_for(:controller => 'races', :action => 'index') and return
end
render 'edit'
Since you redirect_to ... and return you can close out the conditional and, if you make it this far, simply render the edit page.
You may also want to add a simple validation to your user model:
validates :dob, presence: true
This will always fail if the dob can't be set for some other, unforseen, reason.
To get the user entered string to populate the field on re-load, you could add an accessor to the user model for :dob_string
attr_accessor :dob_string
def dob_string
dob.to_s
#dob_string || dob.strftime('%m/%d/%Y')
end
def dob_string=(dob_s)
#dob_string = dob_s
date = Date.strptime(dob_s, '%m/%d/%Y')
self.dob = date
rescue ArgumentError
puts "DOB format error"
errors.add(:dob, 'The birth date is not in the correct format')
end
Then change the form to set the :dob_string
<%= form_for #user do |f| %>
<%= f.text_field :dob_string, :value => f.object.dob_string , :size => "20", :class => 'textField', placeholder: 'MM/DD/YYYY' %>
<% if #user.errors[:dob] %><%= #user.errors[:dob] %><% end %>
<%= f.submit %>
<% end %>
And update the controller to set the dob_string:
def update
#user = User.first
begin
##user.dob = Date.strptime(params[:user][:dob], '%m/%d/%Y')
#user.dob_string = user_params[:dob_string]
end
if ! #user.errors.any? && #user.update_attributes(user_params)
redirect_to url_for(:controller => 'users', :action => 'show') and return
end
render 'edit'
end
def user_params
params.require(:user).permit(:name, :dob_string)
end
I would add a validation rule in the model. Like:
validates_format_of :my_date, with: /\A\d{2}\/\d{2}\/\d{4}\z/, message: 'Invalid format'
Try adding validation rule in model.
validate :validate_date
def validate_date
begin
self.dob = Date.parse(self.dob)
rescue
errors.add(:dob, 'Date does not exists. Please insert valid date')
end
end
and in your controller update your code
...
#user.update_attributes(user_params)
if #user.save
....
I think this is a case where Active Model shines. I like to use it to implement form objects without extra dependencies. I don't know the exact details of your situation but below I pasted a small demo that you should be able to adapt to your case.
The biggest benefit is that you don't pollute your controllers or models with methods to support profile updates. They can be extracted into a separate model which simplifies things.
Step 1: Store dob in users
Your users table should have a column dob of type date. For example:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name, null: false
t.date :dob, null: false
end
end
end
Don't put anything fancy in your model:
class User < ActiveRecord::Base
end
Step 2: Add Profile
Put the following in app/models/profile.rb. See comments for explanations.:
class Profile
# This is an ActiveModel model.
include ActiveModel::Model
# Define accessors for fields you want to use in your HTML form.
attr_accessor :dob_string
# Use the validatiors API to define the validators you want.
validates :dob_string, presence: true
validate :dob_format
# We store the format in a constant to keep the code DRY.
DOB_FORMAT = '%m/%d/%Y'
# We store the user this form pertains to and initialize the DOB string
# to the one based on the DOB of the user.
def initialize(user)
# We *require* the user to be persisted to the database.
fail unless user.persisted?
#user = user
#dob_string = user.dob.strftime(DOB_FORMAT)
end
# This method triggers validations and updates the user if validations are
# good.
def update(params)
# First, update the model fields based on the params.
#dob_string = params[:dob_string]
# Second, trigger validations and quit if they fail.
return nil if invalid?
# Third, update the model if validations are good.
#user.update!(dob: dob)
end
# #id and #persisted? are required to make form_for submit the form to
# #update instead of #create.
def id
#user.id
end
def persisted?
true
end
private
# Parse dob_string and store the result in #dob.
def dob
#dob ||= Date.strptime(dob_string, DOB_FORMAT)
end
# This is our custom validator that calls the method above to parse dob_string
# provided via the params to #update.
def dob_format
dob
rescue ArgumentError
errors[:dob] << "is not a valid date of the form mm/dd/yyyy"
end
end
Step 3: Use the form in the controller
Use Profile in ProfilesController:
class ProfilesController < ApplicationController
def edit
# Ensure #profile is set.
profile
end
def update
# Update the profile with data sent via params[:profile].
unless profile.update(params[:profile])
# If the update isn't successful display the edit form again.
render 'edit'
return
end
# If the update is successful redirect anywhere you want (I chose the
# profile form for demonstration purposes).
redirect_to edit_profile_path(profile)
end
private
def profile
#profile ||= Profile.new(user)
end
def user
#user ||= User.find(params[:id])
end
end
Step 4: Render the form with form_for
In app/views/profiles/edit.html.erb use form_for to display the form:
<%= form_for(#form) do |f| %>
<%= f.label :dob_string, 'Date of birth:' %>
<%= f.text_field :dob_string %>
<%= f.submit 'Update' %>
<% end %>
Step 5: Add routing
Keep in mind to add routing to config/routes.rb:
Rails.application.routes.draw do
resources :profiles
end
That's it!

undefined method `permit' for "titleofcoolstuff":String

I am just trying to get two parameters from a view to my controller. I'm using Rails 4.2.x and strong params are killing me.
One param, :query, resolves correctly. However the second param, :location, throws the error in the questions title. I have Googled the issue but everyone's scenario seems to be different and their solutions (relatively) unique.
The view in question is index.html.erb which only contains a simple search form.
<%= form_tag("/searches", action: "create", method: "post") do %>
<div>Job Title</div>
<%= text_field_tag(:query) %>
<div>Location</div>
<%= text_field_tag(:location) %>
<%= submit_tag("Go") %>
<% end %>
The controller in question is searches_controller.rb.
class SearchesController < ApplicationController
def index
binding.pry
end
def show
binding.pry
end
def update
end
def create
#query = search_params["query"].to_s || nil
#location = search_params[:location].to_s || nil
binding.pry
end
def delete
end
private
def search_params
params.require(:query).permit(:location)
end
end
The stack trace points to the search_params method, and shows me that I have the following params in the controller
{
"utf8"=>"✓",
"authenticity_token"=>"DEcTwT/NnSY3S3n25zZGXD+KRZcsRkWj9bmN57AMNivFbMXwHF5Vf/psgzSMkZPBa+OWJgafXYGdW+o5KN3xxg==",
"query"=>"titleofcoolstuff",
"location"=>"milwauke",
"commit"=>"Go"
}
What am I missing?
Strong parameters is for providing a hash of attributes, for example:
<%= form_for #user do |f| %>
## form
<% end %>
This may send parameters like this:
"user" => { "name"=> "Your Name", "age" => "23", "location" => "USA" }
Strong parameters in this case would be instructing rails to process the users hash of attributes and specifically these attributes, like this:
params.require(:user).permit(:name, :age, :location)
In your case, you are passing in individual parameters (not hashes of attributes), so if you want to grab them, you grab them explicitly:
def create
#query = params[:query].to_s || nil
#location = params[:location].to_s || nil
#do something
end
No need for strong parameters to whitelist model attributes here. Hope this helps.
In your case
"query"=>"titleofcoolstuff",
"location"=>"milwauke",
"commit"=>"Go"
since your data are not wrapped with any keys (they are at the root) so you can simply access them using like params[:query].
Whitelisting/Strong params
We need to whitelist params only for mass assignment. like #user.update(user_params) Here, unless the params sent by users in user_params are whitelisted i.e. permitted using .permit method; the update method will throw an exception ActiveModel::ForbiddenAttributes.
In your case since your not updating anything you do not need to create strong params for it.
def create
#query = params["query"].to_s || nil
#location = params[:location].to_s || nil
binding.pry
end
If you are gonna do mass assignment in future you have to whitelist your params
For more info see https://cbabhusal.wordpress.com/2015/10/02/rails-strong-params-whilisting-params-implementation-details/

Rails4: What is the correct way to use form_for for submitting a hidden field

I am trying to pass a hidden field from a form whose value is derived from a text blob that user can edit on the webpage. (I use bootstrap-editable to let the user edit the blurb by clicking on it)
Here is the actual workflow:
User goes on 'Invitations page' where they are are provided with a form to enter friends email and shown a default text that will be used in the email
If the user want they can click on the text and edit it. This will make a post call via javascript to update_email method in Invitation controller
After the text is updated user is redirected back so now the user sees the same page with updated text. This works and user sees the updated text blurb instead of default [1-3] can happen any number of times
When the user submits the form , I expect to get the final version of email that I can save in the db and also trigger an email invitation to the users friend
Problem:
I keep getting default text from form parameters. Any idea what I am doing wrong?
Here is the form (Its haml instead of html)
#new-form
= form_for #invitation, :url=> invitations_path(), :html => {:class => 'form-inline', :role => 'form'} do |f|
.form-group
= f.text_field :email, :type=> 'email', :placeholder=> 'Invite your friends via email', :class=> 'form-control invitation-email'
= f.hidden_field :mail_text, :value => #invitation_email
= f.submit :class => 'btn btn-primary submit-email', :value => 'Send'
Here is the invitation controller:
class InvitationsController < ApplicationController
authorize_resource
before_filter :load_invitations, only: [:new, :index]
before_filter :new_invitation, only: [:new, :index]
before_filter :default_email, only: [:index]
#helper_method :default_email
def create
Invitation.create!(email: params[:invitation][:email], invited_by: current_user.id, state: 'sent', mail_text: params[:invitation][:mail_text], url: {referrer_name: current_user.name}.to_param)
redirect_to :back
end
def update_email
#invitation_email = params[:value]
flash[:updated_invitation_email] = params[:value]
redirect_to :back
end
private
def invitation_params
params.require(:invitation).permit!
end
def load_invitations
#invitations ||= current_user.sent_invitations
end
def new_invitation
#invitation = Invitation.new
end
def default_email
default_text = "default text"
#invitation_email = flash[:updated_invitation_email].blank? ? default_text : flash[:updated_invitation_email]
end
end
Assuming you are using Rails 4 then you need to permit the mail_text parameter:
class InvitationsController < ApplicationController
# ...
private
def invitation_params
params.require(:invitation).permit(:email, :mail_text) #...
end
end
Depending on your settings rails strong parameters will either raise an error or just silently null un-permitted params.
I have to say that your flow is a bit weird and that it may be better if you actually use a
more RESTful pattern:
1. User goes on 'Invitations page' where they are are provided with a form to enter friends email and shown a default text that will be used in the email
Send a AJAX POST request to /invitations (InvitationsController#create) it should return a JSON representation of the UNSENT invitation, store the returned invitation id on the form.
Note that you may need to setup the validations on your Invitation model so that it allows :email and :mail_text to be blank on creation
class Invitation < ActiveRecord::Base
validates :email, allow_blank: true
# ...
# Do full validation only when mail is being sent.
with_options if: :is_being_sent? do |invitation|
invitation.validates :email #...
invitation.validates :mail_text #...
end
# ...
def is_being_sent?
changed.include?("state") && state == 'sent'
end
end
2. User edits text
Send a AJAX PUT or PATCH request to /invitations/:id and update the invitation.
3. User clicks send
Send a POST request to /invitations/:id/send. Update the state attribute and validate.
If valid send invitation. Display a message to user.
class InvitationsController < ApplicationController
# ...
# POST /invitations/:id/send
def send
#invitation = Invitation.find(params[:id])
# Ensure we have latest values from form and trigger a more stringent validation
#invitation.update(params.merge({ state: :sent })
if #invitation.valid?
#mail = Invitation.send!
if #mail.delivered?
# display success response
else
# display error
end
else # record is invalid
# redirect to edit
end
end
# ...
end

Resources