I would like to validate attributes in a function like this
class User < ActiveRecord::Base
validate :check_name( :name )
def check_name( name )
... if name is invalid ...
self.errors.add( :name, 'Name is invalid')
end
end
Can you please write the right code?
Please explain the functionality why...
THX!
class User < ActiveRecord::Base
validate :check_name
def check_name
if name # is invalid ...
self.errors.add(:name, 'Name is invalid')
end
end
end
You can use the validate macro but the method can't accept parameters.
You need to fetch the attribute value from inside the method, then validate it.
Replace
if name # is invalid ...
with your own validation logic.
Related
In an API controller, I'd like to limit what fields of a model can be seen depending on who is logged in. ActiveModel Serializers would seem to allow this, but I've had no luck with the following:
class MyModelSerializer < ActiveModel::Serializer
attributes :name, :custom_field, :secret_field
has_many :linked_records
def custom_field
object.do_something
end
def filter(keys)
unless scope.is_admin?
keys.delete :secret_field
keys.delete :linked_records
end
keys
end
end
But, the filtering is never performed and so my output always contains :secret_field and :linked_records even if there's no user logged in.
Perhaps this is because I am using Rails 6, and it would seem that ActiveModel Serializers might no longer be the best tool (e.g. https://stevenyue.com/blogs/migrating-active-model-serializers-to-jserializer).
Please do offer your suggestions for a means to perform this, if you can think of a better means.
EDIT:
Further to all the comments below, here's some different code:
attributes :name, :id, :admin_only_field, :is_admin
$admin_only = %i[:id, :admin_only_field]
def attributes(*args)
hash = super
$admin_only.each do |key|
unless scope.is_admin?
hash.delete(key)
end
end
hash
end
def is_admin
if scope.is_admin?
'admin!'
else
'not an admin!'
end
end
If I then visit the model's index page without being an admin I see that the admin_only_field and id are both present, and is_admin says that I'm not. Bizarre.
class MyModelSerializer < ActiveModel::Serializer
attributes :name, :custom_field, :secret_field
has_many :linked_records
def custom_field
object.do_something
end
private
def attributes
hash = super
unless scope.is_admin?
hash.delete :secret_field
hash.delete :linked_records
end
hash
end
end
Sometimes I need not to simply validate smth in my app, but also alter it before/after validating.
I.e.
class Channel < ActiveRecord::Base
validate :validate_url
...
private
def validate_url
url = "rtmp://#{url}" if server_url[0..6] != "rtmp://" #alter cause need this prefix
unless /rtmp:\/\/[a-z0-9]{1,3}\.pscp\.tv:80\/[a-z0-9]\/[a-z0-9]{1,3}\//.match url
errors.add(:url, "...")
end
end
end
or smth like this
class Channel < ActiveRecord::Base
validate :validate_restreams
...
private
def validate_restreams
self.left_restreams = self.left_restreams - self.restreams #to be sure there's no intersections
end
end
But I feel it's not a right place for such things, so I need to know what's the way to do it right?
You can create a custom validator for a rails model. You should make a class, inherit it from ActiveModel::Validator, and define a validate(record) method there, which will add errors to the record. For example:
This is your validator class:
class MyValidator < ActiveModel::Validator
def validate(record)
unless url_valid?(record[:url])
record.errors.add(:url, 'is invalid')
end
end
private
def url_valid?(url)
# validate url and return bool
end
end
And now simply add this to the model:
validates_with MyValidator
I have a after_save callback in a model named Field and i am creating dynamic instance methods in it on other model named User, but the code is not working, i am unable to figure out whats wrong with it, as the logic is very simple.Please help.
class field < ActiveRecord::Base
after_create :create_user_methods
private
def create_user_methods
User.class_eval do
define_method(self.name) do
#some code
end
define_method(self.name + "=") do
#some code
end
end
end
end
and then I am creating Field instance in rails console like this
Field.create(name: "test_method")
And then calling that method on User class instance like this
User.new.test_method
But it raises error
undefined method test_method for ....
I got the fix, I can not use self inside the class_eval block as its value is User model not Field class object, therefore the fix is:
class field < ActiveRecord::Base
after_create :create_user_methods
private
def create_user_methods
name = self.name # here self points to field object
User.class_eval do
define_method(name) do
#some code
end
define_method(name + "=") do
#some code
end
end
end
end
For some reason #store object with three attributes won't assign the third value(generated token string).The model has user_id, product_id and token_string.But when it comes to token_generate function nothing is assigned,and end up with nil in the token_string column in the database .Product_id and user_id are saved perfectly in the database though.What is going on here?Thank you in advance.
class Store < ActiveRecord::Base
require 'digest/sha1'
attr_accessor :token_string
before_save :token_generate
def save_with_payment
#Until here the object has user_id and product_id attribute values
save!
end
private
def token_generate
self.token_string = Digest::SHA1.hexdigest("random string")
end
end
controller
def create
#store=Store.new(params[:store])
if #store.save_with_payment
redirect_to :controller=>"products",:action=>"index"
else
redirect_to :action=>"new"
end
end
#SrdjanPejic is correct, try removing the attr_accessor :token_string line which is likely blocking setting the :attributes hash value needed for the INSERT statement.
I am using Rails v2.3
If I have a model:
class car < ActiveRecord::Base
validate :method_1, :method_2, :method_3
...
# custom validation methods
def method_1
...
end
def method_2
...
end
def method_3
...
end
end
As you see above, I have 3 custom validation methods, and I use them for model validation.
If I have another method in this model class which save an new instance of the model like following:
# "flag" here is NOT a DB based attribute
def save_special_car flag
new_car=Car.new(...)
new_car.save #how to skip validation method_2 if flag==true
end
I would like to skip the validation of method_2 in this particular method for saving new car, how to skip the certain validation method?
Update your model to this
class Car < ActiveRecord::Base
# depending on how you deal with mass-assignment
# protection in newer Rails versions,
# you might want to uncomment this line
#
# attr_accessible :skip_method_2
attr_accessor :skip_method_2
validate :method_1, :method_3
validate :method_2, unless: :skip_method_2
private # encapsulation is cool, so we are cool
# custom validation methods
def method_1
# ...
end
def method_2
# ...
end
def method_3
# ...
end
end
Then in your controller put:
def save_special_car
new_car=Car.new(skip_method_2: true)
new_car.save
end
If you're getting :flag via params variable in your controller, you can use
def save_special_car
new_car=Car.new(skip_method_2: params[:flag].present?)
new_car.save
end
The basic usage of conditional validation is:
class Car < ActiveRecord::Base
validate :method_1
validate :method_2, :if => :perform_validation?
validate :method_3, :unless => :skip_validation?
def perform_validation?
# check some condition
end
def skip_validation?
# check some condition
end
# ... actual validation methods omitted
end
Check out the docs for more details.
Adjusting it to your screnario:
class Car < ActiveRecord::Base
validate :method_1, :method_3
validate :method_2, :unless => :flag?
attr_accessor :flag
def flag?
#flag
end
# ... actual validation methods omitted
end
car = Car.new(...)
car.flag = true
car.save
Another technique, which applies more to a migration script than application code, is to redefine the validation method to not do anything:
def save_special_car
new_car=Car.new
new_car.define_singleton_method(:method_2) {}
new_car.save
end
#method_2 is now redefined to do nothing on the instance new_car.
Use block in your validation something like :
validates_presence_of :your_field, :if => lambda{|e| e.your_flag ...your condition}
Depending on weather flag is true of false, use the method save(false) to skip validation.