ActiveModel validate attribute based on association - ruby-on-rails

I have at least 2 classes. One class must validate one of its attributes based on the value of an associated model's attributes. The below code is what I am going for, but its just an idea, it doesn't work. Any way to achieve it?
class Concert
include Mongoid::Document
include Mongoid::Timestamps
field :end_date, type: Date
end
class Sale
include Mongoid::Document
field :end_date, type: Date
belongs_to :concert
validates :end_date, :timeliness => {
:before => lambda {self.concert.end_date},
:after => lambda {self.concert.created_at},
:before_message => 'Sale should not end before the Concert begins',
:after_message => 'Sale should not end after the Concert has already ended',
:type => :date
}
end

just a guess, but isn't there a problem with your reference to self in your lambdas? I'd go for => lambda { |record| record.concert.end_date }

add validation to Sale
validates :end_date, :presence => true, :if => :some_checking
def some_checking
#your validations
#eg
self.concert.end_date.present?
end

Related

Upgrading attr_accessible from Rails 3 to Rails 4

I'm trying to systematically upgrade from rails 3 to rails 4 and all of my 25 models are based on attr_accessor! So before getting into that can anyone provide me a simple example on how to do this. I've read the documentation and other topics but it's not clear on how to do it since this is my first upgrade Rodeo.
class Settings < ActiveRecord::Base
image_accessor :favicon
attr_accessible :company_name, :show_hot_jobs, :show_students, :subheading, :show_testimonials, :show_on_boarding, :max_concurrent_applications
attr_accessible :image_uid, :max_concurrent_application_groups
attr_accessible :primary_color, :white_color, :gray_color, :opacity, :locale, :lang_nl, :lang_fr, :lang_de, :lang_en, :privacy_page
attr_accessible :show_evp, :show_contact_person, :show_jobs_for_you
attr_accessible :favicon, :favicon_uid, :remove_favicon, :retained_favicon
attr_accessible :home_url, :show_correspondence, :show_appointment
attr_accessible :sliderone_uid, :slidertwo_uid, :sliderthree_uid, :sliderfour_uid, :sliderfive_uid
attr_accessible :sliderone_link, :slidertwo_link, :sliderthree_link, :sliderfour_link, :sliderfive_link
attr_accessible :sliderone_testoverview, :slidertwo_testoverview, :sliderthree_testoverview, :sliderfour_testoverview, :sliderfive_testoverview
attr_accessible :sliderone_page, :slidertwo_page, :sliderthree_page, :sliderfour_page, :sliderfive_page
validate :any_lang_present?
validates :max_concurrent_applications, :numericality => { :greater_than_equal_to => 1 }
validates :max_concurrent_application_groups, :numericality => { :greater_than_equal_to => 1 }
# Fav Icon Validation
validates_property :ext, of: :favicon, :in => ['ico', 'png', 'gif']
has_paper_trail
has_many :setting_translations, :foreign_key => :setting_id
accepts_nested_attributes_for :setting_translations, :allow_destroy => true, :reject_if => :all_blank
attr_accessible :setting_translations_attributes, :allow_destroy => true
translates :subheading, :company_name, :image_uid, :home_url, :sliderone_uid, :slidertwo_uid, :sliderthree_uid, :sliderfour_uid, :sliderfive_uid
translates :sliderone_link, :slidertwo_link, :sliderthree_link, :sliderfour_link, :sliderfive_link
translates :sliderone_testoverview, :slidertwo_testoverview, :sliderthree_testoverview, :sliderfour_testoverview, :sliderfive_testoverview
translates :sliderone_page, :slidertwo_page, :sliderthree_page, :sliderfour_page, :sliderfive_page
attr_accessible can be converted like so:
From
class Settings
attr_accessible :home_url
accepts_nested_attributes_for :setting_translations
end
class SettingTranslation
attr_accessible :etc
end
To
class SettingsController
def create
#settings = Settings.new(settings_params)
# ...
end
private
def settings_params
params.require(:settings).permit(
:home_url,
:setting_translations_attributes => [:id, :_destroy, :etc]
)
end
end
Note, you have to include :_destroy if you want to allow destroy on that model (:allow_destroy => true), and you have to include all attributes that should be accessible from any nested attributes. Though you remove attr_accessible when you've permitted, you do not remove accepts_nested_attributes_for.
Just remove attr_accessible from model. and add permit params according to need in controller.
like below :
class SupportTicketsController < ApplicationController
def create
#support_ticket = SupportTicket.create(house_params)
......
end
private
def house_params
params.require(:support_ticket).permit(:subject, :message, ....)
end
end
and if you don't want to make this much changes then add "protected_attributes" gem https://github.com/rails/protected_attributes in your gemfile And everything would work as before.

persist embeds_one relation mongoid

class Order
include Mongoid::Document
include Mongoid::Timestamps
#relationships
embeds_one :user_detail
#fields
field :description
#validations
validates :user_detail, presence: true
end
This the embedded object in order:
class UserDetail
include Mongoid::Document
include Mongoid::Timestamps
#fields
field :name, :type => String
field :zip_code, :type => String
field :email, :type => String
# Relationships
embedded_in :order
#validations
validates_presence_of :name, :zip_code, :email
end
I want save/persist on mongodb order object with user_detail object embedded_in order object.
I have tried with:
order = Order.new(description: "checking description")
order.user_detail = Order.new(:name => "John", :zip_code => "26545", :email => "john#john.com")
order.save!
but I get validation fail:
o.save!
Mongoid::Errors::Validations:
Problem:
Validation of Order failed.
Summary:
The following errors were found: User detail is invalid
Resolution:
Try persisting the document with valid data or remove the validations....
How can I fix this problem? I'm using mongoid 3.x
Should be:
order = Order.new(description: "checking description")
order.user_detail = UserDetail.new(:name => "John", :zip_code => "26545", :email => "john#john.com")
order.save!
You had Order.new for OrderDetail.new
You do not need to manually create user_detail using
order.user_detail = UserDetail.new...
order.save!
The embedded user_detail will be created automatically if u add autobuild attribute
embeds_one :user_detail autobuild: true
If u wanna persist user_detail in database as well, do not forget to add
validates_presence_of :user_detail
or you will not see the persisted user_detail in mongo db.

Mongodb, child model validation?

I have a profile model and it embeds_one kids_type and parent_type. As you can see in below code.
I want to validate kids_type child model. After using validates_associated, it's working fine for me.But problem is that, it validates kids_type model [#, #messages=**{:kids_type=>["is invalid"]}>**] , instead i am expecting field wise error, As i need to display inline error...
class Profile
include Mongoid::Document
validates_associated :kids_type
# PROFILE TYPE KIDS & PARENT
embeds_one :kids_type, :class_name => "ProfileKidsType"
embeds_one :parent_type, :class_name => "ProfileParentType"
end
class ProfileType
include Mongoid::Document
#COMMON FILES FOR KIDS AND PARENT
field :fname, :type => String
field :lname, :type => String
validates_presence_of :fname, :message => "ERROR: First name is required"
validates_presence_of :lname, :message => "ERROR: Last name is required"
end
class ProfileParentType < ProfileType
include Mongoid::Document
field :email, :type => String
embedded_in :profile, :inverse_of => :parent_type
end
class ProfileKidsType < ProfileType
include Mongoid::Document
field :nickname, :type => String
embedded_in :profile, :inverse_of => :kids_type
end
Any suggestion would be appreciated, Thanks in advance.
Try this here #profile is instance of Profile, it will give you all errors field wise for kids type
#profile.kids_type.errors

Using factory_girl with mongoid to test referenced_in/references_many

I am trying to test an associated document for a subscription service. Each subscription is embedded in an account and references a plan. Below is the various bits of code:
The account:
Factory.define :account, :class => Account do |a|
a.subdomain 'test'
a.agents { [ Factory.build(:user) ] }
a.subscription { Factory.build(:free_subscription) }
end
The subscription:
Factory.define :free_subscription, :class => Subscription do |s|
s.started_at Time.now
s.plan { Factory.build(:free_plan) }
end
The plan:
Factory.define :free_plan, :class => Plan do |p|
p.plan_name 'Free'
p.cost 0
end
The error:
Mongoid::Errors::InvalidCollection: Access to the collection for Subscription is not allowed since it is an embedded document, please access a collection from the root document.
If I comment out the line that links the plan to the subscription then the tests work, but obviously I can't test that the subscription has a plan.
Any suggestions would be greatly appreciated.
UPDATE:
Here are the models:
class Account
include Mongoid::Document
field :company_name, :type => String
field :subdomain, :type => String
field :joined_at, :type => DateTime
embeds_one :subscription
accepts_nested_attributes_for :subscription
before_create :set_joined_at_date
private
def set_joined_at_date
self.joined_at = Time.now
end
end
class Subscription
include Mongoid::Document
field :coupon_verified, :type => Boolean
field :started_at, :type => DateTime
referenced_in :plan
embedded_in :account, :inverse_of => :subscription
end
class Plan
include Mongoid::Document
field :plan_name, :type => String
field :cost, :type => Integer
field :active_ticket_limit, :type => Integer
field :agent_limit, :type => Integer
field :company_limit, :type => Integer
field :client_limit, :type => Integer
field :sla_support, :type => Boolean
field :report_support, :type => Boolean
references_many :subscriptions
end
You need to create an account with the subscription for it to be valid.
Factory.define :free_subscription, :class => Subscription do |s|
s.started_at Time.now
s.plan { Factory.build(:free_plan) }
s.account { Factory(:account) }
end

validates_uniqueness_of can't check on unsaved data?

I have a model called Science Subject Choice
class ScienceSubjectChoice < SubjectChoice
belongs_to :subject
belongs_to :subject_preference
validates_associated :subject
validates_associated :subject_preference
#TODO: validation
validates :priority, :presence => true, :numericality => true, :inclusion => {:in => 1..SubjectPreference::MAX_SCIENCE_SUBJECT_CHOICE}
validates_uniqueness_of :subject_id, :scope => :subject_preference_id
validates_uniqueness_of :priority, :scope => :subject_preference_id
end
the uniqueness validator don't work on unsaved data?
How can I solve it?
Solution:
Instead of validating in itself, the parent object should do the validation:
def validate_science_subject_choices_uniqueness
if science_subject_choices.map(&:priority) != science_subject_choices.map(&:priority).uniq
errors[:base] << "Duplicated priority in science subject"
end
end
Validations do not work like that. They are dynamic by nature. If you want database constraints, you have to specify it in your migrations. For instance, a :uniq => true would make sure that a value is unique in your model.

Resources