I have a class User in which i want to do conditional validation like such:
if $organization.first_name_last_name
validates :name_first, presence: true,
length: { maximum: 50 },
:if => :local_authentication?
validates :name_last, presence: true,
length: { maximum: 50 },
:if => :local_authentication?
However this global organisation variable is an object containing specific settings for a subdomain and is initialized in the before_filter of the application controller.
class ApplicationController < ActionController::Base
before_filter :load_organization
...
def load_organization
$organization = Organization.find_by_subdomain(request.subdomain).settings
end
My question is how to make the load_organization run before the User class is loaded so that the organization variable is correctly set.
There is no way, this can work, because the User class is not reloaded between requests, at least not in production.
You are already using the :if parameter for the validation. You can use this for the settings, as well:
validates :names_first, presence: true,
if: -> {
$organization.first_name_last_name &&
local_authentication?
}
BTW: Using a global variable is generally a sign for bad software design. Doesn't the user you are trying to create/update which organization it belongs to and you can access the settings via the relation getter?
P.S.: You are mixing hash syntaxes, as well. You should either use :foo => :bar or foo: :bar, but not both at the same time. Actually I'm surprised this works at all ;)
Related
I am currently new in ruby on rails and I have gem called rails admin. I want the validation not repeatable, i want it to save in one method and make it global so that in my model I can call the validation format.
I added a def in my application controller and inside of it i declare it as global but when I type special characters, it will be add.
Note: My validation is only allowed alphanumeric or underscore..
Expected Output: It wont add unless the input is alphanumeric or underscore
Question: Is my global variable wrong? How could I make a global variable so that I will just call it
Model
class ActivityType < ApplicationRecord
has_many :activities
validates :name, presence: true, length: { maximum: 20 },
:uniqueness => true,
format: { with: /\A[\w\_]+\z/ }
validates :description, presence: true,
format: { with: /\A[\w]+\z/ }
end
RegexValidations
module RegexValidations
def self.alphanumeric_underscore
{ with: /\A[\w\_]+\z/ }
end
end
Validation are done at model level for their respective attributes.
Defining methods in application_controller doesn't make it global, but accessible to all controllers inheriting from it.
Your model validation validates should be enough to validate format of the string in column.
Edit/Improvisation:
If you want to make the regex dry/not repeat for every model. Create a module in your lib folder say:
module RegexValidations
def self.alphanumeric_underscore
{ with: /\A[\w\_]+\z/ }
end
end
And in your model include it and say:
include RegexValidations
validates :description, presence: true, format: RegexValidations.alphanumeric_underscore
This way you can write multiple regex in one file and use it in every model keeping it DRY.
PS: unfortunately i couldn't test it..but it should work. Let me know if you encounter any error.
I have an invoice model with approver_note, po_number and state_type.
I need validations to check:
scope :approver, where(state_type: 3)
scope :po_no, where(state_type: 2)
validates :approver_note, :presence => true, uniqueness: { scope: [:ac_id, :approver]}, if: :state_three?
validates :po_number, :presence => true, uniqueness: { scope: [:ac_id, :po_no]}, if: :state_two?
def state_three?
self.state_type==3
end
def state_two?
self.state_type==2
end
How can I make sure that the uniqueness in approver_note validator is run on selected scope of records. It should validate using records having state_type=3.
I need something in the similar lines of this bug...
https://rails.lighthouseapp.com/projects/8994/tickets/4325-real-scope-support-for-activerecords-uniqueness-validation
Is this available in rails now? or can we achieve this using custom validation?
The scope option of uniquness checks if the combination of 2 column values is uniq in the table, frankly I really I don't see how it would be clever enough to apply a dynamic scope. Too much magic even for rails !
However a custom validator is quite straightforward :
validate :approver_note_scoped_uniqueness, if: :state_three?
def approver_note_scoped_uniqueness
if self.class.approver.where(ac_id: ac_id).count > 0
errors.add(:ac_id, "My custom error message")
end
end
ADDITIONAL INFO:
Adding to that, I see that the conditions option is available in validate_uniqueness_of from Rails 4. We can use that and construct two validations one for presence and one for uniqueness. Just in case if some one is looking for an answer in Rails 4.
In case of Rails 4,
validates_presence_of :approver_note, if: :state_three?
validates_presence_of :po_number, if: :state_two?
validates_uniqueness_of :approver_note, scope: [:ac_id], conditions: -> { where(state_type: 3)}, if: :state_three?
validates_uniqueness_of :po_number, scope: [:ac_id], conditions: -> { where(state_type: 2)}, if: :state_two?
def state_three?
self.state_type==3
end
def state_two?
self.state_type==2
end
I need a conditional validation in some parts of my app. Right now I am using the following scheme:
User.create
User::WithPassword.create
User::WithPhone.create
It would be cool if I could change class behaviour on the fly like this:
User.with_phone.with_password.create
So I tried to do it like this:
class User < ActiveRecord::Base
validates :phone, presence: true, if: :phone_required?
def self.with_phone
define_method(:phone_required?) { true }
self
end
private
def phone_required?
false
end
end
So it can be used like this where needed:
User.with_phone.create(user_params)
The problem with this approach is that all instances of User get new behaviour since the actual class changes.
Is there a way to return only the modified copy of User class with new instance method phone_required? without affecting the "base" class?
Update
Thank you for the comments as this was more of an idea, the requirement is that I create users without certain validation automatically, and then when they edit profile, they are dealing with pristine User model. I create with_/without_ on the fly in method missing when needed.
Here's my next iteration:
class User < ActiveRecord::Base
validates :phone, presence: true, if: :phone_required?
def self.with_password
define_singleton_method(:password_required?) { true }
self
end
def password_required?
self.class.try :password_required?
end
end
Apparently it's not any better as the singleton method stays there all the time.
Why not simply use an instance variable initialized at creation time?
class User < ActiveRecord::Base
validates :phone, presence: true, if: :phone_required?
#phone_required = false
def self.create_with_phone(params)
obj = self.create(params)
obj.phone_required = true
end
private
def phone_required=(v)
#phone_required = v
end
def phone_required?
#phone_required
end
end
User.create_with_phone(user_params)
You have a model, say, Car. Some validations apply to every Car instance, all the time:
class Car
include ActiveModel::Model
validates :engine, :presence => true
validates :vin, :presence => true
end
But some validations are only relevant in specific contexts, so only certain instances should have them. You'd like to do this somewhere:
c = Car.new
c.extend HasWinterTires
c.valid?
Those validations go elsewhere, into a different module:
module HasWinterTires
# Can't go fast with winter tires.
validates :speed, :inclusion => { :in => 0..30 }
end
If you do this, validates will fail since it's not defined in Module. If you add include ActiveModel::Validations, that won't work either since it needs to be included on a class.
What's the right way to put validations on model instances without stuffing more things into the original class?
There are several solutions to this problem. The best one probably depends on your particular needs. The examples below will use this simple model:
class Record
include ActiveModel::Validations
validates :value, presence: true
attr_accessor :value
end
Rails 4 only
Use singleton_class
ActiveSupport::Callbacks were completely overhauled in Rails 4 and putting validations on the singleton_class will now work. This was not possible in Rails 3 due to the implementation directly referring to self.class.
record = Record.new value: 1
record.singleton_class.validates :value, numericality: {greater_than: 1_000_000}
record.valid? || record.errors.full_messages
# => ["Value must be greater than 1000000"]
Rails 3 and 4
In Rails 3, validations are also implemented using ActiveSupport::Callbacks. Callbacks exist on the class, and while the callbacks themselves are accessed on a class attribute which can be overridden at the instance-level, taking advantage of that requires writing some very implementation-dependent glue code. Additionally, the "validates" and "validate" methods are class methods, so you basically you need a class.
Use subclasses
This is probably the best solution in Rails 3 unless you need composability. You will inherit the base validations from the superclass.
class BigRecord < Record
validates :value, numericality: {greater_than: 1_000_000}
end
record = BigRecord.new value: 1
record.valid? || record.errors.full_messages
# => ["Value must be greater than 1000000"]
For ActiveRecord objects, there are several ways to "cast" a superclass object to a subclass. subclass_record = record.becomes(subclass) is one way.
Note that this will also preserve the class methods validators and validators_on(attribute). The SimpleForm gem, for example, uses these to test for the existence of a PresenceValidator to add "required" CSS classes to the appropriate form fields.
Use validation contexts
Validation contexts are one of the "official" Rails ways to have different validations for objects of the same class. Unfortunately, validation can only occur in a single context.
class Record
include ActiveModel::Validations
validates :value, presence: true
attr_accessor :value
# This can also be put into a module (or Concern) and included
with_options :on => :big_record do |model|
model.validates :value, numericality: {greater_than: 1_000_000}
end
end
record = Record.new value: 1
record.valid?(:big_record) || record.errors.full_messages
# => ["Value must be greater than 1000000"]
# alternatively, e.g., if passing to other code that won't supply a context:
record.define_singleton_method(:valid?) { super(:big_record) }
record.valid? || record.errors.full_messages
# => ["Value must be greater than 1000000"]
Use #validates_with instance method
#validates_with is one of the only instance methods available for validation. It accepts one or more validator classes and any options, which will be passed to all classes. It will immediately instantiate the class(es) and pass the record to them, so it needs to be run from within a call to #valid?.
module Record::BigValidations
def valid?(context=nil)
super.tap do
# (must validate after super, which calls errors.clear)
validates_with ActiveModel::Validations::NumericalityValidator,
:greater_than => 1_000_000,
:attributes => [:value]
end && errors.empty?
end
end
record = Record.new value: 1
record.extend Record::BigValidations
record.valid? || record.errors.full_messages
# => ["Value must be greater than 1000000"]
For Rails 3, this is probably your best bet if you need composition and have so many combinations that subclasses are impractical. You can extend with multiple modules.
Use SimpleDelegator
big_record_delegator = Class.new(SimpleDelegator) do
include ActiveModel::Validations
validates :value, numericality: {greater_than: 1_000_000}
def valid?(context=nil)
return true if __getobj__.valid?(context) && super
# merge errors
__getobj__.errors.each do |key, error|
errors.add(key, error) unless errors.added?(key, error)
end
false
end
# required for anonymous classes
def self.model_name
Record.model_name
end
end
record = Record.new value: 1
big_record = big_record_delegator.new(record)
big_record.valid? || big_record.errors.full_messages
# => ["Value must be greater than 1000000"]
I used an anonymous class here to give an example of using a "disposable" class. If you had dynamic enough validations such that well-defined subclasses were impractical, but you still wanted to use the "validate/validates" class macros, you could create an anonymous class using Class.new.
One thing you probably don't want to do is create anonymous subclasses of the original class (in these examples, the Record class), as they will be added to the superclass's DescendantTracker, and for long-lived code, could present a problem for garbage collection.
You could perform the validation on the Car, if the Car extends the HasWinterTires module... For example:
class Car < ActiveRecord::Base
...
validates :speed, :inclusion => { :in => 0..30 }, :if => lambda { self.singleton_class.ancestors.includes?(HasWinterTires) }
end
I think you can just do self.is_a?(HasWinterTires) instead of the singleton_class.ancestors.include?(HasWinterTires), but I haven't tested it.
Have you thought about using a Concern? So something like this should work.
module HasWinterTires
extend ActiveSupport::Concern
module ClassMethods
validates :speed, :inclusion => { :in => 0..30 }
end
end
The concern won't care that it itself has no idea what validates does.
Then I believe that you can just do instance.extend(HasWinterTires), and it should all work.
I'm writing this out from memory, so let me know if you have any issues.
You likely want something like this, which is very similar to your original attempt:
module HasWinterTires
def self.included(base)
base.class_eval do
validates :speed, :inclusion => { :in => 0..30 }
end
end
end
Then the behavior should work as expected in your example, though you need to use include instead of extend on HasWinterTires.
I have two different validations for the :website attribute on my Customer model. One is the build in length helper, with the maximum set to 255, while the other is a custom validation. They both work individually, and the appropriate tests pass, but for some reason, when I run my tests with both validations, RSpec crashes to the point I have to complete exit out of Guard and restart it.
Here is my code, any way they are some how conflicting with each other? I have never experienced this before:
class Customer < Active Record::Base
...
URL_REGEX = /(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*#)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)|\/|\?)*)?$/i
validates :website, length: { maximum: 255 }
validate :valid_urls
private
def valid_urls
["website", "blog", "contact"].each do |attribute|
errors.add(attribute, "needs to be a valid url") if send(attribute).present? && URL_REGEX.match(send(attribute)).nil?
end
end
end
UPDATE: Thanks for the help, turned out the whole issue was just a bad regex. I had copied the regex from a stackoverflow thread, which had escaped some of the ampersands, producing a bad regex. I just now copied it from the jQuery validate source and it worked, sorry for the trouble.
Mackshkatz, can you try removing custom validation to use those provided by rails? As such:
class Customer < ActiveRecord::Base
validates :website, format: { with: URL_REGEX }, allow_blank: true, length: { maximum: 255 }
validates :blog, format: { with: URL_REGEX }, allow_blank: true
validates :contact, format: { with: URL_REGEX }, allow_blank: true
end
And see if it passes? It seems like the problem may be in complex regexp you are using.