Rails exceptions to validates uniqueness - ruby-on-rails

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? }

Related

Why isn't my Rails Custom Validator working?

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.

Newsfeed in Rails, unidentified method

I'm creating a simple newsfeed in rails. The aim is for it to return all the posts from the groups the user is following. I am using socialization for my follow functionality.
The exact error is:
NoMethodError (undefined method `followees' for false:FalseClass)
Here are my basic models not including like and follow as they're empty:
User:
class User < ActiveRecord::Base
authenticates_with_sorcery!
attr_accessible :username, :password, :email
has_many :groups
has_many :posts
acts_as_follower
acts_as_liker
before_create :generate_auth_token
def auth_token_expired?
auth_token_expires_at < Time.now
end
def generate_auth_token(expires = nil)
self.auth_token = SecureRandom.hex(20)
self.auth_token_expires_at = expires || 1.day.from_now
end
def regenerate_auth_token!(expires = nil)
Rails.logger.info "Regenerating user auth_token"
Rails.logger.info " Expiration: #{expires}" if expires
generate_auth_token(expires)
save!
end
end
Group:
class Group < ActiveRecord::Base
attr_accessible :description, :name, :user_id
has_many :posts
belongs_to :user
acts_as_followable
end
Post:
class Post < ActiveRecord::Base
attr_accessible :body, :user_id, :group_id
belongs_to :user
belongs_to :group
acts_as_likeable
end
I have setup a function named newsfeed in my post controller. The function grabs all the groups that a user is following and then grabs all the posts that have group_ids matching group_ids in the returned groups array. But I keep getting unidentified method followees(socialization provides this). Yet it appears to work when using single users and posts in irb.
def newsfeed
#groups = current_user.followees(Group)
#posts = Post.where(:group_id => #groups)
respond_to do |format|
format.html # show.html.erb
format.json { render json: #posts }
end
end
Thanks for any help.
Apparently, your current_user method returns false, instead of a user. Check what's returned from that method, as find out why you get the error...
Your current_user return false instead of instance of User. You may see it from error text.

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.

How to create another object when creating a Devise User from their registration form in Rails?

There are different kinds of users in my system. One kind is, let's say, a designer:
class Designer < ActiveRecord::Base
attr_accessible :user_id, :portfolio_id, :some_designer_specific_field
belongs_to :user
belongs_to :portfolio
end
That is created immediately when the user signs up. So when a user fills out the sign_up form, a Devise User is created along with this Designer object with its user_id set to the new User that was created. It's easy enough if I have access to the code of the controller. But with Devise, I don't have access to this registration controller.
What's the proper way to create a User and Designer upon registration?
In a recent project I've used the form object pattern to create both a Devise user and a company in one step. This involves bypassing Devise's RegistrationsController and creating your own SignupsController.
# config/routes.rb
# Signups
get 'signup' => 'signups#new', as: :new_signup
post 'signup' => 'signups#create', as: :signups
# app/controllers/signups_controller.rb
class SignupsController < ApplicationController
def new
#signup = Signup.new
end
def create
#signup = Signup.new(params[:signup])
if #signup.save
sign_in #signup.user
redirect_to projects_path, notice: 'You signed up successfully.'
else
render action: :new
end
end
end
The referenced signup model is defined as a form object.
# app/models/signup.rb
# The signup class is a form object class that helps with
# creating a user, account and project all in one step and form
class Signup
# Available in Rails 4
include ActiveModel::Model
attr_reader :user
attr_reader :account
attr_reader :membership
attr_accessor :name
attr_accessor :company_name
attr_accessor :email
attr_accessor :password
validates :name, :company_name, :email, :password, presence: true
def save
# Validate signup object
return false unless valid?
delegate_attributes_for_user
delegate_attributes_for_account
delegate_errors_for_user unless #user.valid?
delegate_errors_for_account unless #account.valid?
# Have any errors been added by validating user and account?
if !errors.any?
persist!
true
else
false
end
end
private
def delegate_attributes_for_user
#user = User.new do |user|
user.name = name
user.email = email
user.password = password
user.password_confirmation = password
end
end
def delegate_attributes_for_account
#account = Account.new do |account|
account.name = company_name
end
end
def delegate_errors_for_user
errors.add(:name, #user.errors[:name].first) if #user.errors[:name].present?
errors.add(:email, #user.errors[:email].first) if #user.errors[:email].present?
errors.add(:password, #user.errors[:password].first) if #user.errors[:password].present?
end
def delegate_errors_for_account
errors.add(:company_name, #account.errors[:name].first) if #account.errors[:name].present?
end
def persist!
#user.save!
#account.save!
create_admin_membership
end
def create_admin_membership
#membership = Membership.create! do |membership|
membership.user = #user
membership.account = #account
membership.admin = true
end
end
end
An excellent read on form objects (and source for my work) is this CodeClimate blog post on Refactoring.
In all, I prefer this approach vastly over using accepts_nested_attributes_for, though there might be even greater ways out there. Let me know if you find one!
===
Edit: Added the referenced models and their associations for better understanding.
class User < ActiveRecord::Base
# Memberships and accounts
has_many :memberships
has_many :accounts, through: :memberships
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :account
end
class Account < ActiveRecord::Base
# Memberships and members
has_many :memberships, dependent: :destroy
has_many :users, through: :memberships
has_many :admins, through: :memberships,
source: :user,
conditions: { 'memberships.admin' => true }
has_many :non_admins, through: :memberships,
source: :user,
conditions: { 'memberships.admin' => false }
end
This structure in the model is modeled alongside saucy, a gem by thoughtbot. The source is not on Github AFAIK, but can extract it from the gem. I've been learning a lot by remodeling it.
If you don't want to change the registration controller, one way is to use the ActiveRecord callbacks
class User < ActiveRecord::Base
after_create :create_designer
private
def create_designer
Designer.create(user_id: self.id)
end
end

Displaying Sender and Receiver user profile on each micropost

We are looking to have Sender and Receiver attributes for each micropost that is entered on our site. The sender of the post, and the receiver whom it is directed to.
In other words, on each micropost that each user sees, we want the content, and just above or below the content of the post we want the sender shown and receiver shown. We also want users to be able to click on either the sender or the receiver and be linked directly to that profile.
How can we go about doing this? We are relatively new to rails and think additions need to be made in the Micropost model for this change to work. Or should the changes be made in the MicropostsController?
Micropost Model:
class Micropost < ActiveRecord::Base
attr_accessible :content, :belongs_to_id
belongs_to :user
validates :content, :presence => true, :length => { :maximum => 240 }
validates :user_id, :presence => true
default_scope :order => 'microposts.created_at DESC'
# Return microposts from the users being followed by the given user.
scope :from_users_followed_by, lambda { |user| followed_by(user) }
private
# Return an SQL condition for users followed by the given user.
# We include the user's own id as well.
def self.followed_by(user)
following_ids = %(SELECT followed_id FROM relationships
WHERE follower_id = :user_id)
where("user_id IN (#{following_ids}) OR user_id = :user_id",
{ :user_id => user })
end
end
MicropostsController:
class MicropostsController < ApplicationController
before_filter :authenticate, :only => [:create, :destroy]
def create
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Posted!"
redirect_to current_user
else
#feed_items = []
render 'pages/home'
end
end
def destroy
#micropost.destroy
redirect_to root_path
end
end
To eliminate some confusion and make it a bit more railsy, I'd go with:
class Micropost < ActiveRecord::Base
belongs_to :sending_user, :class_name=>"User", :foreign_key=>"user_id"
belongs_to :receiving_user, :class_name=>"User", :foreign_key=>"belongs_to_id"
end
this will allow something like this in your view for a given Micropost object "#micropost":
<%= link_to(#micropost.sending_user.username, user_path(#micropost.sending_user)) %>
<%= link_to(#micropost.receiving_user.username, user_path(#micropost.receiving_user)) %>
*this assumes several things about the user object and routing, but should get you on the right path.

Resources