Rails model validation fails on integer big number input ("out of range ") - ruby-on-rails

I have Integer attribute in my model and I need back-end validation for that (it should be up to 7 digts only).
In my model I do some validations like:
validates :duration, format: { with: /([0-9]{1,7})/ }
validates :duration, numericality: { less_than_or_equal_to: 9999999 }
This works when I try to put letters into the form (it returns 'is not a number' error), works well when I put valid integer (11045555 - returns 'must be less than or equal to 9999999') BUT it crashes when I put really big number - let's say 11045555766666666666. Error is "11045555766666666666 is out of range for ActiveRecord::Type::Integer with limit 4"
How can I skip this crash when user puts that big number?
I use mysql2 DB.

You have to declare the limit on your migration file. By default, an integer is coded in 4 bytes, so the limit is 2147483647.
If you want to set a bigger limit, declare it like this
add_column :yourtable, :duration, :integer, :limit => 8
8 bytes have a 9223372036854775807 limit.
Just check How to specify the size of an integer in the migration script

Related

Rails: truncate text input instead of whining it's too big

I have a Rails 6.0.21 application which, like any normal application, accepts input from users.
Sometimes users type in by mistake or intentionally very long strings where not really supposed to, eg a random 9348913 character string in an input meant for email addresses which saves in a varchar(255) column.
When this happens Rails errors out:
Mysql2::Error: Data too long for column 'email'
I'm looking for a way for the framework to just truncate the data rather than whining it's too big and doesn't fit in.
I can't just truncate before_commit because some models have a lot of fields, I'd end up writing a few thousand lines of code to do this very simple thing.
Anyone know how to do it "automagically"?
You can validate the length of the attribute in your model
class User
validates :email, length: { maximum: 255 }
#validates_lenght_of :email, :maximum => 255 #rails3
end
In your view, limits the amount they can enter with the maximum length attribute:
<input maxlength="255".... >....
And if you have many fields build an array with all the attributes of type string, and if you want to customize the message you can also:
class User
ATTR_TO_VALIDATE = Todo.attribute_names.reject{|attr| Todo.columns_hash[attr].type != :string}
validates ATTR_TO_VALIDATE, length: { maximum: 255,
too_long: "%{count} characters is the maximum allowed" }
end

Ruby on Rails Decimal property validation

I have a business model with a decimal field. Its value must be between 0.00 and 999.99. So I have created a decimal field and add validations to the model.
class Order < ApplicationRecord
validates :price, format: { with: /\A\d+(?:\.\d{0,2})?\z/ }, numericality: { greater_than: 0, less_than: 1000 }
end
When I create the Order object with a decimal value which is greater than 1000 or less than 0, I get the error I expected and that's ok. I can see that
"numericality: { greater_than: 0, less_than: 1000 }" validations work as expected.
Howeber, when I try to create an Order object with a decimal value of 45.45554 or 45.45666, rails persists the object to the database with the price value of 45.45. I expect getting a format error, but it seems that
format validation doesn't work.
What I'm doing wrong ?
Any suggestions,
Thanks.
What is the precision of your price field? Via the docs:
precision Defines the precision for the decimal fields, representing the total number of digits in the number.
I'm assuming price only has a precision of 2, and that's why it's getting rounded.
The value is converted to decimal by the setter. Which means that format validation will always pass.
If you need validate the format use read_attribute_before_type_cast in a custom validation.
class Order < ApplicationRecord
validates :price, numericality: { greater_than: 0, less_than: 1000 }
validates :price_format
PRICE_REGEXP = /\A\d+(?:\.\d{0,2})?\z/.freeze
private
def price_format
unless read_attribute_before_type_cast('price') =~ PRICE_REGEXP
errors.add('price', 'must match the correct format')
end
end
end

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.

Use Ruby ActiveRecord Validates_Numericality to accept 20.00 as integer

We are working with web development firm to build a site. We have a field where we request that the user input the amount that they would like to invest. We asked for this field to be limited to whole numbers. For example, the user should be able to invest "20" or "20.00" but not "20.50".
The developer is using Ruby ActiveRecord Validate_numericality :only_integer which is restricting the input to "20". If a user inputs and submits the value "20.00" they receive an error telling them that they need to input an integer.
Is there a way to use ActiveRecrod validate_numericality to accept only numbers that are whole numbers, not necessarily only integers? The code is currently:
validates :principal, numericality: {greater_than_or_equal_to:MINIMUM_INVESTMENT_AMOUNT,
less_than_or_equal_to:MAXIMUM_INVESTMENT_AMOUNT,
:only_integer => true}
I am hoping that there is a numericality constraint that will allow 20.00 as a whole number.
you could do this
before_validation {|a| a.principal = principal.to_i}
This will convert it to an integer without modifying the validations but if you want to notify the user that they entered something that is not a whole number then I would remove the :only_integer and create a more flexible validation like
validates :principal, numericality:{greater_than_or_equal_to:MINIMUM_INVESTMENT_AMOUNT, less_than_or_equal_to:MAXIMUM_INVESTMENT_AMOUNT}
validate :principal_is_whole_number
def principal_is_whole_number
errors.add(:principal, "must be a whole number.") unless principal.to_i == principal
end

Validation to not allow both fields to be used issue

I have a Price model with 4 different fields:
t.decimal "amount"
t.decimal "amount_per_unit"
t.decimal "unit_quantity"
t.string "unit"
I'm trying to make a custom validation that allows either the amount or amount_per_unit fields (this includes unit quantity and unit) to be filled but not both of them. So to make a word diagram of what I mean.
amount = YES
amount_per_unit + unit + unit_quantity = YES
amount_per_unit (alone or amount.present) = NO
unit_quantity (alone or amount.present) = NO
unit (alone or amount.present) = NO
amount and amount_per_unit + unit + unit_quantity = NO
if you still are confused, just know that its either the amount itself thats filled in or the amount per unit fields that are (1 or 3).
So far I tried this validation in my Price model:
validates :amount, :numericality => true
validates :amount_per_unit, :numericality => true
validates :unit_quantity, :numericality => true
validates :unit, :inclusion => UNITS
validate :must_be_base_cost_or_cost_per_unit
private
def must_be_base_cost_or_cost_per_unit
if self.amount.blank? and self.amount_per_unit.blank? and self.unit.blank? and self.unit_quantity
# one at least must be filled in, add a custom error message
errors.add(:amount, "The product must have a base price or a cost per unit.")
return false
elsif !self.amount.blank? and !self.amount_per_unit.blank? and !self.unit.blank? and !self.unit_quantity
# both can't be filled in, add custom error message
errors.add(:amount, "Cannot have both a base price and a cost per unit.")
return false
else
return true
end
end
This validation doesn't work though as all fields are blank it results to a numericality error and if I fill all of them, it creates the price with all fields filled. What needs to be fixed?
I think your values are coming in as nil, not blank.
Try changing the second condition to:
elsif !self.amount.to_s.blank? and !self.amount_per_unit.to_s.blank? and !self.unit.to_s.blank? and !self.unit_quantity.to_s.blank?
Also, it seems you have a typo on the last condition on both statements (e.g. !self.unit_quantity instead of !self.unit_quantity.to_s.blank?
I hope that helps.

Resources