Rails inclusion validations - ruby-on-rails

I'm struggling to get a inclusion validation to work on a model so maybe some one could tell me what I'm missing here.
This is the model I have:
class Order < ActiveRecord::Base
ORDER_TYPES = %w{ Verkooporder Retourorder }
ORDER_TYPES.each_with_index do |meth, index|
define_method("#{meth}?") { type == index }
end
validates_inclusion_of :order_type, :in => %w{ Verkooporder Retourorder }
...
I also created a form that creates a dropdownbox using the constant array like this:
(I'm
= f.input :order_type, as: :select, collection: Order::ORDER_TYPES, label: 'Order type', include_blank: false
And I save it to my model like this:
#order.order_type = params[:order][:order_type]
So when I save my order model it always fails on validating the order_type.
Is there anybody who can point me out what I'm doing wrong?
PS: The order_type is an integer value field in my model.

The problem is that you are defining methods Verkooporder? and Retourorder?, but they're not being called from your validation because the :in is interpreting %w{ Verkooporder Retourorder} as an array of strings, i.e. [ "Verkooporder", "Retourorder"].
What you actually want to validate is that order_type is a number between 0 and the size of the ORDER_TYPES array, i.e. a string with a value between 0 and 1:
validates_inclusion_of :order_type, :in => %w{ 0 1 }
In this case you don't really need to define the boolean Verkooporder? and Retourorder? methods, unless you need them elsewhere.
UPDATE:
I realize now that your form will return order_type as a string in Order::ORDER_TYPES, which won't work with the validation above since the validation above is validating on integer-valued strings.
The way I've done this in the past is not to use an integer for order_type but a string. In that case, you can just validate with validates_inclusion_of :order_type, :in => ORDER_TYPES, and the select drop-down doesn't have to change. Is there any particular reason you're using an integer value field for order_type? Alternatively you could have the select return integer values for each order type.

Related

Rails: get values from length validator for a field in template

I havel a model with validators on some fields. Example:
class Notation < ActiveRecord::Base
validates :name, presence: true, uniqueness: true
validates_length_of :name, minimum: 5, maximum: 128
end
is there a way to get those values from minimum and maximum for the :name field in the template (because I want to show there the value of min and max length to the user, and want to do this dynamically, with the template reflecting the values from the model)?
In the rails console, I can do something like
Notation.validators_on(:name)
which outputs
#<ActiveRecord::Validations::PresenceValidator:0x00000005428420 #attributes=[:name], #options={}>, #<ActiveRecord::Validations::UniquenessValidator:0x0000000541b8d8 #attributes=[:name], #options={:case_sensitive=>true}, #klass=Notation (call 'Notation.connection' to establish a connection)>, #<ActiveModel::Validations::LengthValidator:0x0000000540baf0 #attributes=[:name], #options={:minimum=>5, :maximum=>128}>
If I get using the array's index
Notation.validators_on(:name)[2]
I have:
#<ActiveModel::Validations::LengthValidator:0x0000000540baf0 #attributes=[:name], #options={:minimum=>5, :maximum=>128}>
but I was wondering if there is another way, maybe passing the type of validation I'm 'queryng', because I can't rely on the order of the array.
To get the validator, you can filter:
options = Notation.validators_on(:name)
.select { |v| v.is_a? LengthValidator }
.first.options
Then you can get options[:maximum] etc.
In case there is no such validator, you might have to rescue from the code, as otherwise the first will fail.

Validating number field that has capital letters in Ruby

I am trying to validate the entry a user makes in an amount field.
The field is amount_money
This field is a string which is validated on form submission
monetize :amount, :as => :amount_money
validates :amount, numericality: {only_integer: true}
validates :amount_money, numericality: {greater_than_or_equal_to: 0}
validate :amount_money_within_limit
validate :is_a_valid_number
I want to ensure there are no letters or symbols and that the amount is in an acceptable range.
the code to do this is
def amount_money_within_limit
if amount_money && amount_money.cents > 10_000_00
errors.add(:amount_money, 'cannot exceed $10,000.')
end
if amount_money && amount_money.cents < 1_00
errors.add(:amount_money, 'Problem with Amount')
end
end
this works great for input numbers, of numbers and letters, of letters, of special characters but
If I try Bob - the validation kicks in
but if I try BBob - the validation is bypassed.
If the input contains 2 Capital letters next to each other - it fails.
I've tried a downcase - but that doesn't suit as the field is monetized (money gem) - and the downcase screws up if there is valid input.
If the input to the field contains two uppercase letters - all the validations are bypassed So something like AA is not caught by any on the above validations
Why don't you use regular expressions? Something like this:
def is_a_valid_number? amount_money
amount_money =~ /\d+/
end
It seems you have put 1 validation on the wrong field, you should've put validations only on amount field (your real db field), and not on the amount_money which is automagical field from rails-money gem. I'll apply their documentation on numerical validations to your case:
monetize :amount,
:numericality => {
:only_integer => true,
:greater_than_or_equal_to => 1_00,
:less_than_or_equal_to => 10_000_00
}
You won't need any other custom validations with this setup.

Validate number with rails 3

How can I validate if the input in a text field is a number? not_integer is not what I am looking for. It can be a decimal number.
You can check for numericality
validates :points, numericality: true
If you want a more general approach, you can use is_a?. The parent number class in Ruby is Numeric.
a = 4
a.is_a? Numeric
=> true
b = 5.4
b.is_a? Numeric?
=> true
c = "apple"
c.is_a? Numeric
=> false
d = "4"
d.is_a? Numeric
=> false
To restrict the user from entering non-numeric values at the form-level and avoid expensive server call just to check numericality.
Use this in the form:
<%= f.number_field :attribute_name, :step => 'any' %>
This will create an html element as below:
<input id="post_attribute_name" name="post[attribute_name]" step="any" type="number">
Upon form submission, the input value is checked at the form level. step = "any" will allow decimals.
I would also recommend adding validation at the Model level using,
validates :attribute_name, numericality: true ## As suggested by Justin Wood
This way you have double protection, i.e., one at the form-level itself and the other at Model level.

Rails3: Defining enum and using it as a custom type for db column

New to rails so not sure what the best approach is here. I want to define a simple c++ style enum which can then be used as a custom type in my db. The enum can be simulated with an array or a custom module but how do I go about turning that into a custom type for my table?
Here's a pattern I follow in rails:
In my model class, I add a module to hold the possible values of the column. Then I put them into an array and define validation against the array of possible values.
Imagine I have a column/attribute called status and it can be three possible values. I'd do this:
class MyModel < ActiveRecord::Base
# This validates that status can't be null
validates :status, :presence => true
# Define a module with all possible values
module Status
IN_DEVELOPMENT = 'in development'
DISABLED = 'disabled'
ACTIVE = 'active'
end
# Now create an array of possible status values, and add a validation
STATUSES = [ Status::DISABLED, Status::ACTIVE, Status::IN_DEVELOPMENT]
validates :status, :inclusion => { :in => STATUSES, :message => "%{value} is not a valid status value" }
end
Have you considered using the built-in enumeration support in your database? Lots of common RDMBSes have enum support, such as Postgres (see http://www.postgresql.org/docs/9.1/static/datatype-enum.html) and MySQL (see http://dev.mysql.com/doc/refman/5.5/en/enum.html). With that, you can directly create the type in your data store and then use it via one of the ActiveRecord plugins (such as enum_type for Postgres: https://github.com/riscfuture/enum_type).
Alternatively, you could use something like active_enum to structure the enumeration as you described and store fields as integers in the database.
Depending on how you plan to utilize this enum type in your code I've found that using scopes accomplishes close to the same thing along with an enum type in the database to ensure only specific values are set.
Example:
scope :trial, :conditions => { :utype => 'TRIAL' }
scope :registered, :conditions => { :utype => 'REGISTERED' }
scope :active, :conditions => { :status => 'ACTIVE' }
scope :abuse, :conditions => { :status => 'ABUSE' }

How to show a text field based on user's selection?

I have an array
ABC = ["A","B","C"]
<%= f.select :abc, model::ABC, :include_blank => true %>
If I select C, then I want to display an input field for "city" and "state". Otherwise, those fields should be hidden. Is there any simple way of doing this. I don't want to use jQuery or Ajax.
I don't know of a way to change what fields are being displayed without using javascript.
What you could do is always display the city and state fields, but only require them if the select menu is set to C. For example, define a validation rule that requires a field if the select menu is set to C. In your lib/ directory, make require_if_c_validator.rb
class RequireIfCValidator < ActiveModel::EachValidator
def validate_each object, attribute, value
if object.your_attribute_name == 'C' && value == nil
object.errors[attribute] < 'is required'
end
end
end
And then in your model, call it on city and state:
validate :city, :require_if_c => true
validate :state, :require_if_c => true

Resources