Okay I have quite a weird scenario that I do not know how to deal with so please bear with me as I try to explain it to you.
I have the following model.
class User < ActiveRecord::Base
Roles = { pending: 'pending_user', role2: 'role2', etc: 'etc' }
attr_accessible :role
validates :role, inclusion: {in: Roles.values}
before_create :add_pendng_role #Set user role to Roles[:pending]
end
Now the problem is when creating a record for the first time, this validation fails! For example in my controller I have the following code:
class UsersController < ActionController::Base
#user = User.new params[:user]
if #user.save # --------------- ALWAYS FAILS -------------------------------
#do something
else
#do something else
end
end
Now the reason I believe it fails is because a role is only added before_create which is called after the validations have passed. Now I know that I can't replace the before_create :add_role with before_validation :add_role because I think that it will add the role each time a validation is done. The reason I can't have that is because the user role will change in the application and I don't want to reset the role each time a validations are done on the user.
Any clues on how I could tackle this?
You could try:
before_validation :add_role, on: :create
Use *before_validation*, as explained in the rails callback guide
class User < ActiveRecord::Base
Roles = { pending: 'pending_user', role2: 'role2', etc: 'etc' }
attr_accessible :role
validates :role, inclusion: {in: Roles.values}
before_validation :add_pendng_role, on: :create #Set user role to Roles[:pending]
end
Looks like you'll be able to change before_create to before_validation if you use the :on argument:
before_validation :add_pendng_role, :on => :create
Related
I have User model, and need to validate phone number attribute based on the controller param.
class User < ActiveRecord::Base
validates_presence_of :phone_number
end
This validation should validate phone_number in the Create action.
Let's say the param I should check is
params[:phone_number]
you can use before_save validation, in User model you can write
before_save :validate_phone_number
private
def validate_phome_number
self.phone_number = /some regex/
end
In self.phone_number you will get controller params by default
validate :custom_validation, :on => :create
private
def custom_validation
//whatever you want to check
end
I have tried many ways to complete this task,
I used Inheritance - Created a sub class from the User class
Call a method in the model from the controller to set the attribute and bind that attribute with the validation
Use the context option
I guess the context option is the most reliable solution for this issue i faced. So here when i set the context as :interface the model validation will trigger only based on that value
Model - User.rb
class User < ActiveRecord::Base
validates_presence_of :phone_number, on: :interface
end
Controller - users_controller.rb
#user = User.new(user_params)
#save_result = false
if params[:invitation_token] == nil
save_result = #user.save(context: :interface)
else
save_result = #user.save
end
If you use multiple options in ON:
validates :terms_and_conditions, acceptance: {accept: true}, on: [:create, :interface], unless: :child
validates :privacy_policy, acceptance: {accept: true}, on: [:create, :interface], unless: :child
I have a method to change user status inside the it's model, is it possible to use this do something like this inside the user model:
class User < ActiveRecord::Base
def confirm!
super
self.update_column(:status => "active")
end
end
I saw these two examples;
Rails update_attribute
how to update attributes in self model rails
couldn't quite get which one to go with!
It depends on whether or not you want any validations in the model to run. update_attribute will not run the validations, but update_attributes will. Here are a couple of examples.
Using update_attributes:
class User < ActiveRecord::Base
validates :email, presence: true
def confirm!
update_attributes(status: 'active')
end
end
The following will return false and will not update the record, because not email has been set:
user = User.new
user.confirm! # returns false
Using update_attribute:
class User < ActiveRecord::Base
validates :email, presence: true
def confirm!
update_attribute(:status, 'active')
end
end
The following will update status to active regardless of whether or not email has been set:
user = User.new
user.confirm! # returns true
I'm trying to trigger a method right before saving an instance. I've got the User model:
class User < ActiveRecord::Base
has_secure_password
attr_accessible :name, :first_surname,:second_surname,:email, :password, :password_confirmation,:number,:credit
before_save{ self.email.downcase! }
before_create :generate_auth_token
default_scope order: 'users.created_at ASC'
has_many :operations
def consume(what,price,agent)
self.operations.create(category:what,price:price, agent_id:agent)
end
end
And each User has many Operation(note the use of the pry debuger via binding.pry:
class Operation < ActiveRecord::Base
attr_accessible :agent_id, :comment, :postcredit, :precredit, :category, :user_id,:price
validates_presence_of :user_id
validates_presence_of :agent_id
validates_presence_of :price
validates_presence_of :precredit
validates_presence_of :postcredit
validates_presence_of :category
#before_save :compute_prices, :on => :create
before_create :compute_prices
belongs_to :user
private
def compute_prices
binding.pry
user=User.find(self.user_id)
self.precredit=user.credit
#select whether adding or subtracting
if self.category == 'credit'
self.postcredit=self.precredit+self.price
else
self.postcredit=self.precredit-self.price
end
user.update_attributes(credit:self.postcredit)
end
end
I populate the database with users and operations, and test it via the console $rails c --sandbox. Then I:
>fi=User.first
>ope=fi.operations.create(category:'credit',price:12.2,agent_id:3)
#Now here the debugger should start and does not
I try it with both before_create and before_save, but none work.
before_create :compute_prices
before_save :compute_prices, :on => :create
The only option that worked is after_initialize :compute_prices, but this gets triggered after every find or initilialization.
Any ideas?
SOLUTION
As explained as a comment to the first answer, the solution was to user before_validation (function), on: :create, instead of before_save ....
Is your operation valid? The callback lifecycle is here: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html and if validation fails, it won't get to the create callbacks
Ideally i want urls that look like:
/users/john-s
/users/foo-b
/users/brad-p
I have a user model that looks like this:
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :name, :use => :slugged
validates :first_name, :presence => true
validates :last_name, :presence => true
# "John Smith" becomes "John S."
def name
"#{self.first_name.capitalize} #{self.last_name[0].capitalize}."
end
end
The bad behavior is best explained with this console output:
[15] pry(main)> User.new(first_name: nil, last_name: nil).save!
(0.2ms) BEGIN
(0.1ms) ROLLBACK
NoMethodError: undefined method `capitalize' for nil:NilClass
The Issue (finally! :) )
It appears what happens is that FriendlyId is called BEFORE my validations for first_name and last_name are triggered. This results in the name method pooping when capitalize is called on a nil value.
What can I do so that my validations are triggered before FriendlyId is called? And actually taking it a bit further... Why is FriendlyId involved at all prior to any validity being established?
Thank you!!
It is invoked because the slug is generated prior to validation on save:
https://github.com/FriendlyId/friendly_id/issues/280
I am not quite sure what it would take to monkeypatch it.
The way I wound up fixing mine was like this:
def name
"#{self.first_name.capitalize} #{self.last_name[0].capitalize}." if first_name.present? && last_name[0].present?
end
I think the way to go is to set the user name in a before_validation on create that has is prepended to the friendly_id's own before_validation callback of setting the slug:
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :name, :use => :slugged
# Make sure to prepend it so that it runs before Friendly_id's own callback
# http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
before_validation :set_name, on: :create, prepend: true
validates :first_name, :presence => true
validates :last_name, :presence => true
# To control when new slugs should be generated
def should_generate_new_friendly_id?
new_record? || first_name_changed? || last_name_changed?
end
private
def set_name
"#{self.first_name.capitalize} #{self.last_name[0].capitalize}."
end
end
Hope this helps!
I want to be able to write a comment as a guest or as a registered user with devise.
My comment model contains :title, :content, :user_id, :guest_email, :guest_website and :write_as_guest as a boolean.
I wanted to validate the presence of :guest_email only when no user is signed_in. But I think I'm not going in the good direction.
I'm managing the form with AJAX/jQuery and I wanted to have a guest form where :content and :guest_email are necessary fields. In another hand, I want to have the user form where only the :content is necessary.
Here is how I tried to go for it.
class Comment < ActiveRecord::Base
before_validation :set_write_as_guest
belongs_to :user
validates_presence_of :content
validates_presence_of :guest_email, :if => :write_as_guest?
private
def write_as_guest?
self.write_as_guest
end
def set_write_as_guest
if user_signed_in?
self.write_as_guest = false
else
self.write_as_guest = true
end
end
end
It seems that user_signed_in? method needs before_filter :authenticate_user! then I have the following in my comments_controller
before_filter :authenticate_user!, :only => :create
But however I don't want to authenticate to create because that's a guest...
So if somebody would be able to propose me a way to write as a guest or as a user, that would be really appreciated.
Thx
You can do a custom validation like this
class Comment < ActiveRecord::Base
validate :guest_commentator
def guest_commentator
user = User.find(user_id)
self.errors.add(:user_id => "some error message here ") unless user.nil?
end
end