How can I write validation for barcode to be unique for all users where is_deleted is false and same chain?
validates :barcode, uniqueness: { conditions: -> { |record| where(is_deleted: false, chain_id: record.chain_id) } }, if: proc { |u| u.barcode.present? }
what is wrong here?
Thanks.
upd. There can be two users with same barcode with same chain_id, if one of them or both have :is_deleted => true
Rails validation have if and unless parameters which allow you to add conditions, you used it to check barcode presence properly, but you can extend it for is_deleted as well.
As to chain id, I understand that you are interested in scoping.
In your case that would be
validates :barcode, uniqueness: { scope: [:chain_id] }, if: proc { |u| u.barcode.present? && w.is_deleted.false? }
validates :barcode, uniqueness: { scope: :chain_id, conditions: ->{ where(is_deleted: false) } }, if: proc { |u| u.barcode.present? && u.active? }
I came up with a solution.
Thanks and sorry, its late here and head is not working as expected :)
Related
I am coding my own ERP. For the People (model class) I have the following validations:
class People < ApplicationRecord
# some code for N:M relations
# validations
validates :aka, presence: { if: proc { |person| person.aka? } },
uniqueness: true,
length: { within: 3..25,
if: proc { |person| person.aka? } }
validates :last_name, presence: { if: proc { |person| person.last_name? } },
uniqueness: { scope: %i[last_name first_name] },
length: { within: 2..100,
if: proc { |person| person.last_name? } }
validates :phone_ext, presence: { if: proc { |person| person.phone_ext? } },
length: { within: 1..10,
if: proc { |person| person.phone_ext? } },
format: { with: /\A\d{1,10}\Z/i,
if: proc { |person| person.phone_ext? } }
validates :first_name, presence: true,
uniqueness: { scope: %i[last_name first_name] },
length: { within: 2..100 }
end
As you can see in all the if: proc { .... lines, they are almost kind of the same stuff. And fasterer knows about it, that's why I am getting the Calling argumentless methods within blocks is slower than using symbol to proc message.
Now, unsuccessfully all day I have trying all day long to figure a way out to solve this Fasterer's message. I have tried lambdas, closures, &:, ->, so I give up.
Any ideas?
This is referring to, for example, a.map(&:foo) being faster than a.map { |o| o.foo }.
In this context, validates will take a method name to check as a symbol. For example, if: :aka? instead of if: proc { |person| person.aka? }
I don't understand why I can't use self here?
class PayoutRequest < ApplicationRecord
validates :phone, confirmation: true, on: :create
validates :phone_confirmation, presence: true, on: :create
belongs_to :user, foreign_key: "user_id"
validates :amount, numericality: { only_integer: true, greater_than_or_equal_to: 300, smaller_than_or_equal: self.user.balance }
scope :paid, -> { where(:paid => true) }
scope :unpaid, -> { where(:paid => false) }
end
How can I write this?
Use a custom method, for example:
validate :amount_not_greater_than_balance
def amount_not_greater_than_balance
return if amount <= user.balance
errors.add(:amount, "can't be greater than balance")
end
In addition, you should probably only run this specific validation rule on: :create -- because it would presumably be totally acceptable for a payment request to become more than the user's balance, later on n the future.
Because self is not what you think it is. In case you didn't know or forgot, validation DSL is just methods called on the class itself. Here you basically call PayoutRequest.validates and pass it some parameters.
validates :amount, numericality: { only_integer: true, greater_than_or_equal_to: 300, smaller_than_or_equal: self.user.balance }
^ ^ arg ^ kw arg ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
method name just a regular hash, defined at the class level. So `self` is the class.
How can I create a validation where committed presence needs to be true only if the challenge's category is habit?
class Challenge < ActiveRecord::Base
CATEGORY = ['goal', 'habit']
serialize :committed, Array
validates :committed, presence: true, if: :habit # I also tried with 'habit' & 'Habit'
end
Since your category is called 'habit' (note, it is not 'Habit'), the validation would look as follows:
validates :committed, presence: true, if: ->(c) { c.category == 'habit' }
As a sidenote: I do not think your scopes will work, unless you have a column called categories in your challenges table.
Thus, if your intention was to select challenges, which have category 'habit', the scope would look as follows:
scope :habit, -> { where(category: 'habit') }
EDIT
As per discussion in comments, if you want committed to be nil instead of [""] when nothing is there, add custom validation:
validate :committed_content
private
def committed_content
self.committed = nil if committed.empty? || committed.all?(&:blank?)
true
end
validates :committed, presence: true, :if => lambda { |c| c.category == 'Habit' }
You can have a method and use it like this:
validates :committed, presence: true, if: :habit?
def habit?
self.category == 'habit'
end
Short of extracting shipping and billing addresses into an Address model, how can I remove this validation duplication?
I only want to validate the billing address if it's not the same as the shipping address. How would I go about extracting it into a module? An example would be really helpful as I never know what to include in modules, or self refers to.
validates :shipping_name, :shipping_address1, :shipping_street_number, :shipping_city, presence: true
validates :shipping_state, inclusion: { in: Address.states.values }
validates :shipping_post_code, length: { is: 5 }, numericality: { only_integer: true }
validates :billing_name, :billing_address1, :billing_street_number, :billing_city, presence: true, unless: -> { self.bill_to_shipping_address? }
validates :billing_state, inclusion: { in: Address.states.values }, unless: -> { self.bill_to_shipping_address? }
validates :billing_post_code, length: { is: 5 }, numericality: { only_integer: true }, unless: -> { self.bill_to_shipping_address? }
You can make a method and then pass in the bits that are different between the two types of addresses. In this case, the difference is the prefix word for the fields and the ability to pass in extra options.
module AddressValidator
def validates_address(type, options = {})
validates :"#{type}_name", :"#{type}_address1", :"#{type}_street_number", :"#{type}_city", {presence: true}.merge(options)
validates :"#{type}_state", {inclusion: { in: Address.states.values }}.merge(options)
validates :"#{type}_post_code", {length: { is: 5 }, numericality: { only_integer: true }}.merge(options)
end
end
class MyModel < ActiveRecord::Base
extend AddressValidator
validates_address(:shipping)
validates_address(:billing, unless: -> { self.bill_to_shipping_address? })
end
How can I dry up my validation code? I have a Discussion model that has a a category and status fields. The status value depends on the category value. A discussion where category == 'question' can only have a status in STATUSES[:question], for example.
STATUSES = {
question: %w[answered],
suggestion: %w[pending planned started completed declined],
problem: %w[started solved]
}
validates :status, allow_blank: true, inclusion: { in: STATUSES[:question] }, if: lambda { self.category == 'question' }
validates :status, allow_blank: true, inclusion: { in: STATUSES[:suggestion] }, if: lambda { self.category == 'suggestion' }
validates :status, allow_blank: true, inclusion: { in: STATUSES[:problem] }, if: lambda { self.category == 'problem' }
I'm using Rails 3.
:inclusion :in accepts a lambda itself:
validates :status, inclusion: { in: lambda { |o| STATUSES[o.category.to_sym] } }
Documentation: http://apidock.com/rails/ActiveModel/Validations/HelperMethods/validates_inclusion_of