why doesn't attribute in a model work in Rails SIMPLE - ruby-on-rails

new to rails/ruby, so this (i think) is a very straightforward question. Why doesn't this work in my model
class Subscription < ActiveRecord::Base
attribute :email, :validate => /\A([\w\.%\+\-]+)#([\w\-]+\.)+([\w]{2,})\z/i
end
I get the error
undefined method `attribute' for #<Class:0x00000101218ed0>
The model does exist, as does the column (or attribute?) 'email', so surely I must be able to validate its submission like so.

Rails format helper validates the attributes' values by testing whether they match a given regular expression, which is specified using the :with option
Try:
class Subscription < ActiveRecord::Base
validates :email, format: { with: /\A([\w\.%\+\-]+)#([\w\-]+\.)+([\w]{2,})\z/i,message: "your validation message" }
end
If you are using rails 3.x then you need validates_format_of
validates_format_of :email, :with => /\A([\w\.%\+\-]+)#([\w\-]+\.)+([\w]{2,})\z/i

Related

Input validations on fields in ActiveAdmin

When I create a new form in ActiveAdmin, I want validations on my form input fields. But I can't find a related tutorial. I want some fields to accept only alphabets, some only digits , and some should be of specific length.
f.input :name, :label => "Title", input_html: { autofocus: true }
f.input :description
f.input :email
f.input :contact_number
f.input :contact_person
[Answer not only for ActiveAdmin, but for RoR in general]
You should do it in model.
• For digits only:
You want your :contact_number to be a digit, so your model (e.g. User) should look like this:
class User < ActiveRecord::Base
validates :contact_number, numericality: {only_integer: true}
end
• For min. 5 characters:
If description for example must be at least 5 characters it will be:
validates_length_of :description, minimum: 5
• For letters only:
validates_format_of :name, with: /^[-a-z]+$/
(details about reg. expressions --> Validate: Only letters, numbers and - )
Additional info:
If your form don't pass model validation it will return alert about wrong argument (which is accessible in flash[:alert] array).
More about it in:
http://guides.rubyonrails.org/active_record_basics.html#validations
You can have the validations defined in your corresponding Model class.
See the official documentation for Rails validation.
ActiveAdmin will pick it up when you try to create/edit/update objects of that model if you have Rails standard validations or even custom validations defined in your Model class.
For example, for your email validation, you can have this in your model:
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create
Then, when you try to create/save an object through ActiveAdmin, it will show you error if the email is not in the correct format.
So, you have to define all of your validations (for all the fields that you want) in your model. That's it!
And, to display a list of all validation errors, you have to do:
form do |f|
f.semantic_errors *f.object.errors.keys
# ...
end
Update
Add these validations to your Model class:
validates_presence_of :description
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
validates :contact_number, :presence => {:message => 'hello world, bad operation!'},
:numericality => true,
:length => { :minimum => 10, :maximum => 15 }
These are Rails standard validations. You can add custom validations to your model too.
For example, if you want to add a custom validation for the username, you can define that like this:
validate :username_must_be_valid
And, then define the custom validator method username_must_be_valid in the same model class like this:
private
def username_must_be_valid
errors.add(:username, 'must be present') if username.blank? && provider.blank?
end

update nested_attributes_for errors with uniqueness constraint

So I'm working on build a user model in rails and this user model will have an associated email address model. The email address model has a uniqueness constraint on the email. Right now I have it set up so that the user accepts_nested_attributes_for :email_address. This works great on create but on update I get this error:
ActiveRecord::JDBCError: org.postgresql.util.PSQLException:
ERROR: duplicate key value violates unique constraint
"index_email_addresses_on_email"
I can recreate this bug by doing this in the rails console:
u = User.create(:name => "foo", :new_password => "Passw0rd",
:email_address_attributes => {:email => "foo#bar.com"})
u.update({:name => "new name",
:email_address_attributes => {:email => "foo#bar.com"}})
How do I get it to update the name while not caring about the email_address. Which hasn't changed?
Some other notes and code:
I do index my email_address on email and I'm using rails 4.
class User < ActiveRecord::Base
belongs_to :email_address
validates :email_address, :presence => true
accepts_nested_attributes_for :email_address
end
class EmailAddress < ActiveRecord::Base
validates_format_of :email, :with => RFC822::EmailAddress
validates :email, :presence => true
has_one :user
end
When you update your email_address_attributes in this way, you're actually adding a new email_address object for your user. You need to pass the email address's id as an attribute, i.e.:
u.update({:name => "new name",
:email_address_attributes => {:id => u.email_address.id, :email => "foo#bar.com"}})
Or alternatively, you can update the user's email address in a different update statement
u.update({:name => "new name"})
u.email_address.update({:email => "foo#bar.com"})
As for your controller, all you need to do is add the email addresses's :id field as a permitted parameter.
def user_params
params.require(:user).permit(:name, email_address_attributes: [:id, :email])
end
There is more information about Strong Parameters in the Strong Parameters Rails Guide. Check out the More Example section for a setup similar to yours.
If you don't want to validate the e-mail address except on create, you can add that to the validation:
validates :email_address, presence: true, on: :create
Use ":update_only" option in "accepts_nested_attributes_for" like this:
accepts_nested_attributes_for :email_address, :update_only => true
This way active record will update the child record if it already exists, instead of creating a new one. This should take care of the unique constraint.

accessing data on a Model's associated Model to perform validations

class User < ActiveRecord::Base
belongs_to :school
validates :email, :email => { :message => "Must be a valid email." }, :format => { :with => /\A[\w+\-.]+##{Regexp.quote(school.email_domain)}\z/i }
end
I want to be able to validate that on create a user's email matches their school's email domain. I am creating users by:
#school.users.create(params[:user])
Error thrown :
undefined local variable or method `school' for #<Class:0x007f8aaabb0df0>
Thanks for the help!
You're getting that error because you're trying to call the #school method within the context of your class instead of within an instance of your class, and #school is an instance method.
To call instance methods when constructing your validation format Regexp, you can provide a lambda as the :with option, as follows:
validates :email,
:message => "Must be a valid email",
:format => { :with => lambda {|user| /\A[\w+\-.]+##{Regexp.quote(user.school.email_domain)}\z/i } }
This lambda will be invoked on your model instance, allowing you to call methods on your User instance such as #school. See the documentation for validates_format_of for more details.

Different types of validations in rails

I have a User model
is there a difference between
class User < ActiveRecord::Base
validates :name, :presence => true
end
and
class User < ActiveRecord::Base
def validate
errors.add_to_base "name should not be nil" if name.nil?
end
end
The validates macro is more flexible, as it also allows you to do things like:
validates :name, :format => { :with => /\A[a-zA-Z]+\z/,
:message => "Only letters allowed" }, :length => { :in => 6..20 }
The validate method is really a quick and easy way to do custom validations when existing ones do not exist. (When custom validations get too complex, then you should usually move them into custom validators and use the validates macro).
See more at http://guides.rubyonrails.org/active_record_validations_callbacks.html
Yes -- the first will fail to save an empty string, whereas the second will allow it.

Rails: sending emails, getting undefined method `model_name' for Mail::Message:Class

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.

Resources