Rails - Validation across two forms - ruby-on-rails

I have what seems to be a non-standard signup process:
[welcome! enter your email and password] => [cool, enter a lot more information] => [registered]
I'm having trouble getting rails to recognize that I want to validate only the email and password on page 1, and all the other stuff on page 2.
When the user hits the site, they see my welcome page:
class WelcomeController < ApplicationController
def index
#user = User.new
end
end
<%= form_for(#user) do %>
<div class="formItem">
<%= label_tag(:email, "Your email address:") %>
<%= text_field(:user, :email) %>
<br clear="all" />
</div>
<div class="formItem">
<%= label_tag(:password, "Select a password:") %>
<%= password_field_tag(:password) %>
<br clear="all" />
</div>
<%= submit_tag("Sign up today - it's free and easy!", :class => "submitForm") %>
<% end %>
=
class UsersController < ApplicationController
def create
#user = User.new(params[:user])
if #user.save
redirect_to(edit_user_path(#user, :noticeHeader => true ), :notice => 'Please take a minute and answer these additional questions.')
else
render :action => "welcome/index"
end
end
=
Once they click Sign Up, they see my next page of form fields.
The problem I'm running into is that I validates_presence_of fields that are on both pages, in the user model. Since these fields aren't on the welcome page, I get a "undefined local variable or method" error upon submitting the welcome page. For example, I validates_presence_of :title because I want title to be required, but it's only listed on page 2, so page 1 doesn't validate properly.
Thoughts on how to handle this?
Thanks.

Have a look into one of these plugins/gems where you can create Wizard based forms.
http://ruby-toolbox.com/categories/rails_wizards.html my favourite is "ActsAsWizard"

Related

POST is going to 'new' and not 'create' Rails

So I am trying to implement the password_reset functionality into my site using bcrypt. An issue I am having is the POST is going to my new action rather to my create action.
My View
<%= form_for password_resets_path, method: 'post' do %>
<div>
<h3>Please enter your email address</h3>
<%= text_field_tag :email, params[:email] %>
</div>
<div>
<%= submit_tag "Reset Password" %>
</div>
My Controller
class PasswordResetsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:email])
user.send_password_reset if user
redirect_to root_url, :notice => 'Email sent with password reset instructions.'
end
end
My Routes
resources :password_resets
And I am getting this error
ActionController::RoutingError (No route matches [POST] "/password_resets/new"):
I looked at different solutions already, and since I do not have a model the #object, would not work for me. Since I am simply just trying to call to an action.
I feel like I am missing something so very simple but for the life of me I have been unable to figure it out. Many thanks in advance to whomever is the one to help me.
Problem: <%= form_for password_resets_path, method: 'post' do %>
form_for needs an object. If you don't want an object, just use the form_tag helper:
<%= form_tag password_resets_path do %>
<%= text_field_tag :email, params[:email], placeholder: "Please enter your email address" %>
<%= submit_tag "Reset Password" %>
<% end %>
This should work for you.

Select_tag login

I would like to have a drop down menu with a list of all the user names in the db. From there, I would like the user to choose his/her name and be able to click login and be taken to their respective page. At this point, a password is not needed. Currently, I have the following:
controller:
def login
#user = User.new
#users = User.all
# #user = User.find_by_id(:id)
# redirect_to user_path(#user)
end
view:
<%= form_for #user, url: '/login', html: {method: 'get'} do |f| %>
<%= f.label "Name" %>
<br/>
<%= select_tag :user, options_for_select(#users) do |users| %>
<%= link_to users.name, users %>
<% end %>
<br/>
<br/>
<%= f.submit 'Login' %>
<% end %>
I cannot seem to link the user to their path and also, i want to show the users name in the drop down menu. Currently, it shows a hexidecimal pointer.
Thank you in advance.
You shouldn't be making a new User object here: you just want to load one out of the database. What you want to do in the controller is just to set current_user to be one of the existing users, right?
Also you've got the form submitting back to the action which loads the form in, which seems weird. I would make it submit to a new action, like "set_current_user" which is a POST action.
in your login template:
<%= form_tag '/set_current_user' do %>
<%= f.label "Name" %>
<br/>
<%= select_tag "user_id", options_for_select(#users.collect{|user| [user.name, user.id] } %>
<br/>
<br/>
<%= submit_tag 'Login' %>
<% end %>
in the controller (you'll need to amend routes.rb to make the '/set_current_user' go to this action) you then need to set something which will keep the user logged in. The traditional way to do this is via session[:user_id], and to have a method current_user which uses this.
def set_current_user
session[:user_id] = params[:user_id]
redirect_to "/" and return
end
Your initial approach is reminiscent of how this sort of thing is normally handled, wherein you do have a form_for, but it's for a UserSession object rather than a User object.

Mailboxer with rails 4 (undefined local variable or method `root_url')

Update
The error was that rails cant find the root_url
Visit <%= link_to root_url, root_url %> and go to your inbox for more info.
for a quick fix and I dont need to sent the user to the root_url just a notification for the user to go to the app. I change the code to this: on the mailbox email views
Visit **messages** and go to your inbox for more info.
Question
I got devise set with my rails 4 app. Im following the example mailboxer-app when I sent the message I get a error:
`error undefined local variable or method `root_url' for #<#<Class:0x007ffe0b881678>:0x007ffe0b068298>`
Stuff I have fix to get it working
Got the form sending message to user with email
user can sent and reply
mark as delete
view inbox,sentbox and trash
this are my steps
install gem -v 0.12.1
rails g mailboxer:install
run migration
use the code from the example app(controller,view,routes)
add to my user.rb acts_as_messageable and
Conversations Controller
before_filter :authenticate_user!
helper_method :mailbox, :conversation
def index
#inbox ||= current_user.mailbox.inbox.paginate(:page => params[:inbox], :per_page => 5 )
#sentbox ||= current_user.mailbox.sentbox.paginate(:page => params[:sentbox], :per_page => 5 )
#trash ||= current_user.mailbox.trash.paginate(:page => params[:trash], :per_page => 5 )
end
def create
recipient_emails = conversation_params(:recipients).split(',')
recipients = User.where(email: recipient_emails).all
conversation = current_user.
send_message(recipients, *conversation_params(:body, :subject)).conversation
redirect_to :conversations
end
form
<%= bootstrap_form_for :conversation, url: :conversations do |f| %>
<%= f.text_field :recipients%>
<%= f.text_field :subject%>
<%= f.text_field :body %>
<div class="form-actions">
<%= f.primary "send" %>
<%= submit_tag 'Cancel', type: :reset, class: 'btn btn-danger' %>
</div>
<% end %>
View
<% #inbox.each do |conversation| %>
<%= conversation.originator.username%>
<%= link_to raw(truncate(strip_tags(conversation.subject), :length => 15)), conversation_path(conversation) %>
<% end %>
Ok got the fix to this problem.
what happen is that the mailboxer mailer was looking for root_url. Rails 4.1 wont generate the views for that just copy the files from the source code and works greate.
and just change that part of the code here.
view/mailboxer/all of this files
message_mailer
notification_mailer
change this
Visit <%= link_to root_url, root_url %> and go to your inbox for more info.
to this
Visit **messages** and go to your inbox for more info.
Thanx to this guy supremebeing7. on the mailboxer issue page

How to convert a working rails contact form, into a bootstrap modal?

I currently have a working rails contact form (taken from another SO answer), available on /contact, that uses a controller, model and mailer (Mandrill). It responds to new and create routes in the controller, and still uses activemodel features. The form does not use ajax.
I now need to try to make my contact form work on the home page, as opposed to the /contact page via a modal pop-up.
I have read a number of SO queries, about modals, but all seem to be connected with getting a modal to do something specific - and not so much on creating a modal form to mimic a normal form.
To start I added a working modal, to the homepage.
When I then try to add the form into the homepage model, I run into method errors as the form is part of the home_controller. After copying my new and create controller actions into my home controller, I realized, that the form is just hidden, and is still being run when the page loads (by the index action).
Adding #message = Message.new into the index action does not seem to help - and wouldn't I want this on the modal load ?
Here are the main moving parts of my working form from the contact page, and the working modal box from the homepage - how can I merge the two and retain the functionality I have ?
here is the contact controller:
#/app/controllers/contact_controller.rb
class ContactController < ApplicationController
def new
#message = Message.new
end
def create
#message = Message.new(params[:message])
if #message.valid?
NotificationsMailer.new_message(#message).deliver
redirect_to(root_path, :notice => "Message was successfully sent.")
else
flash.now.alert = "Please fill all fields."
render :new
end
end
end
the message model
#app/models/message.rb
class Message
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :subject, :body
validates :name, :email, :subject, :body, :presence => true
validates :email, :format => { :with => %r{.+#.+\..+} }, :allow_blank => true
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
And the view:
<%= form_for #message, :url => contact_path do |form| %>
<fieldset class="fields">
<div class="field">
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<div class="field">
<%= form.label :email %>
<%= form.text_field :email %>
</div>
<div class="field">
<%= form.label :subject %>
<%= form.text_field :subject %>
</div>
<div class="field">
<%= form.label :body %>
<%= form.text_area :body %>
</div>
</fieldset>
<fieldset class="actions">
<%= form.submit "Send" %>
</fieldset>
<% end %>
Finally within my home page I have this for the modal (not containing the form)
<div class="modal fade" id="modal_contact_form">
<div class="modal-header">
<a class="close" data-dismiss="modal">×</a>
<h3>Contact Form</h3>
</div>
<div class="modal-body">
<p>Test Content for Modal</p>
</div>
<div class="modal-footer">
</div>
</div>
Which is called by this <li>contact</li>
My attempts have been many and varied to get this working up until this point - the main issue I need to get my head around (I think) is that the form is loaded by the index action on the home_controller - Ideally I guess I still want to use the logic in my contact_controller ?
Any advice and suggestions appreciated - at this point I am wondering if there is an easy way just to move a form into a modal or not!
Cheers,
Mizpah
note: In response to queries I have about ajax, I have only avoided ajax due to perceived difficulty and lack of progress with trying it on rails 3.2 - I would be delighted to use it if it would actually be easier to turn this into an ajaxified form ?
It seems that I was not far away after all!
After a pointer from #rubyonrails channel, the answer was:
1) To copy the form as is, into the modal. (note that different styling is probably needed - but this answer gives the functionality needed).
2) To add #message = Message.new into the index action on my home_controller.rb
I had tried something very similar to this, but must of introduced another error, and thus bypassed the correct solution.

Rails model error object passing between controllers

I have looked around S.O. and other resources on the web and I am finding other questions that are similar to this one but not exactly like mine. I'm sure that there is a response out there that works but I am asking this question for clarification as much as I am asking to find the solution. Sorry if this sounds redundant to other questions but I have yet to find something that definitively answers my question.
I have an app where businesses have a listing page and each of those pages has a section where users can submit comments about that business.
Each business page is handled via a 'merchants_controller' using the 'show' action:
def show
#merchant = Merchant.merchant_link_test(params[:id])
#title = #merchant.name
# used inside maps javascript
#map_height = '290px'
#map_width = '350px'
#user_reviews = Review.where(:merchant_id => #merchant.id).order("created_at....
#user_reviews_count = Review.where(:merchant_id => #merchant.id).count
#user_or_merchant = 'merchant'
#review = Review.new
#average_rating = Review.where(:merchant_id => #merchant.id).average(:rating)
#rounded = round_rating(#average_rating)
#yelp_query = #merchant.yelp_string
#yelp_count = yelp(#yelp_query, 'avg_rating')
#num_of_yelp = yelp(#yelp_query, 'review_count')
end
I have a partial in the 'show' layout that displays a form for submitting comments about that business. The form input is handled via a 'reviews_controller' using the 'create' action:
def create
user = User.find_by_id(current_user.id)
id = user.id
#merchant = Merchant.find(params[:review][:merchant_id])
params[:review].store(:user_id, id)
#review = Review.new(params[:review])
if #review.save
flash[:success] = "Your review was submitted!"
redirect_to #merchant
else
flash[:error] = "There was an error submitting your review"
render :template => 'merchants/show', :locals => { :id => #merchant.id, }
end
end
The form uses the 'form_for' helper and looks like this:
<% if signed_in? %>
<div class = "comment_form">
<%= form_for #review do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<div class = "field">
<div id = "required_rating"> * </div><%= f.label :rating %>
</br>
<%= f.select :rating, [['', ''],['1 star (terrible)', 1],
['2 stars', 2],['3 stars', 3],['4 stars', 4],['5 stars
(bomb.com)', 5]] %>
</br>
</div>
<div class = "field">
<div id = "required_comment"> * </div><%= f.label :comment,
"Comment (limit 1000 characters)" %>
<br/>
<%= f.text_area :comment, :cols => 80, :rows => 6 %>
</div>
<div id = "denotes_text"> * Denotes required field </div>
<div class = "actions">
<%= f.submit "Submit Review" %>
<% end %>
<% else %>
<div class = "sign_in_to_comment">
<%= link_to "Sign in to submit a review of this merchant",
signin_path %>
</div>
</div>
<% end %>
I have a two tables and two models, 'merchants' and 'reviews' that handle the data for the respective resources. My 'routes' file looks like this:
resources :merchants, :only => [ :new, :create, :show ]
match '/merchants/:name', :to => 'merchants#show'
match '/merchants/:id/all', :to => 'comments#all_comments_merchant', :as => :all
match '/merchants/:id/map', :to => 'merchants#map', :as => :map
resources :reviews, :only => [ :create ]
My issue is that when a user submits a comment, if an error is generated via my validations in the model, the error object is passed back to the create action in the 'reviews_controller.' I then want to display that error message on the 'merchant_show_page' but need to pass the error object from the 'reviews_controller' back to the 'merchants_controller.'
From what I have read, it seems like the error object has some 'automagic' functionality when being passed between actions within the same controller. Specifically, it seems like the model knows the action that submitted the data and returns the error object using 'render' without having to initialize any instance variables on the page.
A few of the solutions that I have seen regarding similar questions:
1. Move the form submission action into the 'merchants_controller' and lose the
'reviews_controller' altogether.
2. Use 'redirect_to' instead of 'render' and pass the error object as a variable in the
redirect call.
It seems like there has to be an easy, 'rails way' to do this. Having separate controllers seems to make logical sense and it allows me to keep merchants and reviews as separate resources which, from what I have read, is the proper rails convention I should be shooting for. Is this just an issue where there is not a proper 'rails way' to do it? If so, what other rails convention should I be defaulting to?
Any help that can be given would be much appreciated.
Thanks for your help,
noob :)

Resources