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!
Related
rails newbie here I am using friendly_id gem, I have a Page model when I create page a friendly_id slug generate from title given in text field,
What i want is to able to edit that slug and do not change on update or when i change the title
here is my Page.rb
class Page < ActiveRecord::Base
belongs_to :user
include Bootsy::Container
extend FriendlyId
friendly_id :title, use: :slugged
validates :title, :presence => true
validates :user_id, :presence => true
def should_generate_new_friendly_id?
end
end
I know this is a very basic task but I am new in rails. thanks
Done this by adding the following login inside should_generate_new_friendly_id? :
def should_generate_new_friendly_id?
new_record? || !self.slug.present?
end
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
I try to add an email-validator in my rails app. I created the following file /lib/validators/email_validator.rb
class EmailValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
unless value =~ /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
object.errors[attribute] << (options[:message] || "is not formatted properly")
end
end
end
In the application.rb I added this line:
config.autoload_paths << "#{config.root}/lib/validators"
And here is my User model:
class User < ActiveRecord::Base
attr_accessible :email, :password,:name
validates :email, :presence => true, :uniqueness => true, :email => true
end
If i want to start the server I got an error:
Unknown validator: 'EmailValidator' (ArgumentError)
Has anybody an idea how I can fix this problem?
If you place your custom validators in app/validators they will be
automatically loaded without needing to alter your
config/application.rb file.
Resource: Where should Rails 3 custom validators be stored? (second answer)
This error occures, because rails loads model file before your validation file
Try to require your validation file manually at the start of your model file
require_dependency 'validators/email_validator.rb'
Try the modified User model;
class User < ActiveRecord::Base
attr_accessible :email, :password,:name
validates :email, :presence => true, :uniqueness => true
end
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
My Rails skills is (to be kind) rusty so this is probably a newbie question. I'm trying to create a simple email sending form, but I keep getting:
NoMethodError in Mail#create
undefined method `model_name' for Mail::Message:Class
I'm pretty sure that my problem is in my controller, the relevant method looks like this:
def create
#mail = Mail.new(params[:mail])
MailMailer.send_mail(#mail).deliver
end
Thinks this line is causing the error #mail = Mail.new(params[:mail]). My Mail model class looks like this:
class Mail < ActiveRecord::Base
include ActiveModel::Validations
validates :password, :presence => true
attr_accessor :password, :to, :cc, :bcc, :from, :subject, :message
end
And my mailer looks like this:
class MailMailer < ActionMailer::Base
def send_mail(mail)
mail(:to => mail.to, :subject => mail.subject)
end
end
Can anyone spot what I'm doing wrong?
Your problem is probably right here:
class Mail < ActiveRecord::Base
# ---------^^^^^^^^^^^^^^^^^^
include ActiveModel::Validations
validates :password, :presence => true
attr_accessor :password, :to, :cc, :bcc, :from, :subject, :message
end
Subclassing ActiveRecord::Base and including ActiveModel::Validations is a bit odd as AR already includes all the validation stuff. Mixing AR and attr_accessor is another sign that something strange is going on.
In your comments you note that you created this model with:
$ rails g model mail
And that tries to create a database-backed ActiveRecord model as that's almost always what people want. You might also run into trouble because Mail is already in use so maybe you want to use a different name.
If you just want a model that is just a temporary bag of data then you can do this:
class SomeEmail
attr_accessor :password, :to, :cc, :bcc, :from, :subject, :message
end
You probably don't need the validations here but you can add them:
class SomeEmail
include ActiveModel::Validations
validates :password, :presence => true
attr_accessor :password, :to, :cc, :bcc, :from, :subject, :message
end
but the validations won't be triggered unless you manually call valid? so there's no much point.
Finally, just adding attr_accessor doesn't give you a useful constructor so with all of the above changes, this:
#mail = SomeMail.new(params[:mail])
still won't do what you want as nothing in params[:mail] will get saved anywhere. So add an initialize implementation to your email class and a call to valid? to your controller.