I'm getting a lot of submissions to my publicly editable site in different languages, but would like to limit it to just English characters. Is there any simple way to do this? Should I just do a validation with a regex limiting characters, or are there any common issues with that method?
Note: The text will not contain HTML or any other markup. It should just be plain text, maybe with common characters like dashes, dots, etc.
validates :comment, format: { with: [a-zA-Z0-9\s]+, on: :create } - or other action
It sounds like your problem maybe spam, so there are better solutions you can use. If this is true, see the end of this answer for details.
I commented that depends on which languages you want to block. A dozen or more languages use a latin charset, and even more use a latin charset subset. English uses accents too, such as the word naïve. So I do not recommend you try and do this.
If you must, and you want to block none-latin characters, you can write a custom validator.
class LatinCharsetValidator < ActiveModel::EachValidator
# Regex taken from this answer http://stackoverflow.com/a/13671443/276959
LATIN_CHARSET_REGEX = /[\p{L}&&[^a-zA-Z]]/
def validate_each(object, attribute, value)
object.errors.add(attribute) if value =~ LATIN_CHARSET_REGEX
end
end
You can then call this validator in your model as such:
class Comment < ActiveRecord::Base
validates :comment, latin_charset: true
end
This assumes that your users are legitimately trying to comment. If not, you can use the regex to skip the comment creation without providing feedback.
I believe this is a bad idea, though. You can't control what people end up writing or pasting in the text area, and you might end up blocking legitimate comments. What if someone wants to include a foreign word while trying to explain it? It's better to politely ask your users to only comment in English.
If your problem is spam, however, you are better off implementing a honeypot or some other type of spam protection.
Related
I defined a custom EachValidator to see if an attribute has leading or trailing whitespace. I know you can add it to the model like so:
validates :name, whitespace: true
But in the controller I want to call just run just the whitespace validator for some form feedback.
I can run it like this:
Validators::WhitespaceValidator.new(attributes: :name).validate_each(obj, :name, obj.name)
Is there a shorter way to call the specific validator? Like you can do user.valid? but that runs all of the validations. I only want the whitespace validator on the name attribute.
Since you did not come here to be told that your idea is bad and people will hate it: here is an idea that you can play with: :on
https://guides.rubyonrails.org/active_record_validations.html#on
validates :name, whitespace: true, on: :preview
and then in the controller:
def something
#model.valid?(:preview)
end
If you want to run the validation also on createand update you can specify something like
on: [:create,:update,:preview]
(Thanks engineersmnky)
Also: I don't think that giving early feedback to users, before they actually save the record is a bad idea if properly implemented.
This feels like trying to solve a problem (leading/trailing whitespace) by creating a new problem (rejecting the object as invalid), and I agree with the comment about this being a brittle strategy. If the problem you want to solve here is whitespace, then solve that problem for the specific attributes where it matters before saving the object; check out
Rails before_validation strip whitespace best practices
I'm implementing a comment section on my blog and was wondering if there was a way to prevent users from submitting a name such as admin or [company name] to prevent people from trolling or otherwise wrong-doing.
I am using this REGEX to validate emails making sure they are properly formatted: VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
I'm just not sure if this is the same approach I need to be taking or if there are built in ways to prevent specific strings from being entered into a form input.
Thanks in advance for any help!
There are several ways you could do this, depending on how you want your front-end to behave. The simplest way would be to do the validation in the front-end, either with simple HTML-5 form validation, or with javascript. For HTML-5 validation you can use the pattern attribute on an input type="text" (which you could use a text_field_tag to generate in rails). This attribute accepts a regex as it's value, which you could use to prevent the input of certain key-words. You can read more about this here https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Form_validation
You could also do the validation in the back-end, either in the controller directly (hard to say what exactly you need but something like as a simple example)
if (params[:my_input_form].split & bad_words_array).any?
flash[:error] = "you entered a bad word"
redirect_to the_same_page_path
end
note: the & in this context is giving the intersection of the two arrays and will return a non-empty array if there is at least one element in common between the arrays (in this case, if any of the words entered in your input are in the bad_words array).
If you want to do it in the back-end and it's more complicated I would probably move the validation into the model as a custom validator.
You can use gem obscenity. It gives you ability to specify black list words and they'll be replaces with [censored] string
An alternative to what has already been proposed is to create a custom validation on your model with a regular expression.
For example:
validate :bad_words
def bad_words
if (/admin|sony/i.match(self.name))
errors.add(:name, "contains a word not allowed")
end
end
You should generate a regular expression that suits your needs, but it is recommended to use the regexp i modifier to do a case-insensitive search
I hope it helps!
I am making a simple retail commerce solution, where there are prices in a few different models. These prices contribute to a total price. Imagine paying $0.30 more for selecting a topping for your yogurt.
When I set the price field to
t.decimal :price, precision:8, scale:2
The database stores 6.50 as 6.5. I know in the standard rails way, you call number_to_currency(price) to get the formatted value in the Views. I need to programmatically call the price field as well formatted string, i.e. $6.50 a few places that are not directly part of the View. Also, my needs are simple (no currency conversion etc), I prefer to have the price formatted universally in the model without repeated calling number_to_currency in views.
Is there a good way I can modify my getter for price such that it always returns two decimal place with a dollar sign, i.e. $6.50 when it's called?
Thanks in advance.
UPDATE
Thanks everyone.
I've elected to use Alex's approach because it seems very 'hackish' to do the includes just for formatting the number. Using his approach, I did:
def price_change=(val)
write_attribute :price_change, val.to_s.gsub(/[\$]/,'').to_d
end
def price_change
"$%.2f" % self[:price_change]
end
Cheers.
UPDATE 2
Caveat Emptor. Once you do this, you lose the ability to do operations to the number because it's now a string.
Please beware if anyone is facing the same problem as me.
Just add a method in your model which is named like your attribute in the database like:
def price
"$%.2f" % self[:price]
end
which gives you full control over the formatting or use the Rails provided helper method
def price
ActionController::Base.helpers.number_to_currency(self[:price])
end
this should do the trick.
hope it helps!
You can use the helpers module, but you should not include the whole module, because it includes a lot of methods which you may not really need or overrides some of yours. But you can use them directly:
ActionController::Base.helpers.number_to_currency(6.5)
#=> "$6.50"
You could also define a method for the helpers, so you can easily use them.
def helpers
ActionController::Base.helpers
end
"#{helpers.number_to_currency(6.5)}"
Have a look at this railscast
I'd suggest going with a Presenter approach, like Draper (see this reailscast) does.
Another solution would be to implement your own method in your model, i.e. formatted_price and do the formatting on your own, (i.e. with the ActionView::Helpers::NumberHelper module). But since models represent the plain data in your rails application, it's kinda shady doing something like this and it interferes with the convention over configuration approach, I think.
try
include ActionView::Helpers::NumberHelper
to your model
I've ran into various problems with various sites over the years with users putting spaces at the start/end of string and text fields. Sometimes these cause formatting/layout problems, sometimes they cause searching problems (ie search order looking wrong even though it isn't really), sometimes they actually crash the app.
I thought it would be useful, rather than putting in a bunch of before_save callbacks as i have done in the past, to add some functionality to ActiveRecord to automatically call .strip on any string/text fields before saving, unless i tell it not to, eg with do_not_strip :field_x, :field_y or something similar at the top of the class definition.
Before i go and figure out how to do this, has anyone seen a nicer solution? Just to be clear, i already know that i can do this:
before_save :strip_text_fields
def strip_text_fields
self.field_x.strip!
self.field_y.strip!
end
but i'm looking for a nicer way.
cheers, max
Here's a handy module that you could drop into lib and include in your models. It doesn't have the exceptions that you mentioned, but it looks for a strip! method which might be good enough. You could add the exceptions feature fairly easily, if needed.
# lib/attribute_stripping.rb
module AttributeStripping
def self.included(context)
context.send :before_validation, :strip_whitespace_from_attributes
end
def strip_whitespace_from_attributes
attributes.each_value { |v| v.strip! if v.respond_to? :strip! }
end
end
Use like this:
class MyModel < ActiveRecord::Base
include AttributeStripping
# ...
end
UPDATE (9/10/2013):
Revisiting this answer a couple of years later, I see how the winds have changed. There's a cleaner way to do it now. Create a module like this:
module AttributeStripper
def self.before_validation(model)
model.attributes.each_value { |v| v.strip! if v.respond_to? :strip! }
true
end
end
and set its method to be invoked at the right time in your model:
class MyModel < ActiveRecord::Base
before_validation AttributeStripper
# ...
end
This module is easier to test since it's not a mixin.
I have dealt with these sort of data integrity issues in various applications.
I used to manipulate the input like that.
But now, the best advice I have actually seen and followed is to store whatever the user types.
Then do post-processing on the backend to do the strip.
Create additional database fields (destripped) if you really want it in the database model table.
The main reason for this is one (primary) thing - when users want to revisit their data, i.e. edit, they're usually gonna expect to see what they typed in. A secondary reason is that you will avoid the possibility that your strip doesn't work right and either mangles the data or actually throw an error.
I've written a plugin for this purpose some time ago. I haven't tried it in a while and it doesn't have tests - so no guaranties that it still works. The upside would be a clean model:
class Story < ActiveRecord::Base
strip_strings :title, :abstract, :text
end
NOT a Rails 3 issue
In a Contact model I have a company_name attribute. For reasons that don't matter to this question, I want to prohibit an ampersand character. We have a lot of clients with ampersands in their company name, and users forget they aren't allowed to use this character.
This isn't an html sanitize issue. I don't care about whitespace or CDATA or anything. The entries in this field are plain text and I don't want an ampersand to be entered in this one field in any way, shape or form.
I assume a validation on the model is the way to go. I have tried validates_exclusion_of. I have tried validates_format_of. No success. I'm unsophisticated when it comes to regex, so I might be doing things very wrong. But the bottom line is - I need to prevent a user from entering that "&" character in the company_name field.
Thanks a million.
Steve
validates_format_of :company_name, :with => /\A[^&]*\Z/
But probably you could just remove ampersands before saving record:
before_save :strip_ampersands
def strip_ampersands
company_name.gsub!("&", "")
end