I'm having this model:
class Vote
include Mongoid::Document
include Mongoid::Timestamps
field :vote, :type=>Integer
embedded_in :voteable, :inverse_of => :votes
referenced_in :user
attr_accessible :vote, :user, :voteable
validates :vote,:inclusion => [-1, 1]
validates :user ,:presence=> true,:uniqueness=>true
end
The problem is that the validation for user uniqueness per vote is not working, and the same user can create several votes, which is not what I want. Any ideas how to solve that?
Looks like this is a known problem.
http://groups.google.com/group/mongoid/browse_thread/thread/e319b50d87327292/14ab7fe39337418a?lnk=gst&q=validates#14ab7fe39337418a
https://github.com/mongoid/mongoid/issuesearch?state=open&q=validates#issue/373
It is possible to write a custom validation to enforce uniqueness. Here is a quick test:
class UserUniquenessValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors[attribute] << "value #{value} is not unique" unless is_unique_within_votes(record, attribute, value)
end
def is_unique_within_votes(vote, attribute, value)
vote.voteable.votes.each do |sibling|
return false if sibling != vote && vote.user == sibling.user
end
true
end
end
class Vote
...
validates :user ,:presence => true, :user_uniqueness => true
end
Related
My Page model look like this:
class Page < ActiveRecord::Base
has_many :blocks
accepts_nested_attributes_for :blocks, allow_destroy: true
rails_admin do
edit do
fields :title, :slug, :blocks
end
end
end
My Block model look like this:
class Block < ActiveRecord::Base
belongs_to :page
rails_admin do
edit do
field :title
field :body, :ck_editor
end
end
end
I needed workflow like this:
As an admin I click create page and I should see opened new block section with prefield title.
How can I create this scenario?
My own answer is realy dearty, but it works for me:
class Page < ActiveRecord::Base
has_many :blocks
accepts_nested_attributes_for :blocks, allow_destroy: true
rails_admin do
edit do
fields :title, :slug
field :blocks do
# It is needed to show nested form
active true
end
end
end
# It is needed to create default block with title "main"
after_initialize do
if self.blocks.empty? && self.new_record?
self.blocks << Block.new(title: 'main')
end
end
# It is needed to prevent create default block when form has errors
after_validation do
return if(self.persisted? || self.blocks.empty?)
destroy_array = []
self.blocks.each do |block|
destroy_array << block if block.title == 'main' && block.body.nil?
end
self.blocks.destroy(destroy_array)
end
end
I want to include errors from a rather deep assocation in a parent:
class Order < ActiveRecord::Base
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :product, polymorphic: true
end
class Site < ActiveRecord::Base
has_one :line_item, as: :product, autosave: true
validates :domain, presence: true
end
Used as:
product = Site.new(domain: nil)
order = Order.new
order.line_items << LineItem.new(product: product)
order.valid? #=> false
product.valid? #=> false
product.errors? #=> { 'domain' => 'cannot be blank' }
Is there some rails way, or association-parameter to make the errors
bubble up so that I get:
order.errors #=> { 'domain' => 'cannot be blank' }
In other words, that the Order, the top of the association,
transparently proxies the validation errors from its children?
I am aware of using simple before_validation hooks, like so:
class Order < ActiveRecord::Base
before_validation :add_errors_from_line_items
private
def add_errors_from_line_items
self.line_items.each do |line_item|
line_item.product.errors.each do |field, message|
errors.add(field, message)
end unless line_item.product.valid?
end
end
end
end
But I am wondering if there is not some ActiveRecord feature that I am overlooking.
This is my model class:
class Availability < ActiveRecord::Base
attr_accessible :beginning_date, :end_date
validates :beginning_date, :end_date :presence => true
# custom validators
validate :dates_cant_be_in_the_past
def dates_cant_be_in_the_past
if Date.parse(beginning_date) < Date.today
errors.add(:beginning_date, "cant be in the past")
end
if Date.parse(end_date) < Date.today
errors.add(:end_date, "cant be in the past")
end
end
end
Now two things should happen: At first validate the presence of the beginning_date and end_date attributes and than run my dates_cant_be_in_the_pastvalidator.
Sadly this approach doesn't work. If I leave a field empty the Date.parsemethod throws an exception, because the argument is obviously empty.
Is it possible to define the order of default and custom validations? Or do I have to implement the presence validator myself, so I would do something like:
validate :dates_cant_be_blank, :dates_cant_be_in_the_past
The guide at least says:
You can pass more than one symbol for each class method and the respective validations will be run in the same order as they were registered.
Thank in advance
It's much simpler if you create a validator for that:
class DateValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if Date.parse(value) < Date.today
record.errors.add(attribute, "cant be in the past")
end
end
end
And at your model you would use it like this:
class Availability < ActiveRecord::Base
attr_accessible :beginning_date, :end_date
validates :beginning_date, :end_date :presence => true
validates :beginning_date, :end_date, :date => true, :allow_blank => true
end
The :allow_blank piece if the one prevents the validation from running if the value is empty. Using a real validator object also removes the code form your model making it much simpler and removing the duplication you currently have.
You could try something like this
class Availability < ActiveRecord::Base
attr_accessible :beginning_date, :end_date
validates :beginning_date, :end_date : presence => true
# custom validators
validate :valid_dates
def valid_dates
if valid_string(beginning_date)
errors.add(:beginning_date, "Can't be in the past") unless Date.parse(beginning_date) > Date.today
end
if valid_string(end_date)
errors.add(:end_date, "Can't be in the past") unless Date.parse(end_date) > Date.today
end
end
def valid_string(test_value)
test.value.is_a? String
end
end
I am new to coding - and have not enough reputation to comment this answer:
Rails 3: Uniqueness validation for nested fields_for
So I am creating this question as "Part 2" :)
I am a web designer but curious to learn coding, held with this from my days.
# app/validators/nested_attributes_uniqueness_validator.rb
class NestedAttributesUniquenessValidator < ActiveModel::EachValidator
record.errors[attribute] << "Products names must be unique" unless value.map(&:name).uniq.size == value.size
end
end
above code with "ActiveModel::EachValidator" throw this error:
"undefined method `map' for "Area 1":String"
# app/validators/nested_attributes_uniqueness_validator.rb
class NestedAttributesUniquenessValidator < ActiveModel::Validator
record.errors[attribute] << "Products names must be unique" unless value.map(&:name).uniq.size == value.size
end
end
above code with "ActiveModel::Validator" throw this error:
"Subclasses must implement a validate(record) method. "
this is model file:
class Area < ActiveRecord::Base
validates :name,
:presence => true,
:uniqueness => {:scope => :city_id},
:nested_attributes_uniqueness => {:field => :name}
belongs_to :city
end
You can find complete code over here:
https://github.com/syed-haroon/rose
#Syed: I think you are trying to do this. else reply to my comment.
# app/models/city.rb
class City < ActiveRecord::Base
has_many :areas
validates :areas, :area_name_uniqueness => true
end
# app/models/area.rb
class Area < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
end
# config/initializers/area_name_uniqueness_validator.rb
class AreaNameUniquenessValidator < ActiveModel::Validator
def validate_each(record, attribute, value)
record.errors[attribute] << "Area names must be unique" unless value.map(&:name).uniq.size == value.size
end
end
I found the answer over here :
https://rails.lighthouseapp.com/projects/8994/tickets/2160-nested_attributes-validates_uniqueness_of-fails
&
validates_uniqueness_of in destroyed nested model rails
This is for rails 2, one line need to me modified over here:
add_to_base has been deprecated and is unavailable in 3.1. Use self.errors.add(:base, message)
Has "def validate" been taken out in Rails 3.1? I'm on Rails 3.1 pre and it doesn't seem to be working
class Category < ActiveRecord::Base
validates_presence_of :title
private
def validate
errors.add(:description, "is too short") if (description.size < 200)
end
end
The "title" validation works but the "description" validation doesn't.
Does something like this work for you?
class Category < ActiveRecord::Base
validates_presence_of :title
validate :description_length
def description_length
errors.add(:description, "is too short") if (description.size < 200)
end
end
class Category < ActiveRecord::Base
validates_presence_of :title
private
validate do
errors.add(:description, "is too short") if (description.size < 200)
end
end
For other types of validations, you can also add 'Validators' like the one listed here:
http://edgeguides.rubyonrails.org/3_0_release_notes.html#validations
class TitleValidator < ActiveModel::EachValidator
Titles = ['Mr.', 'Mrs.', 'Dr.']
def validate_each(record, attribute, value)
unless Titles.include?(value)
record.errors[attribute] << 'must be a valid title'
end
end
end
class Person
include ActiveModel::Validations
attr_accessor :title
validates :title, :presence => true, :title => true
end
# Or for Active Record
class Person < ActiveRecord::Base
validates :title, :presence => true, :title => true
end