Rails and Validation - ruby-on-rails-3

I'm still (for those that have helped before) having issues validating data.
The Scenario
I have two models - user and accommodation with each user having one accommodation (has_one). I am able to access the logged in user using current_user.
The Problem
When I validate users upon registration everything works fine with validation error messages displayed accordingly for each validation rule. However, I am now trying to validate accommodations when they are entered and I get a Rails error page:
Unknown Action
The action 'show' could not be found for AccommodationsController
but interestingly the url has changed to /accommodations//edit which appears to be missing an id for the accommodations id (I do want it to divert to edit if everything is ok).
I don't think it's the validation rules themselves but more how I am handling redirection (which is confusing me to be honest!). The data saves correctly and redirects correctly if it passes the validation rules but not sure how to handle a "non-save" gracefully.
The Code
Accommodation Model
class Accommodation < ActiveRecord::Base
belongs_to :user
#validation rules
validates :user_id, presence: true
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
end
/accommodations/new.html.erb
...
<%= form_for :accommodation, url: accommodations_path do |f| %>
<% if #accommodation.errors.any? %>
<% #accommodation.errors.full_messages.each do |msg| %>
<p class="error"><%= msg %></p>
<% end %>
<% end %>
...
AccommodationsController (thanks to #depa for help with this)
...
def index
if current_user.accommodation.present?
redirect_to edit_accommodation_path(current_user.accommodation)
else
redirect_to new_accommodation_path(current_user.accommodation)
end
end
def new
#accommodation = Accommodation.new
end
def create
current_user.create_accommodation(accommodation_params)
flash[:success] = "Accommodation details added"
redirect_to edit_accommodation_path(current_user.accommodation)
end
def edit
end
def update
if #accommodation.update(accommodation_params)
flash[:success] = "Accommodation details updated successfully"
redirect_to edit_accommodation_path(#accommodation)
else
flash[:error] = "Accommodation details could not be updated"
render 'edit'
end
end
private
def accommodation_params
params.require(:accommodation).permit(:name, :email)
end
...

To handle your failed validations gracefully:
def create
#accommodation = current_user.build_accommodation(accommodation_params)
if #accommodation.save
flash[:success] = "Accommodation details added"
redirect_to edit_accommodation_path(current_user.accommodation)
else
flash.now.notice = "Error creating accommodation"
render "new"
end
end

Is accommodation_params actually set up on entry to create? I'd expect you to need to use params or params[:accommodation].
If it isn't, the create_accommodation call is going to fail, which will mean current_user.accommodation will be nil, which may well produce your error.

Related

Alternate update form in Rails

I'm trying to make a form that you can only access once before a certain boolean is set to true, and along with it you input some other info, the problem is that I don't know how to set it up properly, so far the form loads no problem, but it doesn't update the information of the user.
my main problem is that I don't know how to set up the routes and the form, since most of the time I just use resources: to make them automatically
here is my form :
<%= form_for #user, url: organizer_user_path, method: :put do |f| %>
.
.
.
<%end%>
my routes, which I don't think put is correct at all
get 'organizer/user', to: "users#organizer_form"
put 'organizer/user', to: "users#organizer_registration"
and my controller
def organizer_form
#user = User.find(current_user[:id])
end
def organizer_registration
#user = User.find(current_user[:id])
if #user.update_attributes(user_params)
redirect_to #user
else
render 'organizer_form'
end
end
I'm sure the problem lies in how I define my routes and how they are called onto the form, any help?
here are the params that are submitted:
--- !ruby/object:ActionController::Parameters
parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
utf8: "✓"
_method: put
authenticity_token: w9zTUQLK+4kZtbvRCfcYfZgn8ma4xSRdbTMy+fktpoHlGADxHSB6u9QBh3K3zv72V/Ys2b3Q1MiKC0Gr+OqNJw==
user: !ruby/object:ActionController::Parameters
parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
organizer: 'true'
contact_name: Name
last_name: Last name
contact_email: mail1#mail.com
cellphone: '1234567'
company: ''
office_phone: ''
website: ''
permitted: false
commit: continuar
controller: users
action: organizer_registration
permitted: false
This is my user_params
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation, :organizer, :contact_name, :last_name, :contact_email, :cellphone, :company, :office_phone, :website)
end
Finally found the answer! I had
attr_accessor :organizer, :contact_name, :last_name, :contact_email, :cellphone, :company, :office_phone, :website
which somehow stopped the update from happening, so removing that made the updates save to the database.

Clearance failure when forbidden password reset

I’m using clearance and love it, but I'm having trouble resetting passwords. I type in my email to reset the password, which works, but then when I try to navigate to the edit password page using the reset token, I get the failure when forbidden flash error “Please double check the URL or try submitting the form again” and it redirects me back. I get the same error in my tests.
I think this has something to do with my before_action statements, but I just don’t know how to fix them. I have researched questions like this to no avail.
I'm sure it's a stupid question, but I'm new so I really appreciate any help. Please let me know if this isn't enough code.
class UsersController < Clearance::UsersController
before_action :require_login, only: [:create] # does this need to be in both user controllers?
...
def user_params
params.require(:user)
end
end
And here is the clearance controller.
class Clearance::UsersController < ApplicationController
before_action :require_login, only: [:create]
require 'will_paginate/array'
def new
#user = user_from_params
render template: 'users/new'
end
def create
#user = user_from_params
#user.regenerate_password
if #user.save
sign_in #user unless current_user
UserMailer.welcome_email(#user).deliver!
redirect_to users_path
else
render template: 'users/new'
end
end
def edit
#user = User.friendly.find(params[:id])
end
def update
#user = User.friendly.find(params[:id])
if #user.update(permit_params)
redirect_to #user
flash[:success] = "This profile has been updated."
else
render 'edit'
end
end
private
def avoid_sign_in
redirect_to Clearance.configuration.redirect_url
end
def url_after_create(user)
dashboards_path(user)
end
def user_from_params
user_params = params[:user] || Hash.new
is_public = check_public_params(user_params)
first_name = user_params.delete(:first_name)
last_name = user_params.delete(:last_name)
email = user_params.delete(:email)
password = user_params.delete(:password)
parish = user_params.delete(:parish)
division = user_params.delete(:division)
admin = user_params.delete(:admin)
Clearance.configuration.user_model.new(user_params).tap do |user|
user.first_name = first_name
user.last_name = last_name
user.password = password
user.email = email
user.is_public = is_public
user.parish_id = parish.to_i
user.division = division
user.admin = admin
end
end
def permit_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :is_public, :parish_id, :division, :admin)
end
end
EDIT: relevant portions of routes.rb
Rails.application.routes.draw do
resources :passwords, controller: "clearance/passwords", only: [:create, :new]
resource :session, controller: "clearance/sessions", only: [:create]
resources :users, controller: "clearance/users", only: [:create] do
resource :password,
controller: "clearance/passwords",
only: [:create, :edit, :update]
end
get "/sign_in" => "clearance/sessions#new", as: "sign_in"
delete "/sign_out" => "clearance/sessions#destroy", as: "sign_out"
get "/sign_up" => "clearance/users#new", as: "sign_up"
constraints Clearance::Constraints::SignedOut.new do
root to: 'high_voltage/pages#show', id: 'landing'
end
constraints Clearance::Constraints::SignedIn.new do
# root to: 'dashboards#index', as: :signed_in_root
root to: 'high_voltage/pages#show', id: 'parish_dashboard', as: :signed_in_root
end
# constraints Clearance::Constraints::SignedIn.new { |user| user.admin? } do
# root to: 'teams#index', as: :admin_root
# end
resources :users do
collection { post :import }
end
It turns out there was a conflict between the way I was finding the user instance in the password reset link. Clearance finds users simply by using #user, but since I'm using FriendlyId I needed to change that to #user.id.
So instead of...
<%= link_to 'Change My Password', edit_user_password_url(#user, token: #user.confirmation_token.html_safe) %>
I did
<%= link_to 'Change My Password', edit_user_password_url(#user.id, token: #user.confirmation_token.html_safe) %>
Thanks, Thoughbot, for this great gem!

Allow acts_as_taggable_on to work with hash tags

I am using the acts_as_taggable_on gem: https://github.com/mbleigh/acts-as-taggable-on
This gem accepts user input for tags as comma separated i.e., Clever, Cool, Joyful
I have a partial: shared/_micropost_form that asks for tags:
<%= form_for(#micropost) do |f| %>
<%= f.text_field :tag_list, placeholder: "Tags", id: "genre_tag_field" %>
<% end %>
However sometimes users enter the tags with hashtags i.e., #Happy #Drunk or #Stupid, #Drunk
How can I make it so that before a Micropost is created, it checks the entered :tag_list if each word starts with a # and if it does then remove the # and add a , (unless there is a , already) to the end of each word. So When the user enters #Happy #Drunk then the tags automatically save as Happy, Drunk
Micropost Model:
acts_as_ordered_taggable
Micropost Controller:
def create
#user = User.find(params[:user_id])
#micropost = #user.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Posted!"
redirect_to :back
else
flash[:notice] = "Error!"
redirect_to :back
end
end
Thanks, appreciate any help. Is this possible without tossing the gem?
Though not verified in practice, here is a solution.
According to README, you can set the following in initializer to remove special characters in tag name:
ActsAsTaggableOn.force_parameterize = true
It seems acts_as_taggable_on doesn't have an installation command and a default initializer. You can put above setting in any file in /initializers/, and preferred a new file dedicated to this gem.
By setting that, all of your tag name inputs will be processed after saving like
my_string.parameterize
Test
"radical)(cc".parameterize
#=> "radical-cc"
"#Happy".parameterize
#=> "happy"

Using flash to notify user of previous registration

I'm trying to use my Users controller to notify the user when their email has already been used in a registration, but even when the email already exists, I still get the error "Plase validate your input and try again," rather than "You've already registered! Thanks for being enthusiastic!" Is using the controller not the create way of achieving this behavior?
In the rails console (assuming "foo#bar.com" is in the database"), when I use user = User.new(name:"Example", email:"foo#bar.com") then User.find_by_email(user.email) it does return the proper User entry, so I'm not sure if I'm on the right track and just executing it incorrectly or what. Any ideas?
users_controller.rb:
class UsersController < ApplicationController
def new
#user = User.new(params[:user])
end
def create
#user = User.new(params[:user])
if #user.save
flash[:success] = "Thanks for supporting cofind! We'll be in touch!"
redirect_to root_path
UserMailer.welcome_email(#user).deliver
else
if #user.email == User.find_by_email(#user.email)
flash[:error] = "You've already registered! Thanks for being enthusiastic!"
redirect_to root_path
else
flash[:error] = "Plase validate your input and try again."
redirect_to signup_path
end
end
end
end
user.rb:
class User < ActiveRecord::Base
attr_accessible :email, :name
before_save { |user| user.email = email.downcase }
validates :name, presence: true
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
end
this line
if #user.email == User.find_by_email(#user.email)
checks the user's email (a string) against a user record (an ActiveRecord object) which will always be false. You should change that to
if User.where(email: #user.email).exists?

rails controller model view advice for ContacUs page

I have a simple static website written in rails 3.
The site has one controller called pages and each static page is served as view. Such as pages/home, pages/about, pages/prices, etc. This all works great.
I've now run into a problem where I need to add a simple contactus feature but I'm struggling to get my head round the model/controller/views for this.
I already have a pages controller with a contactus view, that view has details addresses etc. Now I somehow need to get a message model into the contactus view so I can populate the model attirbutes and send the email.
Can I / Should I just create a new message model from within the Pages Controller as in ,
class PagesController < ApplicationController
def contact
def new
#message = Message.new
end
def create
#message = Message.new(params[:message])
if #message.valid?
# TO DO send message here using OS mail program.
redirect_to root_url, notice: "Message sent! Thank you for contacting us."
else
render "new"
end
end
end
def about
end
def products
end
def portfolio
end
def services
end
end
Or should I take out the contactus view from the pages controller and make new controller called messages ?
Thanks.
I would have a separate controller called contact for example with new and create actions
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
Then a separate model to handle your messages
class Message
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :subject, :body, :file
validates :name, :email, :subject, :body, :presence => true
validates :email, :format => { :with => %r{.+#.+\..+} }, :allow_blank => true
end
your attributes can be anything you like, obviously this is just an example of what you can do