Custom Uniqueness validator for hstore Postgres - ruby-on-rails

I would like to perform a uniqueness validation on a hstore field.
When I set is as:
class User
store_accessor :attributes, :foo_attr
validates :foo_attr, uniqueness: true
end
I get undefined method 'limit' for nil:NilClass
In a Rails issue store_accessor and uniqueness validation? user al2o3cr explains:
validates_uniqueness_of is not going to work in this case - it's
expecting a database column named stripe_id. With an Hstore column
it's technically possible to perform the required query, but the
resulting SQL is only applicable to that storage format and only works
on Postgres.
In your case, a custom subclass of
ActiveRecord::Validations::UniquenessValidator with an overridden
build_relation would probably be a better choice.
How would you go around creating that custom validator?
I already have a database level uniqueness set up as explained in Race condition using Postgres hstore all I need now is to make valid? return false on the same foo_attr.

If you're going to the trouble of setting up a custom hstore index and writing a custom validation, my first instinct is that maybe you want foo_attr to be a column of its own.
As for a custom validation, it's pretty straightforward:
validate :foo_attr_uniqueness
def foo_attr_uniqueness
if self.class.where(foo_attr: foo_attr) # Same foo_attr
.where.not(id: id) # On a different record
.exists?
errors.add(:foo_attr, 'must be unique')
end
end

Related

Is there a better way of validating a non model field in rails

I have a form field in ROR 4 app called as 'measure'. It is not a database column, but its values will help model create child entries of its own via acts_as_tree : https://github.com/rails/acts_as_tree
I have to throw a validation when 'measure' is invalid. So I have created a virtual attribute known as measure and check for its validations only on a certain condition.
model someModel
attr_accessor :measure
validates_presence_of :measure, :if => condition?
Problem is when I am saving the code, I am thrown a validation which is fine. I am also thrown the same validation when I am trying to update the record in some other method of the model. The only way to surpass that is by writing this code:
# I do not want to do this, is there a better way?
self.measure = "someRandomvalue"
self.save
I am making this as virtual attribute only for throwing validations. Is there a better way of throwing validations? The form has other validations, I do not want the error for this validations to be shown differently just because it is not an attribute.
I want it to validated only when active record is saved via create and update action of the controller and not when it is being updated by some random method of model.
I have seen other developers in my team doing similar thing and was always curious about one thing - "What are you trying to achieve doing things the way you are doing?". You see, I am not sure if validators should be used for values that will not be serialized.
Anyways, you may try using format validator instead of presence, which worked in my team's case:
# Rails 3/4
validates :measure, format: { with: /^.+$/, allow_nil: true }
# Rails 2
validates_format_of :measure, :with => /^.+$/, :allow_nil => true
You may also try using allow_blank instead of allow_nil.
I would rather create a custom validator along the lines of validates_accessor_of for values that I know will never be serialized.
HTH

How to validate unique pairs while being case insensitive

I have a model for Guilds that have a "Name" and "Realm" attribute
I want to make it so that the "Name" and "Realm" pairings are unique regardless of case. Enforcing the case sensitivity is the only issue I have at them moment.
here's what I've got so far:
validates_uniqueness_of :name, scope: :realm, case_sensitive: false
the [case_sensitive: false] part works but only for the first attribute(Name)
I'm currently using Rails 4.1.8, SQLite for developemnt and PG for Production
I really appreciate any help. Thanks!
It's my understanding that this isn't supported. The case_sensitive option applies to the column being validated and the scope option applies only to columns in the same table.
You could try using PG's lower like so scope: "lower(realm)" but I suspect it'll fail when trying to access the column table_name.lower(realm)
What you need to do is replace that validator with a custom one.
class Model
validate :your_validator
private
def your_validator
if Model.
where("LOWER(name) = ? AND LOWER(realm) = ?",
name.downcase, realm.downcase).
exists?
# add errors here
end
end
end

Can validate_uniqueness_of work with custom scopes?

I'm working on an RoR project and I'd like to have a uniqueness validation on one of my models that checks against a custom scope:
class Keyword < ActiveRecord::Base
belongs_to :keyword_list
scope :active, -> { where("expiration > ?", DateTime.now) }
validates :name, uniqueness: { scope: [:active, :keyword_list_id] }
end
Only, this doesn't work. It checks the database for an active column, which doesn't exist and throws this error:
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column keywords.active does not exist
So, my question is there any way to make this work, or do I have to write a custom validator? And if so, are there any tips on what it should look like to hitting the database too much?
No, you will have to write a custom validation.
Try this.
# In app/models/keyword.rb
validate :freshness
private
def freshness
if Keyword.active.find_by(name: self.name, keyword_list_id: self.keyword_list_id)
errors.add(:base, "Name is not fresh enough.") # Fails the validation with error message
end
end
Here's another interesting point, you cannot rely on validates_uniqueness_of, or any other uniqueness validator in rails, because validations are not run atomically, meaning that if two identical records are inserted at the same time, and there is no SQL constraint validating uniqueness, the ruby validations will pass and both records will be inserted.
What I'm trying to say here is that if your validations are mission-critical, use a SQL constraint.

Rails object.valid? with arguments

Is it possible to pass :symbols to the valid? method so that I can define if the object is valid up to a certain point?
Eg. if I have an object Person and want to call Person.valid?(:basic_info) and it will return true only if a certain subset of fields (say name & gender) are present?
I saw something that I thought might be of use but cannot get it working, it's conditional validations http://guides.rubyonrails.org/active_record_validations_callbacks.html#conditional-validation , in particular grouping conditional validations, but I couldn't get it working...
Can anyone help me out here please...
I don't think there already present like this however you can write a method on your own like following
def is_valid_field?(field)
self.valid?
self.errors[field].blank?
end
and then just person.is_valid_field?(:basic_info)
To validate basic_info you'll have to define a custom validator:
class Person < ActiveRecord::Base
validate :basic_info_present
def basic_info_present
if name.blank? || gender.blank?
errors.add(:basic_info, "can't be in blank")
end
end
end
If you then want to see if there are errors on the specific field, you can use #Salil's approach.
Note however that since there is no actual attribute called basic_info in your model, the validation errors here will not come up in forms, etc. (although they will be in the errors hash). That may or may not be what you want.
I got this to work using conditional validations, so now i can use .valid?(:basic) say for when i only want to check that the person has a name with the call...
validates_presence_of :name, :when => [:basic]
Documentation here: http://apidock.com/rails/ActiveRecord/Validations/valid%3F
This way I can have the object return true when calling .valid? even when it doesn't have a name, good times...

How do I define synthetic attributes for an ActiveRecord model?

I have an ActiveRecord model whose fields mostly come from the database. There are additional attributes, which come from a nested serialised blob of stuff. This has been done so that I can use these attributes from forms without having to jump through hoops (or so I thought in the beginning, anyway) while allowing forwards and backwards compatibility without having to write complicated migrations.
Basically I am doing this:
class Licence < ActiveRecord::Base
attr_accessor :load_worker_count
strip_attributes!
validates_numericality_of :load_worker_count,
:greater_than => 2, :allow_nil => true, :allow_blank => true
before_save :serialise_fields_into_properties
def serialise_fields_into_properties
...
end
def after_initialize
...
end
...
end
The problem I noticed was that I can't get empty values in :load_worker_count to be accepted by the validator, because:
If I omit :allow_blank, it fails validation complaining about it being blank
If I put in :allow_blank, it converts the blank to 0, which when fails on the :greater_than => 2
In tracking down why these blank values are getting to the validation stage in the first place, I discovered the root of the problem: strip_attributes! only affects actual attributes, as returned by the attributes method. So the values which should be nil at time of validation are not. So it feels like the root cause is that the synthetic attributes I added in aren't seen when setting which attributes to strip, so therefore I ask:
Is there a proper way of creating synthetic attributes which are recognised as proper attributes by other code which integrates with ActiveRecord?
I assume you are talking of the strip_attributes plugin; looking at the code, it uses the method attributes, defined in active_record/base.rb, which uses #attributes, which is initialized (in initialize) as #attributes = attributes_from_column_definition.
Maybe it's possible to hack ActiveRecord::Base somehow, but it would be a hard work: #attributes is also used when getting/putting stuff from/to db, so you would have to do a lot of hacking.
There's a much simpler solution:
before_validate :serialise_fields_into_properties
...
def serialise_fields_into_properties
if load_worker_count.respond_to? :strip
load_worker_count = load_worker_count.blank? ? nil : load_worker_count.strip
end
...
end
After all, this is what strip_attributes! does.
Wouldn't it be easier to just use Rails' serialize macro here?
class License < ActiveRecord::Base
serialize :special_attributes
end
Now you can assign a hash or array or whatever you need to special_attributes and Rails will serialize it a text field in the database.
license = License.new
license.special_attributes = { :beer => true, :water => false }
This will keep your code clean and you don't have to worry about serializing/deserializing attributes yourself.

Resources