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.
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 two validations:
validates :email, format: { with: /\A(.+)#(aol|gmail|office365|outlook|verizon|yahoo)\.com\Z/i }, if: Proc.new { |user| user.imap_server.blank? }
validates :email, presence: true
validates :imap_server, presence: true, if: Proc.new { |user| user.email.present? && user.email_invalid? }
def email_invalid?
self.email =~ /\A(.+)#(aol|gmail|office365|outlook|verizon|yahoo)\.com\Z/i
end
I show a user a form. It displays an email field but not imap_server field. If the value in email field does not match the specific regex, then I want to show them form again with the imap_server field present as well. If they enter a value for the imap_server field, then I no longer want to validate the regex of email field (although it must still be present).
The problem it feels like I am duplicating a validation. Both email_invalid? and the validates :email, format: ... do the same thing. How can I clean this up?
You could replace the validates :email, format: ... with
validate :email_format
def email_format
errors.add(:email, 'format invalid') if imap_server.blank? && email_invalid?
end
which is slightly more lines but lets you define the format validation in one place.
I suspect that the issue is you're trying to check the result of a validation (email_invalid?) while you're * still doing * the validations... you don't know what order the validations are going to be run (the order on the page is not something I'd trust)... so the best way to solve it is to just write all these things into a single validates method eg a quick-and-dirty way:
validates :email_or_imap_server
def email_or_imap_server
email_valid = false # for scoping
if email.present?
# note: email validation via regex is harder than you think...
# google it...
email_valid = email.match(/#{VALID_EMAIL_FORMATS}/)
if email_invalid
errors.add(:email, "email invalid format should be...")
errors.add(:imap_server, "email or imap-server must be present") unless imap_server.present?
end
else
errors.add(:imap_server, "either email or imap-server must be present") unless imap_server.present?
end
end
etc.
Note: the code above is almost certainly full of bugs and typos... don't copy/paste it almost certainly won't work and the logic doesn't exactly match that of your validations... but do something like this.
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
So I am having an issue where length errors are rails exception because the model doesn't have a length validation on it.
I understand I can put a length validator on my string by using
validates :blah, length: { maximum: 255 }
However most of my strings are using the default size of 255, so I would have to repeat this validation in a lot of places.
Is there a DRY way to put a default validator on all strings to validate to the default database length of 255?
The gem schema_validations (https://github.com/SchemaPlus/schema_validations) does what you want.
Automatically creates validations basing on the database schema.
It inspect the database and automatically creates validations basing on the schema. After installing it your model is as simple as it can be.
class User < ActiveRecord::Base
end
Validations are there but they are created by schema_validations under the hood.
This probably isn't exactly the answer you are looking for but if you add multiple fields to the validator then you aren't really repeating the validator and seems fairly DRY to me.
validates :foo, :bar, :baz, length: { maximum: 255 }
class ModelName < ActiveRecord::Base
validates :name, length: { maximum: 255 }
end
Then use it by typing
ModelName.validators
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 ;)