Why isn't my Rails Custom Validator working? - ruby-on-rails

I'm creating a store that allow users to purchase greeting cards (Happy Birthday, Valentine's Day)
I have two models: Card and Recipient
class Card < ActiveRecord::Base
belongs_to :recipient
def valid_name
if #recipient && #recipient.name.nil?
#card.errors.full_messages.push("Name can't be blank")
end
end
validate :valid_name
validates_associated :recipient
validates :recipient, presence: true
validates :note, length: { in: 1..200 }
class Recipient < ActiveRecord::Base
has_one :card
validates :name, presence: true
end
Card Controller
def new
#card = Card.new
#card.build_recipient
end
def create
#card = Card.new(card_params)
if #card.save
flash[:notice] = "Your card was successfully added to your cart!"
redirect_to :back
else
flash[:danger] = "Something was wrong"
render 'new'
end
end
Paramaters
def card_params
params.require(:card).permit(:note, :amount, :recipient_attributes:[:name, :email])
end
I don't have a RecipientsController but I don't think I need one.
I would like to validate the fields in my Recipients model in a form for purchasing a Card.
I've tried validates_associated and adding my own validators. I just need to validate my recipient (customers) info.

Related

FactoryGirl polymorphic associations

I can't create a valid comment factory using factory girl. My comment model belongs to commentable and is polymorphic. I've tried a whole bunch of different things, but at the moment, most of my tests are failing with this error:
ActiveRecord::RecordInvalid:
Validation failed: User can't be blank, Outlet can't be blank
I'm not sure why it isn't passing validation, especially because my comment model validates the presence of user_id and outlet_id, not User and Outlet
Here is my factory:
factory :comment do
body "This is a comment"
association :outlet_id, factory: :outlet
association :user_id, factory: :user
#outlet_id factory: :outlet
association :commentable, factory: :outlet
end
class CommentsController < ApplicationController
def new
#comment = Comment.new
end
def create
#outlet = Outlet.find(params[:outlet_id])
#comment = #outlet.comments.build(comment_params)
#comment.user_id = User.find(params[:user_id]).id
if #comment.save
redirect_to(#outlet)
end
end
def edit
#comment = Comment.find(params[:id])
end
def update
#comment = Comment.find(params[:id])
if #comment.update(comment_params)
redirect_to #comment.outlet
end
end
def destroy
#comment = Comment.find(params[:id])
if #comment.destroy
redirect_to #comment.outlet
end
end
private
def comment_params
params.require(:comment).permit(:body, :outlet_id, :user_id)
end
end
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
validates :body, :user_id, :outlet_id, presence: true
validates :body, length: { in: 1..1000 }
end
Is there a particular reason for using association :user_id?
You probably want something more like:
factory :comment do
body "This is a comment"
association :outlet, factory: :outlet
association :user, factory: :user
association :commentable, factory: :outlet
end
Which incidentally can simplify to:
factory :comment do
body "This is a comment"
outlet
user
association :commentable, factory: :outlet
end

Rails Client side Collection Validation fails - Simple Form

I have to build a simple app that allows users to loan and borrow books. Simply put a User can create books, and they can pick another user to loan the book to.
I have three models User, Book and Loan:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :books
has_many :loans, through: :books
has_many :borrowings, class_name: "Loan"
validates :username, uniqueness: true
validates :username, presence: true
end
class Book < ActiveRecord::Base
belongs_to :user
has_many :loans
validates :title, :author, presence: true
end
class Loan < ActiveRecord::Base
belongs_to :user
belongs_to :book
validates :user, :book, :status, presence: true
end
The LoansController looks like this:
class LoansController < ApplicationController
before_action :find_book, only: [:new, :create]
def new
#users = User.all
#loan = Loan.new
authorize #loan
end
def create
#loan = Loan.new
#loan.book = #book
#loan.user = User.find(loan_params[:user_id])
#loan.status = "loaned"
authorize #loan
if #loan.save
redirect_to :root
else
render :new
end
end
private
def loan_params
params.require(:loan).permit(:user_id)
end
def find_book
#book = Book.find(params[:book_id])
end
end
My form looks like:
<%= simple_form_for([#book, #loan]) do |f| %>
<%= f.input :user_id, collection: #users.map { |user| [user.username, user.id] }, prompt: "Select a User" %>
<%= f.submit %>
<% end %>
If I submit the form without selecting a user, and keep the "Select a User" prompt option, the form is submitted and the app crash because it can't find a user with id=
I don't know why the user presence validation in the form does not work...
you will change your Create method
def create
#loan = Loan.new
#loan.book = #book
#loan.user = User.find_by_id(loan_params[:user_id])
#loan.status = "loaned"
authorize #loan
if #loan.save
redirect_to :root
else
render :new
end
end

Rails exceptions to validates uniqueness

I want to make it so that users who log into my site can only like once on a question, But I would also want people who aren't logged in to also be able to like.
currently I have this to ensure that logged in users only vote once
model
class Yesvote < ActiveRecord::Base
belongs_to :user
belongs_to :question
validates_uniqueness_of :user, scope: :question
end
controller
def yesvote
#question = Question.find(params[:id])
if current_user != nil
yesvote = Yesvote.create(yes: params[:yes], user: current_user, question: #question)
else
yesvote = Yesvote.create(yes: params[:yes], question: #question)
end
if yesvote.valid?
redirect_to :back
else
flash[:danger] = "once only!"
redirect_to :back
end
end
currently if one user likes without logging in, it prevents further likes from un-logged in users. basically, it prevents more than one yesvotes to have a user_id of null/nil
This may helpful :-
validates_uniqueness_of :user_id, :allow_blank => true, :scope => [:question_id]
:allow_blank or :allow_nil, which will skip the validations on blank and nil fields, respectively.
To validate multiple attributes you could use scope:
class Yesvote < ActiveRecord::Base
belongs_to :user
belongs_to :question
validates_uniqueness_of :user_id, scope: :question_id
end
I guess you are using devise for authentication. If so, you can add a before filter in your controller to authenticate users before voting:
before_filter: authenticate_user!, only: :yesvote
def yesvote
#question = Question.find(params[:id])
yesvote = Yesvote.create(yes: params[:yes], user: current_user, question: #question)
redirect_to :back
end
Edit:
You can use Proc to skip validation if user_id is blank.
validates_uniqueness_of :user_id, scope: :question_id, unless: Proc.new { |v| v.user_id.blank? }

Foreign key is still nil after relation was built

I have problem while creating new user with address. I create record in Users and Addresses table, but foreign key to address in user is still nil.
class User < ActiveRecord::Base
belongs_to :address
accepts_nested_attributes_for :address
end
class Address < ActiveRecord::Base
has_one :user
end
def new
#user = User.new
#user.build_address
end
def create
#user = User.new(user_params)
if #user.save
flash[:notice] = "Your account has been created."
redirect_to signup_url
else
flash[:notice] = "There was a problem creating you."
render :action => :new
end
end
private
def user_params
params.require(:user).permit(
:first_name,
:last_name,
:email,
:password,
:password_confirmation,
address_attributes: [:id, :city]
)
end
end
Thank you.
You mixed up relation types.
Try this:
class User < ActiveRecord::Base
has_one :address
end
class Address < ActiveRecord::Base
belongs_to :user
end
Also your code is structured funny, two models and then some methods out of their context. I hope it's just a misprint. If not, put everything except Address model into User model.

Rails how to get associated model attributes

I have the method below which saves data to the users table as well as the user_details table.
When i pass the #newUser variable to the EmailMailer, i can't access the user_details attributes. How can i pass the user_details in the #newUser object without having to re-query the database?
Models
class User < ActiveRecord::Base
has_one :user_details, :dependent => :destroy
accepts_nested_attributes_for :user_details
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :login, :home_phone, :cell_phone, :work_phone, :birthday, :home_address, :work_address, :position, :company, :user_details_attributes
end
class UserDetails < ActiveRecord::Base
belongs_to :user
attr_accessible :first_name, :last_name, :home_phone, :cell_phone, :work_phone, :birthday, :home_address, :work_address, :position, :company
end
Controller
# POST /users
def create
#newUser = User.new(params[:user], :include =>:user_details)
# create password
require 'securerandom'
password = SecureRandom.urlsafe_base64(8)
#newUser.password = password
respond_to do |format|
if #newUser.save
#newUser.build_user_details
# Tell the UserMailer to send a welcome Email after save
EmailMailer.welcome_email(#newUser).deliver
# To be used in dev only. Just tests if the email was queued for sending.
#assert ActionMailer::Base.deliveries.empty?
format.html {
flash[:success] = "User created successfully"
redirect_to(contacts_path)
}
else
format.html {
flash[:error] = flash[:error].to_a.concat resource.errors.full_messages
redirect_to(contacts_path)
}
end
end
end
Something like this might do what you are after.
class User < ActiveRecord::Base
has_one :user_details
accepts_nested_attributes_for :user_details
after_initialize :build_user_details
...
end
# In controller
def create
#new_user = User.new
#new_user.attributes = params[:user]
if #new_user.save
# do mail thing
else
# other thing
end
end
You need to build the UserDetails association prior to saving #newUser
#newUser.build_user_details
if #newUser.save
#send mailer
else
#do something else
end
Alternatively you could use the create action after the #newuser is saved
if #newUser.save
#newUser.create_user_details
#send mailer
else
#do something else
end
By the way, Ruby/Rails convention is to use snake_case for variables. so #newUser should be #new_user.

Resources