Spec Errors validation With RSpec + CouchRest_Model - ruby-on-rails

I trying to spec validations of my model this is my model
class Account < CouchRest::Model::Base
property :user, String
property :password, String
property :name, String
property :email, String
property :activate, TrueClass, :default => true
validates_presence_of :user, :password, :name, :email
validates_length_of :password, :in => 6..15, :if => lambda{|account| !account.password.blank? }
validates_uniqueness_of :user, :if => (lambda{|account| !account.user.blank?})
end
and into my model_spec i'm trying to do this
account = Account.new
account.should have(1).error_on_presence_of(:email)
But instead 1 error, I'm getting 6
I think that might be caused by validates of couchrest but not sure.
Can someone clarify this for me please?
P.S.: If I validate the same model into console I get 4 errors corresponding the four empty properties

It seems you're expecting 1 error, but there are actually 6. To figure out what is in the error hash, you can set a temporary expectation:
`account.errors.should == {}
Then the example will fail and RSpec will print the value of the error hash, and you can see which errors are actually being generated.

Related

Pass validation context to associated model

I'm using contexts to invoke specific validations at different points in the model lifecycle:
model Address
validates :city, presence: true
validates :street, presence: true, on: :send_letter
end
incomplete_address = Address.new(city: 'Berlin')
incomplete_address.valid? # => true
incomplete_address.valid?(:send_letter) # => false
This works fine for the simple case above. But, as far as I can tell, the context is ignored for any associated objects:
model Address
belongs_to :country
validates :street, presence: true, on: :send_letter
validates_associated :country
end
model Country
has_many :addresses
validates :iso_alpha_3, presence: true, size: 3, on: :send_letter
end
incomplete_address = Address.new(street: 'Oranienstr', country: Country.new(name: 'Germany', iso_alpha_3: 'Invalid iso code')
incomplete_address.valid? # => true
incomplete_address.valid?(:send_letter) # => true
incomplete_address.country.valid?(:send_letter) => false
Question: Is this expected behaviour, or is it a bug I'm hitting? Or am I making a conceptual mistake? What's the most elegant way to validate associated models under such circumstances?
I know this question is 3 years old but there is a slightly easier path now and an an even easier option on the horizon.
There is currently an outstanding PR that adds this functionality via a configuration option on the validates_associated call. In the meantime, you can add that version of AssociatedValidator as a separate validator (e.g. AssociatedPreservingContextValidator) and call validate_with AssociatedPreservingContextValidator, :country in `Address.
Is the expected behaviour, validators are executed only in the current model.
You can achieve the desired result using a custom method validator.
You can find more informations here

Rails validate uniqueness only if conditional

I have a Question class:
class Question < ActiveRecord::Base
attr_accessible :user_id, :created_on
validates_uniqueness_of :created_on, :scope => :user_id
end
A given user can only create a single question per day, so I want to force uniqueness in the database via a unique index and the Question class via validates_uniqueness_of.
The trouble I'm running into is that I only want that constraint for non-admin users. So admins can create as many questions per day as they want. Any ideas for how to achieve that elegantly?
You can make a validation conditional by passing either a simple string of Ruby to be executed, a Proc, or a method name as a symbol as a value to either :if or :unless in the options for your validation. Here are some examples:
Prior to Rails version 5.2 you could pass a string:
# using a string:
validates :name, uniqueness: true, if: 'name.present?'
From 5.2 onwards, strings are no longer supported, leaving you the following options:
# using a Proc:
validates :email, presence: true, if: Proc.new { |user| user.approved? }
# using a Lambda (a type of proc ... and a good replacement for deprecated strings):
validates :email, presence: true, if: -> { name.present? }
# using a symbol to call a method:
validates :address, presence: true, if: :some_complex_condition
def some_complex_condition
true # do your checking and return true or false
end
In your case, you could do something like this:
class Question < ActiveRecord::Base
attr_accessible :user_id, :created_on
validates_uniqueness_of :created_on, :scope => :user_id, unless: Proc.new { |question| question.user.is_admin? }
end
Have a look at the conditional validation section on the rails guides for more details: http://edgeguides.rubyonrails.org/active_record_validations.html#conditional-validation
The only way I know of to guarantee uniqueness is through the database (e.g. a unique index). All Rails-only based approaches involve race conditions. Given your constraints, I would think the easiest thing would be to establish a separate, uniquely indexed column containing a combination of the day and user id which you'd leave null for admins.
As for validates_uniqueness_of, you can restrict validation to non-admins through use of an if or unless option, as discussed in http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of
Just add a condition to the validates_uniqueness_of call.
validates_uniqueness_of :created_on, scope: :user_id, unless: :has_posted?
def has_posted
exists.where(user_id: user_id).where("created_at >= ?", Time.zone.now.beginning_of_day)
end
But even better, just create a custom validation:
validate :has_not_posted
def has_not_posted
posted = exists.where(user: user).where("DATE(created_at) = DATE(?)", Time.now)
errors.add(:base, "Error message") if posted
end

Form validation - if something is false, require this validation

In my form validation of my model, I'm trying to say that if the params of a column called :virtual is false, then the :location field should validate for :presence => true.
My current code is:
validates :location, if :virtual => false, :presence => true
But that's giving me a syntax error. What's the correct way to format this?
Something like:
attr_accessor :virtual # sets up a "virtual attribute" called "virtual" to which you can read/write a value
# this step isn't necessary if you already have an attribute on the model called "virtual"
validates :location, :presence => true, :unless => :virtual?
The use of virtual? should check whether the attribute virtual is true or false. Using unless means this validation is only performed if virtual is false (or is a value that is considered false).
More detail on virtual attributes and validation: Rails: Using form fields that are unassociated with a model in validations
validates :location, presence: true, if: Proc.new { |p| p.virtual == false }

undefined method 'delete' for String:Class when using Mongoid and creating records via console

I have a class that looks like this:
class SearchService
include Mongoid::Document
key :name, String
field :url, String
field :searchBaseUrl, String
validates_presence_of :name, :url, :searchBaseUrl
validates_uniqueness_of :name
end
The first issue here is that I was using validates_presence_of incorrectly or so it would seem. I commented the line out and I could create the class instance at the console with no problem. But when I tried to create the object with validates_presence_of in place I got an error:
NoMethodError: undefined method `delete' for String:Class
from D:/Ruby193/lib/ruby/gems/1.9.1/gems/mongoid-2.3.3/lib/mongoid/fields.rb:230:in `add_field'
from D:/Ruby193/lib/ruby/gems/1.9.1/gems/mongoid-2.3.3/lib/mongoid/fields.rb:145:in `field'
Is this error message meaningful? I'm having a hard time seeing the relationship between a missing method on the String class and me using validates_presence_of incorrectly. If I could pick only one thing about Ruby that bugs me it would have to be terrible error messages.
The rails docs say that validates_presence_of is for association. But the mongo db page says that it's used for required fields. Are the mongodb docs incorrect?
I'm trying to decide if this is an issue I should report to the mongoid team or if I'm just not understanding how the language works.
Update: OK now I'm getting a very similar error in a class that doesn't use validates_presence_of at all. So it's some other issue (even though commenting out that line fixed it initially).
Update2: This appears to me to be bug in mongoid. Changing this line:
field :name
to
field :name, String
will cause the error to show up. Seems that any field that is defined with a type will mess up mongoid. Or those aren't supposed to be there anymore? The mongodb docs (linked above) have code that looks like key :votes, Integer, :default => 0 so if it isn't valid the docs are wrong.
Rails 3.1.1, Ruby 1.9.3
You use incorrect syntax for field type definition. It should use options hash and key "type". Example from from documentation http://mongoid.org/
class Artist
include Mongoid::Document
field :name, type: String
embeds_many :instruments
end
You refer to MongoMapper examples, but use Mongoid ))
jcollum, the problem is coming from the fact that you are using mongoid but the examples on mongodb.org are using mongomapper.
Please be aware that these are different ODMs for rails and have slightly different syntax for defining fields.
Please see the documentation here for mongoid.
The format for the fields using mongoid:
class Person
include Mongoid::Document
field :first_name, type: String
field :middle_name, type: String
field :last_name, type: String
end
In Rails 3, you should be using the new style validations:
validates :name, :presence => true, :uniqueness => true
validates :url, :presence => true
validates :searchBaseUrl, :presence => true
Use with_options to make it DRYer
with_options :presence => true do |v|
v.validates :name, :uniqueness => true
v.validates :url
v.validates :searchBaseUrl
end

Rails: Why "format" (regex) validation fails?

I have the following validation of product's price:
class Product < ActiveRecord::Base
...
PRICE_REGEX = /^([1-9]\d{0,5}|0)(\.\d{1,2})?$/
validates :price, :presence => true, :format => PRICE_REGEX
...
end
It supposed to allow prices from 0 to 999999.99.
However, if I enter hello, the validation passes, and 0.00 is saved in the database.
The :presence validation works fine, though.
What am I missing here ?
The price column is a float, and so Rails will automatically convert the string "hello" to float, as per "hello".to_f # => 0.0. This is then converted back to the string "0.0", which obviously matches the regular expression.
In general, it's a bad idea to use regular expressions on non-string columns. Instead, use validates_numericality_of. If you wanted the same restriction as with the regex, do it like this:
class Product < ActiveRecord::Base
validates_numericality_of :price, :greater_than => 0, :less_than => 1000000
end
Not only is it safer, but it's easier to read and follow as well. Note that it'll automatically reject the price if blank as well.
I haven't tested this, but every description I saw from validates uses :format in this way:
validates :price, :presence => true, :format => { :with => PRICE_REGEX }
Maybe the problem is there

Resources