Ruby on Rails: No validation error when using rails models association - ruby-on-rails-3

I'm using Ruby on Rails association to link the Store model to the Product model, using:
store has_many :products
product belongs_to :store
The only condition on the Product model, is the presence of a name:
validates :name, :presence => true
To create a new product, I use this code inside the Products controller, create method:
#store = Store.find_by_id session[:store_id]
if #store.products.create(:name => params[:name])
redirect_to :back, :notice => "New product successfully created."
else
redirect_to :back, :alert => "Can't create new product."
end
The problem, is that it works however the product name exists or not. I mean, in all cases, I have the "New product successfully created." message, even if the product name is empty.
I can't figure out where is the problem. Any help, please?

From documentation:
Creates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.
You should use another method to understand the saved record or not.
#store = Store.find_by_id session[:store_id]
product = #store.products.build(name: params[:name])
if product.save
redirect_to :back, notice: "New product successfully created."
else
redirect_to :back, alert: "Can't create new product."
end

Related

Rails - Show Action & View for models with one-to-one association?

I have a simple app with a User model & an Instructor_Profile model. The associations between these two models is one-to-one. I cannot get my view show.html.erb to render. I am simply trying to display a single attribute from the Instructor_Profile model and I get this error:
NoMethodError in Instructor_profiles#show
undefined method `name' for nil:NilClass
Any help would be greatly appreciated!
Models:
class User
has_one :instructor_profile
class InstructorProfile
belongs_to :user
UsersController:
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
UserMailer.welcome_email(#user).deliver
render 'static_pages/congratulations'
else
render 'new'
end
end
InstructorProfilesController:
def new
#instructor_profile = current_user.build_instructor_profile
end
def create
#instructor_profile = current_user.build_instructor_profile(params[:instructor_profile])
if #instructor_profile.save
flash[:success] = "Profile created!"
redirect_to root_path
else
....
end
end
def show
#user = User.find(params[:id])
#instructor_profile = #user.instructor_profile
end
Views/instructor_profiles/show.html.erb:
<p>Display Name: <%= #user.instructor_profile.name %></p>
It happens because #user.instructor_profile is nil.
That means there is no instructor_profile corresponding to the #user.
Please check the create method inside the UserController to confirm whether instructor_profile is creating or not. Code should be something like this,
#user.instructor_profile = InstructorProfile.new(name: "my_name")
#user.instructor_profile.save
edited:
has_one association doesn't mean that every user has to have an instructor_profile. So before you call the #user.instructor_profile.name, just confirm that #user have instructor_profile or not. In your view, you can easily solve this error by adding one condition..
<p>Display Name: <%= #user.instructor_profile ? #user.instructor_profile.name : "no instructor_profile present" %></p>.
One more thing, in instructor_profiles_controller/show, change the code into
#instructor_profile = InstructorProfile.find(params[:id])
#user = #instructor_profile.user

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

Rails 3.0 Skip validations for nested attributes

I need to be able to save a record without running validations on itself or its nested attributes. I'm stuck in Rails 3.0, and I cannot update to a newer version.
I have a report, each report has many responses (answers to questions). The responses are nested in the report form.
There are two ways the user should be able to save the report: Submit for review, where all validations are run, and Save And Finish Later, where no validations are run for the report or the nested responses. This needs to work for both create and update actions.
I am currently trying to use conditional validations. This works for update but not create. The problem is this line:
validate :has_answer_if_required, :if => Proc.new { |response| !response.report.finish_later? }
The report doesn't exist yet, so active record can't find this responses's report. That's where it crashes.
There are a lot some suggested solutions for this problem, but I couldn't get them working in Rails 3.0. update_attributes(attributes, :validate => false), for instance, is not available in Rails 3.0.
So, how do I skip the validations in the nested attributes?
class Report < ActiveRecord::Base
has_many :responses, :order => "created_at asc", :autosave => true
accepts_nested_attributes_for :responses
...
end
class Response < ActiveRecord::Base
belongs_to :report
validates_associated :report
validate :has_answer_if_required, :if => Proc.new { |response| !response.report.finish_later? }
validate :correct_answer_or_comment, :if => Proc.new { |response| !response.report.finish_later? }
end
class ReportsController < BaseController
def update
#report = Report.find(params[:id])
#report.attributes = params[:report]
if params[:finish_later].nil?
#report.update_attribute(:finish_later, false)
if #report.save!
redirect_to :action => :index
else
render :template => "reports/edit"
end
else
#report.finish_later = true
#report.save(:validate => false)
redirect_to :action => :index
end
end
def create
#report = Report.new(params[:report])
if params[:finish_later].nil?
#report.finish_later = false
if #report.save!
redirect_to :action => :index
else
render :template => "reports/edit"
end
else
#report.finish_later = true
#report.save!(:validate => false)
redirect_to :action => :index
end
end
end
Not sure if it will work with nested attributes, though I think it should... but give ValidationSkipper a try:
https://github.com/npearson72/validation_skipper
Just make sure you call skip_validation_for on the object you want to skip. Since nested attributes pass behavior to their children, you might be able to call this method directly on the parent object. Give it a try.

Mass Assignment Security Error when testing new action with Agile Web tutorial using Rails 3.2

I am going through the Agile Web tutorial with some slight changes. When I run functional tests in Rails 3.2, I am getting the following error:
test_should_get_new(OrdersControllerTest):
ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes: cart, deal
Here is the orders_controller_test.rb code:
test "should get new" do
cart = Cart.create
session[:cart_id] = cart.id
LineItem.create(cart: cart, deal: deals(:one))
get :new
assert_response :success
end
Here is the orders fixtures:
one:
name: MyString
address: MyText
email: MyString
pay_type: Check
Here is the line items fixtures:
one:
deal: one
order: one
Here is the deals fixture:
one:
title: MyString
description: MyText
image_url: MyString
price: 9.99
Here is the order controller code:
def new
#cart = current_cart
if #cart.line_items.empty?
redirect_to store_url, notice: "Your cart is empty"
return
end
#order = Order.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #order }
end
end
I tried using FactoryGirl but still got the same error message. Here is the code:
test "should get new" do
cart = FactoryGirl.build(:cart)
session[:cart_id] = cart.id
LineItem.create(cart: cart, deal: deals(:one))
get :new
assert_response :success
end
And the FactoryGirl code:
FactoryGirl.define do
factory :cart do
end
end
For FactoryGirl I also tried 'create' instead of 'build' and got the same error message.
Although I could turn off the mass assignment error in config, I would rather not since I prefer to test properly.
Any suggestions please?
Instead of LineItem.create(cart: cart, deal: deals(:one)) try
item = LineItem.create
item.cart = cart
item.deal = deals(:one)
or in your LineItem model, add:
attr_accessible :cart, :deal
In your 'models', 'order.rb' add the attr_accessible line
class Order < ActiveRecord::Base
has_many :line_items, dependent: :destroy
attr_accessible :name, :address, :email, :pay_type
end

Rails3 and Respond_with problem

I have an application, on which I have two user interfaces.
The first one is for normal users and the second one is for iphone users.
Everything was working fine until i refactored my code within controller to use the respond_with declarative instead of respond_to.
The application is still working for the html interface(:format => :html) but not on the iphone interface(:format => :iphone).
On the iphone, when I do the following action (:index, :new, :edit, :show) it works.
But when i do (:create, :update, :destroy), I get errors saying the template is not found(create.iphone.haml for example).
On my controller I have
respond_to :html, :iphone
And then for example, the edit and the update action
def edit
#refund = Refund.find(params[:id])
respond_with(#refund)
end
def update
#refund = Refund.find(params[:id])
if #refund.update_attributes(params[:refund])
flash[:notice] = 'Refund was successfully updated.'
end
respond_with(#refund, :location => project_refunds_path(#project))
end
In fact, I would like the :iphone format is handle as :html is ... and not by calling the to_format method as it is specified into the doc.
Solved it by myself.
Just need to add this to an initializer file :
ActionController::Responder.class_eval do
alias :to_iphone :to_html
end
What if you do:
respond_with(#refund, :location => project_refunds_path(#project)) do |format|
format.iphone { whatever you had here before refactoring }
end