I have a very generic validator and I want to pass it arguments.
Here is an example model:
class User
include Mongoid::Document
field :order_type
has_many :orders, inverse_of :user
validates: orders, generic: true #i want to pass argument (order_type)
field :task_type
has_many :tasks, inverse_of :user
validates: tasks, generic: true #i want to pass argument (task_type)
end
and Example validator:
class GenericValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
if some_validation?(object)
object.errors[attribute] << (options[:message] || "is not formatted properly")
end
end
end
Is there any way to pass arguments to the validator dependant on which field it is validating?
thanks
I wasn't aware of this either, but if you want to pass an argument, then pass a hash to generic: instead of true. This post details the exact process you're wanting to follow:
class User
include Mongoid::Document
field :order_type
has_many :orders, inverse_of :user
validates: orders, generic: { :order_type => order_type }
field :task_type
has_many :tasks, inverse_of :user
validates: tasks, generic: { :task_type => task_type }
end
GenericValidator should now have access to both arguments you're wanting to pass in validation: options[:order_type] and options[:task_type].
It might make more sense, however, to divide these up into two validators, with both inheriting the shared behavior as mentioned by dpassage:
class User
include Mongoid::Document
field :order_type
has_many :orders, inverse_of :user
validates: orders, order: { type: order_type }
field :task_type
has_many :tasks, inverse_of :user
validates: tasks, task: { type: task_type }
end
Related
I'm having a tough time figuring something out in Rails. It probably has to do with my very limited knowledge of SQL, since I know Rails pretty well. I'm using Rails 5.
I have two models: Applicant and Application.
class Applicant < ApplicationRecord
has_one :application
has_many :skills
accepts_nested_attributes_for :application
accepts_nested_attributes_for :skills,
reject_if: ->(skill) { skill[:name].empty? || skill[:experience].empty? }
validates_with ApplicantValidator
end
class Application < ApplicationRecord
belongs_to :applicant
has_many :notes
VALID_STATUSES = ["in review", "accepted", "declined", "closed"]
validates_length_of :why_interested, minimum: 25
validates :accept_terms, acceptance: true
validates :status, inclusion: { in: VALID_STATUSES }
before_validation :set_status
private
def set_status
self.status ||= "in review"
end
end
I'd like to add a scope, :active, to the Applicant model that returns only applicants who have an application whose status is "in review". However, I can't find a way to access the application within a scope proc.
I've seen other suggestions for cases where there is a has_many relationship with the child, but they didn't work in my case.
I doubt it makes a difference, but I'm using Postgres. The closest I've come to a solution is to add this, but when I run RSpec it says there needs to be a FROM-clause for the applications table. I don't know how to effect that.
scope :active, -> { joins(:application).where('"application"."status" = "in review"') }
scope :in_review_applicants, -> { joins(:application).where('application.status = ?', :in_review) }
I think is something like that..
I have such db structure:
term.rb:
class Term < ActiveRecord::Base
has_many :tasks, through: :students
...
def accepted_tasks_count
tasks.where(status: Task.statuses[:accepted]).count
end
task.rb:
class Task < ActiveRecord::Base
has_many :notes, through: :submissions
...
def notes_count
self.notes.count
end
I need to add some method which will return accepted tasks without notes.
How can I do that?
Try this one:
class Task < ActiveRecord::Base
has_many :notes, through: :submissions
scope :accepted, -> { where(status: self.statuses[:accepted]) }
scope :without_notes, -> { includes(:notes).where(notes: { id: nil }) }
end
I've moved 'accepted tasks' query to the scope too, to make it reusable.
class Term < ActiveRecord::Base
has_many :tasks, through: :students
def accepted_tasks_count
tasks.accepted.count
end
end
To get all accepted tasks without notes, use this:
Task.accepted.without_notes
To get all accepted tasks without notes for the specific term, use that:
#term.tasks.accepted.without_notes
Let's say we have the following data structure (imagine as a loop).
MyModel * -- 1 User 1 -- * Clients 0 -- * MyModel
Thus, the MyModel looks like:
class MyModel < ActiveRecord::Base
belongs_to :client
belongs_to :user
attr_accessible :user_id, :client_id, # ...
validates :user_id, :presence => true
So it can belong to a client but must belong to a user. Also a client must belong to a user.
But how can I assert, that if someone saves a MyModel instance belonging to a client, that the client actually belongs to the user? Do I have to write a custom validator for this or is there any validation shortcut I overlooked?
Solution:
Following p.matsinopoulos' answer, I now did the following. I don't assign the foreign keys to MyModel before saving but rather the objects directly.
# my_model_controller.rb
def create
m = MyModel.create(whitelist_parameters(params[:my_model]))
m.user = current_user
if(params[:my_model][:client_id].present?)
client = Client.find params[:my_model][:client_id]
end
# ...
end
This way I can at first validate if there is a Client with such an ID at all. Then I implemented the custom validator by p.matsinopoulos. It's not tested yet, but since I am now working with the objects, it should do the trick.
If I were you, I would have written a custom validator.
validate :client_belongs_to_user
protected
def client_belongs_to_user
errors[:client] << 'must own the user' if client.present? && client.user != user
end
I would suggest, building on what you have:
class MyModel < ActiveRecord::Base
belongs_to :client
has_one :user, :through => :client
attribute_accessor :client_id
validates :client_id, :presence => true
...
end
class Client < ActiveRecord::Base
has_many :my_models
belongs_to :user
attribute_accessor :user_id
validates :user_id, :presence => true
...
end
class User < ActiveRecord::Base
has_many :clients
...
end
My model structure is as follows:
class Client < ActiveRecord::Base
has_many :charts
end
class Chart < ActiveRecord::Base
belongs_to :client
has_many :chart_data
end
class ChartDatum < ActiveRecord::Base
belongs_to :chart
end
ChartDatum has an attribute called 'name' which needs to be unique for each client.
I tried using "validates_uniqueness_of :name, :scope => [:chart_id]" but this helped me getting a unique key for a particular chart but not for all charts for a particular client. I am looking for something like "validates_uniqueness_of :name, :scope => [:client_id]" but obviously with the current structure it will not work out.
Could someone please help me?
Since you need unique name for chart_data for each client, you can try writing a custom validation for name something like this :
class ChartDatum < ActiveRecord::Base
belongs_to :chart
validates :name, :uniqueness => true, unless => :unique_for_client?
def unique_for_client?
client = self.chart.client
client.charts.chart_data.pluck(:name).include?(self.name)
end
end
I get this error "WARNING: Can't mass-assign protected attributes: races_attributes"
, when following this http://railscasts.com/episodes/196-nested-model-form-part-1 on rails 3.
Where Races are a component of Events. This is my models/race.rb:
class Race < ActiveRecord::Base
belongs_to :event
attr_accessible :name, :unit
end
This is my models/event.rb:
class Event < ActiveRecord::Base
has_many :races, :dependent => :destroy
accepts_nested_attributes_for :races
attr_accessible :name, :date, :description, :location_name, :address_one, :address_two, :city, :state, :zip, :active, :races_attributes
end
Any Ideas?
Shorter than using attr_accessible, safer than using whitelist_attributes: attr_protected
Just indicate the protected attributes, and Rails will infer that all others can be mass-assigned:
class MyClass < ActiveRecord::Base
attr_protected :id
end
(I always have way more attributes that I want mass-assigned than the ones I want protected.)
attr_accessible specifies that you can not mass-assign attributes, using save method, for example. So, if you change an attribute that is not defined with attr_accessible, you will get a warning because it will not actually be saved in the database.