Attributes of the helper validates_length_of - ruby-on-rails

Is there any attribute in the helper validates_length_of that makes the field tested accept only one of 2 fixed sizes, such as 10 and 12?
In case, if the user sent the value 11 will not be accepted.
I already researched some attributes, but found nothing
The code I tried to run was this:
validates_length_of :field, is: 14 || 18
The code snippet does not produce an error, but only takes the first value

In your code || is ruby boolean or operator, it returns first argument if it is non-falsey and the second otherwise.
Length validator does not accept array for is in current rails, so for your case you'll have to write a custom validator:
validate :length_of_my_field
private def length_of_my_field
errors.add(:field, "should have length of 12 or 18") unless [12, 18].include?(field.size)
end

Related

Validate phone-number format in Rails 4 - REGEX

I'm working on a small project where the user should type the phone numbers, so I would like to validate that information using the "ClientSideValidations" gem.
validates_format_of :telcasa, :celular, :tel_flia, :tel_trab, :tel_ref_2, :tel_ref_1,
length: { in: 10 },
:with => /\A(\d{10}|\(?\d{3}\)?[-. ]\d{3}[-.]\d{4})\z/,
:message => "Formato invalido"
But, for the region where this project is going to be used I have to validate the three first numbers of the phone that correspond to the area code ("809"/"829"/"849"). How can I validate that the user correctly typed the phone number with one of the three area codes?
Change /\A(\d{10}|\(?\d{3}\)?[-. ]\d{3}[-.]\d{4})\z/ to:
/\A(\(?(809|829|849)\)?[-. ]\d{3}[-.]\d{4})\z/
I took the liberty of dropping the part where you are matching any ten digit number - not sure why it was there or how it should be used in your context.
You can write some custom validation
validate do
valid_phone_codes = [ "007", "042", ...]
valid_phone_codes.each do |valid_code|
# Also handle optional parenthesis
return true if self.phone_number.starts_with?(valid_code, "(#{valid_code})")
end
errors.add(:phone_numbers, "Must start with a valid country code (one of #{valid_phone_codes.join(', ')}")
false
end
Or if you prefer, you can declare this code in a function def valid_country_codes, and then add a line
validate :valid_country_codes

In Rails, Using Mongoid, How do I find all Models with a valid (not nil) has_one reference?

So I have a two models like this
class ModelParent
include Mongoid::Document
field :name, :type => String
has_one :model_child
end
class ModelChild
include Mongoid::Document
field :name, :type => String
belongs_to :model_parent
end
Assuming I have an persisted instance of ModelParent called mp in the rails console
mc = mp.create_model_child(:name=>"child")
and then do
mp.model_child
it returns a valid object
however if I search for it like this:
ModelParent.where(:model_child.ne => nil).length
it returns 0
I've tried creating model_child and then assigning it, also using build_model_child(), and each method shows model_child is clearly in the parent, however the query for not nil (.ne) fails to find all ModelParents with children.
What am I doing wrong?
Update:
Answering my own question. I'm still unsure why the :model_child.ne => nil is not working, however...
I solved the problem by coding something like this:
def self.with_child
user_ids = ModelChild.all.only(:model_parent_id).map(&:model_parent_id)
return ModelParent.where(:_id.in => user_ids).all
end
It is not working as foreign key is stored on belongs to side of the relationship. So, in your case ModelChild collection will have a field model_parent_id and not the other way around. I guess you had already figured that out, but instead of solving it the way you did, I would suggest you switch around the has_one and belongs_to associations and then use:
ModelParent.where(:model_child_id.ne => nil)
I found another way, but I don't know if is more efficient or less: you can use the reject method.
For example:
ModelParent.all.reject{ |r| r.model_child.nil?}
I wouldn't rely on the .ne in such cases, I found that .ne method isn't always working good:
For example try to put a string named "false" and try to search for it with .ne ;-)
I found out that for such cases the best way to find the proper models reliably is to filter by native Mongo $type
ModelParent.where(:model_child_id => { :$type => 7 })
Below the list of known types, found this much more efficient to find malformed fields
Double 1
String 2
Object 3
Array 4
Binary data 5
Undefined (deprecated) 6
Object id 7
Boolean 8
Date 9
Null 10
Regular Expression 11
JavaScript 13
Symbol 14
JavaScript (with scope) 15
32-bit integer 16
Timestamp 17
64-bit integer 18
Min key 255
Max key 127

How are symbols used to identify arguments in ruby methods

I am learning rails and going back to ruby to understand how methods in rails (and ruby really work). When I see method calls like:
validates :first_name, :presence => true
I get confused. How do you write methods in ruby that accept symbols or hashes. The source code for the validates method is confusing too. Could someone please simplify this topic of using symbols as arguments in ruby class and instance methods for me?
UPDATE:
Good one #Dave! But What I was trying out was something like:
def full_name (:first_name, :last_name)
#first_name = :first_name
#last_name = :last_name
p "#{#first_name} #{last_name}"
end
full_name("Breta", "Von Sustern")
Which obviously raises errors. I am trying to understand: Why is passing symbols like this as arguments wrong if symbols are just like any other value?
Symbols and hashes are values like any other, and can be passed like any other value type.
Recall that ActiveRecord models accept a hash as an argument; it ends up being similar to this (it's not this simple, but it's the same idea in the end):
class User
attr_accessor :fname, :lname
def initialize(args)
#fname = args[:fname] if args[:fname]
#lname = args[:lname] if args[:lname]
end
end
u = User.new(:fname => 'Joe', :lname => 'Hacker')
This takes advantage of not having to put the hash in curly-brackets {} unless you need to disambiguate parameters (and there's a block parsing issue as well when you skip the parens).
Similarly:
class TestItOut
attr_accessor :field_name, :validations
def initialize(field_name, validations)
#field_name = field_name
#validations = validations
end
def show_validations
puts "Validating field '#{field_name}' with:"
validations.each do |type, args|
puts " validator '#{type}' with args '#{args}'"
end
end
end
t = TestItOut.new(:name, presence: true, length: { min: 2, max: 10 })
t.show_validations
This outputs:
Validating field 'name' with:
validator 'presence' with args 'true'
validator 'length' with args '{min: 2, max: 10}'
From there you can start to see how things like this work.
I thought I'd add an update for Ruby 2+ since this is the first result I found for 'symbols as arguments'.
Since Ruby 2.0.0 you can also use symbols when defining a method. When calling the method these symbols will then act almost the same as named optional parameters in other languages. See example below:
def variable_symbol_method(arg, arg_two: "two", arg_three: "three")
[arg, arg_two, arg_three]
end
result = variable_symbol_method :custom_symbol, arg_three: "Modified symbol arg"
# result is now equal to:
[:custom_symbol, "two", "Modified symbol arg"]
As shown in the example, we omit arg_two: when calling the method and in the method body we can still access it as variable arg_two. Also note that the variable arg_three is indeed altered by the function call.
In Ruby, if you call a method with a bunch of name => value pairs at the end of the argument list, these get automatically wrapped in a Hash and passed to your method as the last argument:
def foo(kwargs)
p kwargs
end
>> foo(:abc=>"def", 123=>456)
{:abc=>"def", 123=>456}
>> foo("cabbage")
"cabbage"
>> foo(:fluff)
:fluff
There's nothing "special" about how you write the method, it's how you call it. It would be perfectly legal to just pass a regular Hash object as the kwargs parameter. This syntactic shortcut is used to implement named parameters in an API.
A Ruby symbol is just a value as any other, so in your example, :first_name is just a regular positional argument. :presence is a symbol used as a Hash key – any type can be used as a Hash key, but symbols are a common choice because they're immutable values.
I think all replies have missed the point of question; and the fact it is asked by someone who is - I guess - not clear on what a symbol is ?
As a newcomer to Ruby I had similar confusions and to me an answer like following would have made more sense
Method Arguments are local variables populated by passed in values.
You cant use symbols as Arguments by themselves, as you cant change value of a symbol.
Symbols are not limited to hashes. They are identifiers, without the extra storage space of a string. It's just a way to say "this is ...."
A possible function definition for the validates call could be (just to simplify, I don't know off the top of my head what it really is):
def validates(column, options)
puts column.to_s
if options[:presence]
puts "Found a presence option"
end
end
Notice how the first symbol is a parameter all of its own, and the rest is the hash.

Rails Custom Validation error message

Without getting into all the javascript for a textfield, I'd like the following validation:
validates_length_of :brief_description,
:maximum=>250,
:message => "Brief is #{self.brief_description.length} long (max is 250)"
But the self.brief_description reference in the message doesn't work. What's the best way to do this?
I tried
#{params[:brief_description].length}
as well, but the model doesn't know anything about params...
I could be wrong but I think you can try enclosing the string in single-quotes, and it might defer evaluating the string until runtime.
Short of that, you could write a custom validation (that doesn't use the validates_length_of macro) and do something like:
def validate_brief_description
errors.add :brief_description, "Brief is #{brief_description.length} long (max is 250)" if brief_description.length > 250
end

Rails / ActiveRecord: field normalization

I'm trying to remove the commas from a field in a model. I want the user to type a number, i.e. 10,000 and that number should be stored in the database as 10000. I was hoping that I could do some model-side normalization to remove the comma. I don't want to depend on the view or controller to properly format my data.
I tried:
before_validation :normalize
def normalize
self['thenumber'] = self['thenumber'].to_s.gsub(',','')
end
no worky.
http://github.com/mdeering/attribute_normalizer looks like a promising solution to this common problem. Here are a few examples from the home page:
# By default it will strip leading and trailing whitespace
# and set to nil if blank.
normalize_attributes :author, :publisher
# Using one of our predefined normalizers.
normalize_attribute :price, :with => :currency
# You can also define your normalization block inline.
normalize_attribute :title do |value|
value.is_a?(String) ? value.titleize.strip : value
end
So in your case you might do something like this:
normalize_attribute :title do |value|
value.to_s.gsub(',', '')
end
I think you're doing it right. This test passes:
test "should remove commas from thenumber" do
f = Foo.new(:thenumber => "10,000")
f.save
f = Foo.find(f.id)
assert f.thenumber == "10000"
end
And I used your code.
class Foo < ActiveRecord::Base
before_validation :normalize
def normalize
self['thenumber'] = self['thenumber'].to_s.gsub(',','')
end
end
Now, my schema is set up for thenumber to be a string though, not an integer.
Started
.
Finished in 0.049666 seconds.
1 tests, 1 assertions, 0 failures, 0 errors
If you wanted to store this in the db as an integer, then you definitely need to override the setter:
def thenumber=(value)
self['thenumber'] = value.to_s.gsub(',','').to_i
end
If you do it your way, with an integer column, it gets truncated by AR....
>> f.thenumber = "10,000"
=> "10,000"
>> f.thenumber
=> 10
That's a little-known thing with Ruby and integers... it auto-casts by truncating anything that's no longer an integer.
irb(main):004:0> i = "155-brian-hogan".to_i
=> 155
Can be cool for things like
/users/155-brian-hogan
#user = User.find_by_id(params[:id])
But not so cool for what you're doing.
So either change the col to a string and use the filter, or change the setter :)
Good luck!
The problem with doing it that way is that for a while, the non-normalized stuff will exist in the object; if you have code that works on the attributes before stuff gets normalised, then that will be a problem.
You could define a setter:
def thenumber=(value)
# normalise stuff here, call write_attribute
end
Unfortunately I think a lot of the Rails form stuff writes the attributes directly, which is one of the reasons I don't tend to use it.
Or you could normalise the params in the controller before you pass them through.
Does ruby let you interchange between a . and [''] ?
I don't know, I'll try later, but I think you are supposed to use .
self.thenumber = self.thenumber.to_s.gsub(',','')
You should return true from your before_validation method, otherwise if the expression being assigned to self['thenumber'] ends up being nil or false, the data will not be saved, per the Rails documention:
If a before_* callback returns false,
all the later callbacks and the
associated action are cancelled.
Ostensibly, you are trying to normalize here then check the result of the normalization with your Rails validations, which will decide if nil/false/blank are okay or not.
before_validation :normalize
def normalize
self['thenumber'] = self['thenumber'].to_s.gsub(',','')
return true
end

Resources