How to validate field doesnt contain . and , - ruby-on-rails

I have a form which include number fields
<%= f.number_field :contribution_to_sales, class: 'form-control',:pattern=>["\d+"] %>
It allows 2.0 but I want it should not allow 2.0 .
How to do that?

You are missing anchors on your regular expression which causes the "2" in "2.0" to be matched (on some browsers). The regex to use is:
<%= ... pattern: "^\d+$" %>
You should probably be doing the validation on the model as well, as the HTML5 pattern attribute may not be obeyed by all browsers. Simply add:
class YourModel < ActiveRecord::Base
validates :contribution_to_sales, numericality: { only_integer: true }
...
end

Add the following to the Model this form relies on:
validate :format_numbers
def format_numbers
if self.number_field.include?(',') || self.number_field.include?('.')
errors.add(base: "Do not add commas or periods please")
end
end
That is one way to validate however I prefer the much more user friendly way which removes certain things a user may add that I know I don't want and then continue to process the form instead of reporting back with an error. This would be done by removing commas and since you don't want periods either I would imagine we can strip anything to the right of a period out as well. To do it this way you would do the following in your Model instead of the above:
validate :format_numbers
def format_numbers
self.number_field = self.number_field.gsub(/\.\d+/, '') #This will remove any period and all numbers to the right of that period.
self.number_field = self.number_field.gsub(',','') #This will remove all commas throughout.
end
This provides a smoother user experience since they can now type 2,000.00 in the form field and it will be saved as 2000

Related

Rails is persisting on validates

I have an ActiveRecord model that does find and replace filtering on an attribute when its accessor is called, and I'm doing it like the answer to my question here. It looks like this:
class Post < ActiveRecord::Base
include Replaceable
profanity_attrs :body, :title
end
If you haven't looked at the link, then there's a macro called profanity_attrs that calls a profanity_filter method that does find/replace on known keywords.
So when I have something like body = "Oh my poop" and I do a replace with **** so that the filter returns "Oh my ****" then that's what it looks like, and I'm very happy. The DB shows "Oh my poop" and the view shows "Oh my ****", which is exactly what I want, until... I add validation. So if I do this:
class Post < ActiveRecord::Base
include Replaceable
validates :body, length: { maximum: 255 }
profanity_attrs :body, :title
end
Then it still does the replace properly, but the replaced value will be persisted (ie. the db contains "Oh my ****"), and that doesn't make a lot of sense to me. It appears as if the validator is mutating the field that it validates.
Is there a way to preserve the validation, but not have it persist the value returned from the accessor?
Verify that you are not setting the attribute value in your profanity_filter implementation, that you are just returning a filtered copy of it (just validating an attribute shouldn't change it's value unless you're accidently also setting its value in your profanity_filter method).
In addition I would also change a little bit what you did in the macro you defined, instead of
define_method(attr)
I would define the method as
define_method("#{attr}_clean")
and in the views access the filtered version of the attribute by calling the "clean" version, i.e.
<%= post.body_clean %>

Rails 5 re-use before_validation code in multiple models with different fields

I have a Rails 5 application where users can enter currency values in different fields and different models.
Since I only serve one locale, I want users to be able to enter decimals using both . and , decimal separators and ignore any thousands separator.
For example, my users might enter: 1023.45 or 1023,45 but never 1.023,45.
Simple solution that fits my use case: On specific decimal fields representing currency, replace , with . using gsub(',', '.').
What is the best place to put this code? There are multiple models with differently named fields that need to use this code.
Preferably, I would use something like a custom validator that I create once and simply reference with 1 line from all models with the appropriate field. Very much like this example of a custom validator that checks whether an entered value is positive:
class PositiveValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if value and BigDecimal.new(value).negative?
record.errors[attribute] << 'is negative'
end
end
end
And then in the model reference it with:
validates :total, positive: true, allow_blank: true
However, this is ofcourse for validations, which shouldn't modify data. I tried to do this with concerns, but from the examples I have seen I should already know which fields are being transformed.
Do I really need to write in each model something like this:
before_validation lambda {self.total.gsub!(',', '.') if self.total}
or is there a more elegant Rails-like solution?
edit: again, note that field name 'total' might be different in each model
You could use custom type-casting starting from Rails 4.2
class LocalFloat < ActiveRecord::Type::Float
def cast_value(value)
value = value.gsub(',', '.') if value.is_a? String
super(value)
end
end
class Order < ApplicationRecord
attribute :total, LocalFloat.new
end
o = Order.new total: '5,5'
o.total # => 5.5

Rails validates :username, exclusion

validates :username, exclusion: { in: %w(about account root etc..)
I am using the above to disallow users from using reserved usernames but they are still able to use them between underscores (I do allow underscores in usernames)
Is there anyway I can make rails validate the reserved username even if it is before or after an underscore?
Thanks
You could create a method to do your validations for you and use plain old ruby in that. As you can see in the docs here
This would look something like this for you:
validate :my_validation_method
def my_validation_method
errors.add(:username, :exclusion) if some_condition
end
What this does is say that the model needs to be validated with the my_validation_method as well as all your normal other validations. You then manually add the field that is in error (in your case :username) to the errors of the model, thus it fails validation.
Also note the validate rather than validates.
Your other question is basically how to check whether an entered value includes some words. You could do this like so:
def my_validation_method
forbid = %w(luke darth artoo fry bender)
errors.add(:username, :exclusion) if forbid.find { |w| username.include?(w) }
end
Here I added a condition to the adding of the error where we loop through each word in the forbidden list and check if username includes this word. Note that "blablaluke" would fail too! So it is not completely what you'd want. But you can play around with this yourself of course.
The level of normalization you do (e.g. stripping away other characters) could give you more control, like preventing ad_min, etc.
Update:
You can strip away characters like so:
username.tr('-_+$^&', '')
You can add whatever you want to strip away to that first string in tr.
according to the rails docs this way you can only check if a value is in a set of given values.
This helper validates that the attributes' values are not included in a given set.
i would just write a custom validation method - you can do whatever you want in there.
I'd probably use a Regex. Like this one:
validates_format_of :username, :with => /\A(([ _]*)(?!(about|admin|root|etc)[ _])[^ _]+)+([ _]+|\z)\z/

Possible bug in rails conditional validation?

I have a form that allow me to edit an entry. I have a secondary set of attributes that must all be here if one of them have a value. I use conditional validates to do so :
validates_presence_of :raison_sociale,:nom_contact,
:prenom_contact,:telephone,
if: (:nom_contact?||:raison_sociale?||
:prenom_contact? || :telephone?)
But the strange thing is, those 4 fields are not evaluated the same way! If i remove nom_contact, it save. But if i remove it and telephone, it fails.
What i observed is that it was ignoring a blank field if it was the first one in the if condition! As soon as i put prenom contact as first condition i cannot save without nom_contact, but now it's prenon_contact that is ignored!
Why does my conditions behave strangly and what can i do?
What ca i do to avoid that?
You could put the if validation in a proc.
"Symbol-only" conditional validation expects a symbol for the name of a method that will be called.
You're including conditionals, which AFAIK won't work without being in a proc.
You can do some combinations by using an array, as per the docs:
class Computer < ActiveRecord::Base
validates :mouse, presence: true,
if: ["market.retail?", :desktop?]
unless: Proc.new { |c| c.trackpad.present? }
end
But those are and, not or.
I'd probably wrap it up in a method anyway; IMO it's a bit long for an in-line block, but that's more a matter of opinion.

Validates uniqueness of :link

I have a url field named link in my model with the following validation
validates_uniqueness_of :link, :case_sensitive => false
When I put "http://stackoverflow.com", it goes well.
Now when I put "https://stackoverflow.com/" (with the trailing slach), this is also accepted as unique.
But I want it to be invalid though there is "/" at the last?
I'd suggest that you normalize your URLs (add/strip trailing slash, etc. see http://en.wikipedia.org/wiki/URL_normalization) before storing them in the DB and even before validation.
validates_uniqueness_of :link, :case_sensitive => false
before_validation :normalize_urls
def normalize_urls
self.link.strip!
self.link.gsub!(/\/$/,'')
end
This isn't quite what you were asking for but if you don't store normalized URLs, you'll have to query your DB for all possible variations during validation and that could quickly get expensive.
You could always do a custom validator (by using the validate method, for example).
It might look something like this:
class MyModel < ActiveRecord::Base
validate :link_is_unique
def link_is_unique
#Clean up the current link (removing trailing slashes, etc)
link_to_validate = self.link.strip.gsub(/\/$/,'')
# Get the current count of objects having this link
count = MyModel.count(:conditions => ['link = ?', link_to_validate])
# Add an error to the model if the count is not zero
errors.add_to_base("Link must be unique") unless count == 0
end
end
You could then add other logic to clean up the link (i.e. check for http://, www, etc.)
You can customize validations. See this railscast.

Resources