Datamapper not accepting regex - ruby-on-rails

I'm using a regex to validate a form field in my sinatra app that's being sent to my db using the data_mapper gem. The code I'm using for the field in my model is:
property :price, Float, :required => true, :format => /\$?\d{0,3}\.{1}\d{2}/
And it's being saved from the params:
b.price = params[:price]
I keep getting an invalid format error when I try to save, though. I checked my regex with rubular and it seems to be working correctly. Anyone have any idea what's going wrong?

It's not perfect, but here's what I'm currently doing as a solution:
property :price, Float, :required => true
validates_format_of :price, :with => /\$?\d{0,3}(\.{1}\d{2})?/
And then:
c = params[:price]
c[0] == "$" ? b.price = c[1,7] : b.price = c
So if there's a "$" I'm just saving the number to b.price without it, otherwise the whole thing gets set to b.price. I feel like there should be a better way...

Related

Validate User email to match one of two regex's?

In my Rails app college students with either #berkeley.edu or #uw.edu email addresses can register. I have the regex for validating both ready but since I need to check the email address the user enters to see which one it matches I think I need to create one regex, but I don't know how. Here are my two regex's:
berkeley_regex = /\A[\w+\-.]+#berkeley\.edu\z/i
uw_regex = /\A[\w+\-.]+#uw\.edu\z/i
And my validate:
validates :email, :presence => true, :uniqueness => true, :format => {:with => berkeley_regex}
Now, what would the regex to check against both but only match against one look like?
Can't you just validate against something like /\A[\w+\-.]+#(berkeley|uw)\.edu\z/i and be done with it? If you really need to later determine which it is, make a method that just checks the back part, or returns the match, or whatever...
First I think your regex should be changed from [\w+\-.] to [\w+\-\.]
The validation could be
validates :email, format: { with: "\A#{berkely_regex}|#{uw_regex}\z/i" }
but you'll need to remove the flags ( \A, \z, /i ) from the vartiables

Simple rails format validation not firing

I'm building an app where users can create url slugs for their profile. To make sure the slugs are valid I've added a validation in the User model for slugs:
validates :slug, :uniqueness => true, :format => { :with => /[a-z]+/ }, :allow_nil => true, :allow_blank => true
However, validation seems to pass, regardless of what format the slug string is, for example:
u.slug = 'jlskdf .jc oi/slkdjfie\*asdf&(*&*ss%&'
=> "jlskdf .jc oi/slkdjfie\\*asdf&(*&*ss%&"
u.save
=> true
Apparently it doesn't matter what I change the regex to either, everything passes. I've tried this format as well:
validates_format_of :slug, :with => /[a-z]+/
which gives the same results. Anyone have any ideas of what could be happening?
Your regular expression isn't anchored, so the pattern matches as long as it contains at least one letter a-z. Anything else is valid. Add \A and \z to the beginning and end to prevent matching any substring within the larger input.
:with => /\A[a-z]+\z/

Ruby Regex for price

This one fails when a zero is at the end
12.12 passes
5.51 passes
12.50 fails
12.60 fails
price_regex = /^\d+(\.\d{2})?$/
why? and how do I fix it?
Some more info
in _form.html.erb
<p>
<%= f.label :price %><br />
<%= f.text_field :price %>
</p>
in menu_item.rb
price_regex = /^\d+(\.\d{2})?$/
validates :price, :presence => true,
:format => { :with => price_regex }
in menu_items_controller.rb
def create
#menu_item = MenuItem.new(params[:menu_item])
if #menu_item.save
respond_with #menu_item, :location => menu_items_url
else
flash[:notice] = "Not Saved"
end
end
price is a decimal in the database with a precision of 2.
You say that price is "a decimal in the database with a precision of 2". That means that price is being represented as a BigDecimal in Ruby and the regex test will be done on the string form of that BigDecimal. A little bit of experimentation will clarify things:
> p = BigDecimal.new('12.50')
=> #<BigDecimal:12a579e98,'0.125E2',18(18)>
> p.to_s
=> "12.5"
And so your regex will fail. You shouldn't be using a regex for this at all, regexes are meant for strings but you're checking a number. You should be able to keep using your regex if you allow for the conversion:
/^\d+(\.\d{1,2})?$/
I'm using Rails 3 with the client_side_validations gem, which means I need a Regexp that works both in Ruby and Javascript. I also have a clear delineation between frontend and backend format--The user should never be able to enter "$12.5", but once it hits the server, I don't care about the trailing 0.
My solution was to add a core extension (in my case, for Float, but BigDecimal would probably be more appropriate in most cases):
class Float
def can_convert_to_i_with_no_loss_of_precision
(self % 1).zero?
end
alias_method :to_s_with_loss_of_trailing_zeroes, :to_s
def to_s
if can_convert_to_i_with_no_loss_of_precision
to_i.to_s
else
"#{to_s_with_loss_of_trailing_zeroes}#{0 if (self * 10 % 1).zero?}"
end
end
end
Now I can use this in a Model, and it plays nicely on the front end (Javascript doesn't convert it to a Float, so the user will always be forced to enter 2 digits after the decimal) and on the backend (where ActiveModel's FormatValidator will call to_s and the core extension will know when to add the trailing 0):
validates :price, :format => { :with => /^\d+(\.\d{2})?$/, :allow_blank => true }
The regex looks fine to me. I tested it at Rubular with the inputs you mentioned and a few more, and it captures all of them correctly.
The problem is likely with some other part of the code. Maybe you are using price = <regex>, whereas you should be using price =~ <regex> to match a string with a regex.

Rails : Validates_format_of for float not working

I am new at Ruby on Rails.
I was trying to validate format of one of the attribute to enter only float.
validates :price, :format => { :with => /^[0-9]{1,5}((\.[0-9]{1,5})?)$/, :message => "should be float" }
but when I enter only character in price, it accepts it and show 0.0 value for price.
can anybody tell, what is wrong in this or why this happens?
This is my solution,
validates :price,presence:true, numericality: {only_float: true}
when you fill in for example 7 it automatically transfer the value to 7.0
For rails 3:
validates :price, :format => { :with => /^\d+??(?:\.\d{0,2})?$/ },
:numericality =>{:greater_than => 0}
A float is a number and regular expressions are for strings.
It appears that when you enter a string for the float, it gets converted as 0.0 automatically by Rails.
Do you have a default (0.0) on the column? If yes, then you may try removing it and use validates_presence_of :price only.
Something to try: instead of putting the string directly into the price column, put it into a price_string attr and use a before_save callback to try to convert the string to price. Something like that:
attr_accessor :price_string
before_save :convert_price_string
protected
def convert_price_string
if price_string
begin
self.price = Kernel.Float(price_string)
rescue ArgumentError, TypeError
errors.add(ActiveRecord::Errors.default_error_messages[:not_a_number])
end
end
And in your form, change the name of the text_field to :price_string.

How to validate a rails model against the original value but store the processed value

I am interacting with a time duration in a rails form, currently it is a text box and the format requires MM:SS
I have the validator:
validates_format_of :time, :with => /^[0-9]?[0-9]{1}[:][0-9]{2}$/, :allow_nil => true, :allow_blank => true, :message => 'format must be MM:SS'
though I want to store this in the database as an integer(seconds) to make it easier to do reporting on that field.
I overwrote the accessors as:
def time=(new_time)
parts = new_time.split(':')
write_attribute(:time, (parts[0].to_i * 60) + parts[1].to_i)
end
def time
Time.at(read_attribute(:time).to_i).gmtime.strftime('%R:%S')
end
but it ends up sending a validation error since the time attribute is just an integer after it gets set by the time= method.
How do store a duration value in the database in seconds but still enforce validation in a different format (MM:SS)?
I don't know if this is the best solution, but I believe you could use after_validation like the following:
after_validation :convert_time_to_integer
def convert_time_to_integer
parts = self.split(':')
write_attribute(:time, (parts[0].to_i * 60) + parts[1].to_i)
end

Resources