Chapter 7 Errors, Ruby on Rails tutorial - ruby-on-rails

No matter what I write there I will always get these errors, I have read chapter twice, I don't understand what I am doing wrong.
Here's my code at GitHub
Here's a screenshot of errors.
user_controller.rb
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(params[user_params])
if #user.save
redirect_to user_url(#user)
#Handle a successfull save
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end
Model
class User < ActiveRecord::Base
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
end
Form
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#user) do |f| %>a
<%= render '/shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div>

Change line
#user = User.new(params[user_params])
to
#user = User.new(user_params)
in UsersController.
Everything else looks fine.

Related

Data not persisted between steps in wizard

I have followed Nicolas Blanco's tutorial to make a "goal" wizard for my app.
There are two steps in the wizard. The first consisting of the form fields "name", "description" and "plan", the second has "deadline", which is a datetimepicker, "reporting frequency" and "days missed tolerance".
It seems to work when I click continue in the first step, but on clicking finish in the second step, the object #goal_wizard doesn't seem to include the parameters from the first step.
My goal.rb:
module Wizard
module Goal
STEPS = %w(step1 step2).freeze
class Base
include ActiveModel::Model
attr_accessor :goal
delegate *::Goal.attribute_names.map { |attr| [attr, "#{attr}="] }.flatten, to: :goal
def initialize(goal_attributes)
#goal = ::Goal.new(goal)
end
end
class Step1 < Base
validates :name, presence: true, length: { maximum: 50 }
validates :description, presence: true, length: { maximum: 300 }
validates :plan, presence: true, length: { maximum: 1000 }
end
class Step2 < Step1
validates :reporting_frequency, presence: true,
numericality: { greater_than_or_equal_to: 0 }
validates :days_missed_tolerance, presence: true,
numericality: { greater_than_or_equal_to: 0}
validates :deadline, presence: true
end
end
end
wizards_controller.rb:
class WizardsController < ApplicationController
before_action :load_goal_wizard, except: :validate_step
def validate_step
current_step = params[:current_step]
#goal_wizard = wizard_goal_for_step(current_step)
#goal_wizard.goal.attributes = goal_wizard_params
session[:goal_attributes] = #goal_wizard.goal.attributes
if #goal_wizard.valid?
next_step = wizard_goal_next_step(current_step)
create and return unless next_step
redirect_to action: next_step
else
render current_step
end
end
def create
# #user = current_user
# #goal = #user.goals.new(#goal_wizard.goal)
if #goal_wizard.goal.save
session[:goal_attributes] = nil
redirect_to root_path, notice: 'Goal succesfully created!'
else
redirect_to({ action: Wizard::Goal::STEPS.first }, alert: 'There were a problem creating the goal.')
end
end
private
def load_goal_wizard
#goal_wizard = wizard_goal_for_step(action_name)
end
def wizard_goal_next_step(step)
Wizard::Goal::STEPS[Wizard::Goal::STEPS.index(step) + 1]
end
def wizard_goal_for_step(step)
raise InvalidStep unless step.in?(Wizard::Goal::STEPS)
"Wizard::Goal::#{step.camelize}".constantize.new(session[:goal_attributes])
end
def goal_wizard_params
params.require(:goal_wizard).permit(:name, :description, :plan, :deadline, :reporting_frequency, :days_missed_tolerance)
end
class InvalidStep < StandardError; end
end
step1.html.erb:
<ol class="breadcrumb">
<li class='active'>Step 1</li>
<li>Step 2</li>
</ol>
<%= form_for #goal_wizard, as: :goal_wizard, url: validate_step_wizard_path do |f| %>
<%= render "error_messages" %>
<%= hidden_field_tag :current_step, 'step1' %>
<%= f.label :name %>
<%= f.text_field :name, class: "form_control" %>
<%= f.label :description %>
<%= f.text_field :description, class: "form_control" %>
<%= f.label :plan %>
<%= f.text_field :plan, class: "form_control" %>
<%= f.submit 'Continue', class: "btn btn-primary" %>
<% end %>
step2.html.erb:
<ol class="breadcrumb">
<li><%= link_to "Step 1", step1_wizard_path %></li>
<li class="active">Step 2</li>
</ol>
<%= form_for #goal_wizard, as: :goal_wizard, url: validate_step_wizard_path do |f| %>
<%= render "error_messages" %>
<%= hidden_field_tag :current_step, 'step2' %>
<%= f.label :deadline %>
<div class='input-group date' id='datetimepicker1'>
<%= f.text_field :deadline, class: "form-control" %>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
<%= f.label "How often do I want to report? (1 = every day)" %>
<%= f.number_field :reporting_frequency, class: "form_control" %>
<%= f.label "How many times can I miss my report?" %>
<%= f.number_field :days_missed_tolerance, class: "form_control" %>
<script type="text/javascript">
$(function () {
$('#datetimepicker1').datetimepicker({
minDate:new Date()
});
});
</script>
<%= f.submit "Finish", class: "btn btn-primary" %>
<% end %>
Over here you're passing the goal_attributes to initialize, but you're never using them.
def initialize(goal_attributes)
#goal = ::Goal.new(goal)
end
If you look at Nicolas Blanco's code he doesn't make that mistake.

Showing errors in the modal rails

I'm very frustrated because I've been working on it for days and I haven't found a solution yet.
I have a Visit.rb model and, beyond the common validations, I have two more validations through two methods of mine (visit's start date cannot be prior to end one and there's no possibility to have two visits having the same start date, end date and visitor_id).
I need to show these errors set by me in the modal I'm giving to register a visit, but until now I've never had success.
In fact every time this error occurs: ArgumentError in VisitsController#create - First argument in form cannot contain nil or be empty
Here what I've done until now is...
Visit.rb
class Visit < ActiveRecord::Base
belongs_to :employee
belongs_to :visitor
default_scope -> { order(:created_at) }
validates :from, presence: true, uniqueness: { scope: [:to, :visitor_id] }
validates :to, presence: true
validates :visitor_id, presence: true
validates :employee_id, presence: true
validate :valid_date_range_required
validate :visit_uniqueness
def valid_date_range_required
if (from && to) && (to < from)
errors[:base] << "Visit's end date cannot be prior to the start one."
end
end
def visit_uniqueness
if Visit.find_by(from: :from, to: :to, visitor_id: :visitor_id)
errors[:base] << "A visit with the same start date, end date and visitor already exists."
end
end
end
visits_controller.rb
class VisitsController < ApplicationController
before_action :logged_in_employee, only: [:create, :destroy]
before_action :correct_employee, only: [:destroy, :update ]
def create
#visit = current_employee.visits.build(visit_params)
if #visit.save
flash[:success] = "Visit added"
redirect_to employee_path(session[:employee_id], :act => 'guestsVisits')
else
#visits = current_employee.visits.all
#employee = current_employee
render 'employees/guestsVisits'
end
end
private
def visit_params
params.require(:visit).permit(:from, :to, :visitor_id, :employee_id)
end
def correct_employee
#visit = current_employee.visits.find_by(id: params[:id])
redirect_to root_url if #visit.nil?
end
end
guestsVisits.rb (my view)
<div class="jumbotron3 text-center">
<div class="row">
<h1>Guests Visits</h1>
<hr>
<%=render :partial =>"layouts/sidebar"%>
<div class="panel3">
<div class="panel-body">
<!-- Modal for Visit -->
<% if logged_in? %>
<%=render :partial =>"shared/error_messages"%>
<a class="btn icon-btn btn-success pos" data-toggle="modal" data-target="#visitModal">
<span class="glyphicon btn-glyphicon glyphicon-plus img-circle text-success"></span>
Add a visit
</a>
<div id="visitModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Add Visit</h4>
</div>
<div class="modal-body">
<%= form_for(#visit) do |f| %>
<%= f.label :start_date %>
<%= f.date_field :from, class: 'form-control',:value => (f.object.from.strftime('%m/%d/%Y') if f.object.from) %>
<%= errors_for #visit, :from %><%if #visit.errors.any?%><br><%end%>
<br>
<%= f.label :end_date %>
<%= f.date_field :to, class: 'form-control',:value => (f.object.to.strftime('%m/%d/%Y') if f.object.to) %>
<%= errors_for #visit, :to %><%if #visit.errors.any?%><br><%end%>
<br>
<%= f.label :Visitor %>
<%= f.collection_select :visitor_id, Visitor.all, :id, :full_name, { :class=> "form-control", :include_blank => ''}%>
<%= errors_for #visit, :visitor_id %><%if #visit.errors.any?%><br><%end%>
<br>
<%= f.submit "Add visit", class: "btn btn-primary btn-color" %>
<% end %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-info" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<% end %>
</div>
</div>
</div>
</div>
I tried to use two different versions of error_messages.rb
error_messages.rb (1)
<% if #visit && #visit.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
×
The form contains <%= pluralize(#visit.errors.count, "error") %>.Open the modal for more details
</div>
</div>
<% end %>
error_messages.rb (2)
<% if #visit.errors.any? %>
<ul class="errors">
<% #visit.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
I hope someone can help me because I don't understand how to figure it out.
Thank you a lot.
EDIT:
class Employee < ActiveRecord::Base
before_save { self.email = email.downcase }
before_create :confirmation_token
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
has_many :macaddresss, dependent: :destroy
has_many :visits, dependent: :destroy
has_many :visitors, dependent: :destroy
#Validations
validates :name, presence: true , length: { maximum:20 , minimum: 2 }
validates :lastname, presence: true, length: { maximum:20 , minimum: 2 }
validates :gender, presence: true
validates :dateofbirth, presence: true
validates :birth_country, presence: true
validates :birth_place, presence: true, length: { maximum:60}
validates :contry_of_residence, presence: true
validates :city_of_residence, presence: true, length: { maximum:60 }
validates :address, presence: true, length: { maximum:60 , minimum: 7 }
validates :email, presence: true, length: { maximum:243 } ,format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
validates :password, length: { minimum: 6 }
validates :terms_of_service, acceptance: { message: 'accept terms and conditions' }
[...]
has_secure_password
def Employee.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
end
session_helper.rb
def current_employee
#current_employee ||= Employee.find_by(id: session[:employee_id])
end
EDIT2:
employees_controller.rb
class EmployeesController < ApplicationController
def show
if logged_in?
#employee = Employee.find(params[:id])
#indirizzimac = current_employee.indirizzimacs.new
#visitor = current_employee.visitors.new
#visit = current_employee.visits.new
#visits = current_employee.visits.all
if params[:act]=='myData'
render 'myData'
elsif params[:act]=='myNetwork'
render 'myData'
elsif params[:act]=='temporaryUsers'
render 'temporaryUsers'
elsif params[:act]=='guestsVisits'
render 'guestsVisits'
elsif params[:act]=='myAccount'
render 'myAccount'
else
render 'show'
end
else
render 'static_pages/errorPage'
end
end
end
From what i can see if creating visit fails you instantiate #visits and #employee instance variables then rendering 'employees/guestsVisits' its view you use #visit which i think it doesn't exist inside empleyees controller
So add it ad
def guestsVisits
#visit = current_employee.visits.build
end

Rails create/update more than one model at the same time

I have been struggeling with a nested form for a while and I dont know why it is not working.
The form updates/creates the venue without complaint, but not the associated venue_image.
Problem: I want to create/update a model and one of its associated models at the same time. Please note that the controllers are namespaced under "admin"
_form.haml
.col-md-12
= form_for([:admin, venue], html: { class: 'form-horizontal', multipart: true }) do |f|
- if venue.errors.any?
.col-md-12.alert.alert-danger{role: "alert"}
%h2
= pluralize(venue.errors.count, "Error")
%ul
- venue.errors.full_messages.each do |message|
%li= message
.form-group
= f.label :name, class: 'col-md-2 control-label'
.col-md-10
= f.text_field :name, class: 'form-control'
.form-group
= f.label :category, class: 'col-md-2 control-label'
.col-md-10
= f.text_field :category, class: 'form-control'
.form-group
= f.label :description, class: 'col-md-2 control-label'
.col-md-10
= f.text_area :description, rows: 5, class: 'form-control'
.form-group
= f.label :street, class: 'col-md-2 control-label'
.col-md-10
= f.text_field :street, class: 'form-control'
.form-group
= f.label :zip, class: 'col-md-2 control-label'
.col-md-10
= f.text_field :zip, class: 'form-control'
.form-group
= f.label :city, class: 'col-md-2 control-label'
.col-md-10
= f.text_field :city, class: 'form-control'
.form-group
= f.label :homepage, class: 'col-md-2 control-label'
.col-md-10
= f.url_field :homepage, class: 'form-control'
%h3 Add images
= f.fields_for(:venue_image, html: { class: 'form-horizontal', multipart: true }) do |vi|
.form-group
= vi.label :name, class: 'col-md-2 control-label'
.col-md-10
= vi.input :name, label: false, class: 'form-control'
.form-group
= vi.label :venue_id, class: 'col-md-2 control-label'
.col-md-10
= vi.input :venue_id, label: false, class: 'form-control'
.form-group
= vi.label :default, class: 'col-md-2 control-label'
.col-md-10
= vi.input :default, as: :radio_buttons, label: false, class: 'form-control radio radio-inline'
.form-group
= vi.label :image_file, class: 'col-md-2 control-label'
.col-md-10
= vi.file_field :image_file, label: false, class: 'form-control'
.actions
= f.submit 'Save', class: 'btn btn-primary pull-right'
= link_to 'Cancel', :back, class: 'btn btn-default'
venues_controller
class Admin::VenuesController < Admin::BaseController
before_action :set_venue, only: [:show, :edit, :update, :destroy]
def index
#venues = Venue.all
end
def show
end
def new
#venue = Venue.new
#venue.venue_images.build
end
def edit
end
def create
#venue = Venue.new(venue_params)
if #venue.save
redirect_to admin_venue_path(#venue), notice: 'Venue was successfully created.'
else
render :new
end
end
def update
if #venue.update(venue_params)
redirect_to admin_venue_path(#venue), notice: 'Venue was successfully updated.'
else
render edit_admin_venue
end
end
def destroy
#venue.destroy
redirect_to admin_venues_url, notice: 'Venue was successfully destroyed.'
end
private
def set_venue
#venue = Venue.find(params[:id])
end
def venue_params
params.require(:venue).permit(:name, :category, :description, :street, :zip, :city, :homepage,
venue_image_attributes: [:name, :default, :image_file])
end
end
venue_images_controller
class Admin::VenueImagesController < Admin::BaseController
def new
image = VenueImage.new
render locals: { image: image }
end
def create
# TODO: Remove # if possible
#image = VenueImage.new(venue_images_params)
if #image.save
redirect_to admin_venue_path(#image.venue.id), notice: 'Image was successfully created.'
else
render admin_new_venue_path
end
end
private
def venue_images_params
params.require(:venue_image).permit(:name, :default, :image_file, :venue_id)
end
end
routes
namespace :admin do
resources :venues do
resources :venue_images
end
resources :users
end
Thanks in advance for any help! Please let me know if you need more of the code.
Seems like you have a has_many relation with the Venue(i.e, has_many :venue_images), then this line
= f.fields_for(:venue_image, html: { class: 'form-horizontal', multipart: true }) do |vi|
should be
= f.fields_for(:venue_images, html: { class: 'form-horizontal', multipart: true }) do |vi|
And your venue_params method should be like below
def venue_params
params.require(:venue).permit(:id, :name, :category, :description, :street, :zip, :city, :homepage, venue_images_attributes: [:id, :name, :default, :image_file])
end
Notice the plural venue_images and also I added :id to venue_params for Update to work correctly.

Split Form User Update Rails

Recently, I decided to split my User Form into an Edit & Account Form.
User Edit Form
-Name
-Username
-AboutMe
User Account Form
-Email
-Password
-Password Confirmation
The routes work fine and everything gets updated accordingly. But for some reason, when my User Model validates the Email Presence & it Fails, it renders the Edit Form with the appropriate Error Messages as oppose to the Account Form.
How can I set up my Update Method in my Controller to know which form to Render with the appropriate error messages?
Model
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :username
validates :username, :presence => true,
:length => { :maximum => 15 },
:format => { :with => VALID_UNAME_REGEX },
:uniqueness => { :case_sensitive => false }
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }, :if => :password #only validate if password changed!
validates :password_confirmation, presence: true, :if => :password
end
Views
Edit View
<%= form_for #user, :html => { :multipart => true } do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="statictitle">Your Profile</div>
<%= f.text_field :username, placeholder: "Username..", :class => "form-control" %>
<%= f.text_field :name, placeholder: "Name", :class => "form-control" %>
<%= f.text_area :bio, placeholder: "About yourself in 160 characters or less...", class: "textinput" %>
<%= f.submit "Update Profile", class: "btn btn-primary" %><br>
<% end %>
Account View
<%= form_for #user, :html => { :multipart => true } do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="statictitle">Your Account</div>
<%= f.text_field :email, placeholder: "Email", :class => "form-control" %>
<%= f.password_field :password, placeholder: "Password", :class => "form-control" %>
<%= f.password_field :password_confirmation, placeholder: "Password Confirmation", :class => "form-control" %>
<%= f.submit "Update Account", class: "btn btn-primary" %><br>
<% end %>
Controller
class UsersController < ApplicationController
def edit
#user = User.find_by_username(params[:id])
end
def account
#title = "Account"
#user = User.find_by_username(params[:id])
end
def update
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in #user
redirect_to user_url
else
render 'edit' ###I can't seem to figure out how to render the correct form
end
end
end
Routes
resources :users do
member do
get :account
end
end
You can check the HTTP Referer in this case which will tell you where the request came from and accordingly render your view.
for eg:
if URI(request.referer).path == edit_user_path
render :edit
else
render :account
end

Why do I get "NameError in Discussions"?

I'm currently working on a Ruby on Rails application that involves users creating discussions and commenting with microposts. It's a pretty simple concept but I'm new and have run into some trouble.
When trying to look at the discussions page (index of discussion) I get the error "NameError in Discussions#index":
undefined local variable or method `discussion' for #<#<Class:0x00000100c6e020>:0x0000010380edd8>
This is my Discussion controller:
class DiscussionsController < ApplicationController
before_filter :signed_in_user, only: [:index, :edit, :update]
def show
#user = User.find(params[:id])
#discussions = #user.discussion.paginate(page: params[:page])
#microposts = #user.micropost.paginate(page: params[:page])
end
def index
#discussions = Discussion.all
end
def create
#discussion = current_user.discussions.build(params[:discussion])
if #discussion.save
flash[:success] = "Discussion Started!"
redirect_to root_url
else
render 'static_pages/home'
end
end
def destroy
end
def edit
end
def update
end
def new
end
end
This is my Micropost form:
<% #micropost = Micropost.new %>
<% #micropost.discussion_id = discussion.id %>
<%= form_for(#micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.hidden_field :discussion_id, discussion.id%>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
This is my Discussion partial:
<% content_for :script do %>
<%= javascript_include_tag 'hover_content' %>
<% end %>
<li>
<div class = "intro-bar"><span class = "intro"><%=discussion.intro %></span></div>
<div class = "content-bar">
<span class = "content"><%= discussion.content %></span>
<div class = "buttons">
<div class = "vote-neg"><%= link_to "Break Up", signup_path,class: "btn btn-large btn-breakup" %></div>
<div class = "vote-plus"><%= link_to "Stay Together", signup_path,class: "btn btn-large btn-staytogether" %></div>
</div>
</div>
</li>
<span class = "timestamp">
Posted <%= time_ago_in_words(discussion.created_at) %> ago.
</span>
<div class = "comments">
<% discussion.microposts.each do |micropost| %>
<li>
<div class = "post-comment"><%= micropost.content%></div>
</li>
<% end %>
</div>
<% if signed_in? %>
<div class = "row">
<aside class = "span4">
<section>
<%= render 'shared/micropost_form', :locals => {:discussion => discussion }%>
</section>
</aside>
</div>
<% end %>
This is my Micropost controller:
class MicropostsController < ApplicationController
before_filter :signed_in_user, only: [:create, :destroy]
def index
end
def create
#discussion = current_user.discussions.new
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Posted!"
redirect_to root_url
else
render 'static_pages/home'
end
end
def destroy
end
end
This is my Discussion model:
class Discussion < ActiveRecord::Base
attr_accessible :content, :intro
has_many :microposts, dependent: :destroy
belongs_to :user
validates :content, presence: true, length: { maximum: 600 }
validates :intro, presence: true
default_scope order: 'discussions.created_at DESC'
end
This is my User model:
class User < ActiveRecord::Base
attr_accessible :email, :username, :age, :sex, :points, :password, :password_confirmation
has_secure_password
has_many :microposts, dependent: :destroy
has_many :discussions, dependent: :destroy
before_save {|user| user.email = email.downcase}
before_save :create_remember_token
validates :username, presence: true, length: {maximum: 15}
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}
validates :age, presence: true
validates :sex, presence: true
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
This is my Discussion index:
<% provide(:title, "Discussions") %>
<h1>Discussions</h1>
<ul class = "discussions">
<% #discussions.each do |discussion| %>
<li>
<%= render :partial =>"discussions/discussion", :locals=>{:discussion=>discussion} %>
</li>
<% end %>
</ul>
Try rewriting this line:
<%= render 'shared/micropost_form', :locals => {:discussion => discussion }%>
as:
<%= render 'shared/micropost_form', :discussion => discussion %>

Resources