Validating uniqueness without database in model specs - ruby-on-rails-3.1

I have a model that looks something like this:
class User
include Mongoid::Document
field :email
validate :email, presence: true, uniqueness: true
end
And I have a test that looks like...
it { User.new.should_not be_valid }
it { FactoryGirl.build(:user).should be_valid }
it { should validate_presence_of :email }
it { should validate_uniqueness_of :email }
Both of these want to hit the database when the .valid? method is called. Is there someway I can abstract that out? The uniqueness validator has been tested thoroughly by plenty of other folks, so the last line above is good enough for me.
Not that big a deal if I must run a DB during model specs, but I'd rather avoid it if possible.

The .valid? method should still work with ActiveModel, I am not quite sure what Mongoid includes but ActiveModel you must have include ActiveModel:Validations
This may not have been clear but ActiveModel does not try and hit the database.

Related

Is there a way to serialize nested attributes outside of the parent with ActiveModel Serializer?

I upgraded activemodel serializer and now the output is different than what it once was. I'm trying to get the JSON output to match what it previously was. Specifically, I'd like an objects nested attributes to be on the same level as the main object.
For example, let's say my serializer is as follows:
class DishesSerializer < ActiveModel::Serializer
has_many :ingredients
end
This would be the current output:
{
"dish": {
"dish_stuff_1": "dish_stuff_1",
"dish_stuff_2": "dish_stuff_2",
"dish_stuff_3": "dish_stuff_3",
"ingredients": {
"ingredients_stuff_1": "ingredients_stuff_1"
}
}
}
And what I'd like is something like this:
{
"dish": {
"dish_stuff_1": "dish_stuff_1",
"dish_stuff_2": "dish_stuff_2",
"dish_stuff_3": "dish_stuff_3"
}
"ingredients": {
"ingredients_stuff_1": "ingredients_stuff_1"
}
}
I am currently doing this in the controller using multiple serializers, but it takes some additional querying and feels wrong. I feel like there should be some hacky way to do it in AMS.
I tried something like this:
def attributes
hash = super
hash.merge!(:dishes => dishes)
end
but that ends up in the same layer.
Any help would be greatly appreciated.
In this case, can't you just create a new serializer that has all the data you need - say a menu serializer:
class MenuSerializer < ActiveModel::Serializer
has_one :dish
has_many :ingredients
end
Not sure having a serializer that returns 2 root elements is a good idea, as that would make it hard to reuse in the future.

Mongoid find by embedded document

I have the following embedded structure
class CbAuthor
include Mongoid::Document
embeds_many: cb_bylines
field :name, type: String
end
class CbByline
include Mongoid::Document
embedded_in :cb_author
has_many :cb_articles
field :byline, type: String
end
class CbArticle
include Mongoid::Document
belongs_to :cb_byline
end
This is because there are many bylines or pseudonyms the authors publish under and that is will be attached to their analytics reports. So when I have a byline, how do I find the author? This will be necessary because They will have dashboards that should list all the articles they wrote under all their respective bylines.
I tried CbAuthor.cb_bylines but that gives me a no method error. or CbAuthor.where(cb_bylines["byline"]: bylineInQuestion) but that also gives errors.
Essentially the goal is to have one author name to find all his bylines and the articles associated with those bylines
embeds_many :cb_bylines is just a fancy way of saying "add an array of hashes called cb_bylines" (at least as far as storage is concerned). That means that your CbAuthors look like this inside MongoDB:
{
_id: '...',
name: '...',
cb_bylines: [
{ _id: '...', byline: '...' },
...
]
}
MongoDB will unroll the array for simple queries for you so you can simply look for 'cb_bylines.byline' as though you were querying a hash inside the collection:
authors_by_lined_as_pancakes = CbAuthor.where('cb_bylines.byline' => 'Pancakes McGee')
or if you know there is just one:
pancakes_mcgee = CbAuthor.find_by('cb_bylines.byline' => 'Pancakes McGee')
Don't be afraid to bypass Rails and Mongoid to look at what your data really looks like inside MongoDB.

How do I autocreate associated records in MongoDB using Mongoid?

I'm still getting my head around MongoDB and Mongoid in particlar.
Let's say I have a User and each User has one Thingamajig. When I create the User
I want the system to autmatically also create a blank Thingamajig for that User.
Each Thingamajig has a whatsit field that must be unique if it has a value, but is allowed to have no value when created.
So I define the following classes.
class Thingamajig
include Mongoid::Document
field :whatsit, type: String
index({whatsit: 1}, {unique: true, name: 'whatsit_index'})
end
class User
include Mongoid::Document
field :name, type: String
index({name: 1}, {unique: true, name: 'user_name_index'})
embeds_one :thingamajig, dependent: :nullify, autobuild: true
end
However what I find when I
User.create!(name: 'some name')
is that User.find(name: 'some name').thingamajig is nil.
Questions:
How can I ensure that each User gets an associated Thingamajig? and
How do I specify that the name field of a User is required?
FYI I am using Sintara not Rails (if that matters to anyone).
1 - The autobuild: true option normally should have done the trick. I think the problem is that you forgot to add the other side of the relation to the Thingamajig model:
class Thingamajig
include Mongoid::Document
embedded_in :user
...
end
2 - To specify required fields, use validations:
class User
include Mongoid::Document
field :name, type: String
validates_presence_of :name
...
end
Mongoid uses ActiveModel validations.

writing a custom rails attribute validator that depends on another attribute being valid

I'm using the awesome client_side_validations gem and I've written a custom validator like this:
class PersonNameValidator < ActiveModel::EachValidator
def validate_each(record, attr_name, value)
record.errors.add(attr_name) if record.errors.has_key?(:person_id)
end
end
My goal is to have the virtual attribute :person_name be invalid if :person_id is invalid. My question is whether it is reasonable to use if record.errors.has_key? inside another validator as a way to determine if another attribute is valid?
I read that validators fire in the order declared so this is in my model:
validates :person_id, numericality: { greater_than: 0 }
validates :person_name, presence: true
Is it wise to rely on this technique as a way to keep my validations DRY?

Rails Model callback: Passing field as parameter to callback classes?

I want to implement before_validaton callback in a separate class so that it can be reused by multiple model classes.
Here in callback i want to strip field passed as parameter but i am not sure how to pass parameter to callback class. Also i want to pass this as reference rather than by value(not sure if this concept is in Ruby Rails). I am following the link http://guides.rubyonrails.org/active_record_validations_callbacks.html#callback-classes
Here is code which is not completely correct, please help for same
class StripFieldsCallback
def self.before_validation(field)
field = field.strip
end
end
class User < ActiveRecord::Base
validates_uniqueness_of :name, :case_sensitive => false
validates_length_of :name, :maximum => 50
before__validation StripFieldsCallback(name)
end
If i define method in model in itself rather than defining in separate callback class code is like this (which works fine)
class User < ActiveRecord::Base
validates_uniqueness_of :name, :case_sensitive => false
validates_length_of :name, :maximum => 50
before__validation :strip_blanks
protected
def strip_blanks
self.name = self.name.strip
end
end
Of course it is not good to replicate methods in all of models so i want to define method in callback classes.
You may do this or use normalize_attributes gem
module StripFieldsCallback
def before_validation_z(field)
write_attribute(field, read_attribute(field).strip) if read_attribute(field)
end
end
class User < ActiveRecord::Base
include StripFieldsCallback
before_validation lambda{|data| data.before_validation_z(:name)}
end

Resources