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.
Related
I am trying to get a date from a user and send it inside an email as plain text in the following format: "07/30/2015".
In order to do that, if the output I am getting is a string, I could just do:
Date.parse("2015-07-30").strftime("%m/%d/%Y")
The problem is, I am getting a FixNum.
The issues are many:
If I try to convert to a string to parse it with Date.parse, it becomes "2001".
If I apply the code I just wrote, Date.parse... it will throw 'invalid date'.
For instance:
(2016-02-13).to_s #=> "2001"
(2016-02-13).to_date #=> NoMethodError: undefined method `to_date' for 2001:Fixnum
Date.parse("2001").strftime("%m/%d/%Y") #=> invalid date
So if I can convert 2015-07-30 into "2015-07-30", it would work:
Date.parse("2015-07-30").strftime("%m/%d/%Y") #=> "07/30/2015"
Then I tried using date_select instead of date_field, but now the message arrives with those fields empty.
Any suggestions?
Here is my form:
= form_for #contact do |f|
= f.text_field :product_name
= f.date_field :purchase_date
= f.submit
Here is my code:
<%= message.subject %>
<% #resource.mail_form_attributes.each do |attribute, value|
if attribute == "mail_subject"
next
end
%>
<%= "#{#resource.class.human_attribute_name(attribute)}: #{Date.parse(value).class == Date ? Date.parse(value).strftime("%m/%d/%Y") : value}" %>
<% end %>
My controller:
class ContactsController < ApplicationController
before_action :send_email, except: [:create]
def create
#contact = Contact.new(params[:contact])
#contact.request = request
if #contact.deliver
#thank = "Thank you for your message!"
#message = "We have received your inquiry and we'll be in touch shortly."
else
#error = "Cannot send message. Please, try again."
end
end
def contact_page
end
def product_complaint
#the_subject = "Product Complaint Form"
end
private
def send_email
#contact = Contact.new
end
end
My model:
class Contact < MailForm::Base
# all forms
attribute :mail_subject
attribute :first_name, validate: true
attribute :last_name, validate: true
# product-complaint
attribute :best_by, validate: true, allow_blank: true # date
attribute :bag_code, validate: true, allow_blank: true
attribute :purchase_date, validate: true, allow_blank: true # date
attribute :bag_opened, validate: true, allow_blank: true # date
attribute :problem_noticed, validate: true, allow_blank: true # date
# all forms
attribute :message, validate: true
attribute :nickname, captcha: true
def headers
{
content_type: "text/plain",
subject: %(#{mail_subject}),
to: "xxxxx#xxxxxxx.com",
# from: %("#{first_name.capitalize} #{last_name.capitalize}" <#{email.downcase}>)
from: "xxx#xxxxx.com"
}
end
end
(2016-02-13).to_date #=> NoMethodError: undefined method `to_date' for 2001:Fixnum
youre getting this error because you dont have quotes around the value. i.e. its not a string, its a number that is having subtraction applied to it. this is being interpreted as
2016 - 2
2014 - 13
2001.to_date
it needs to be ('2016-02-13').to_date
if youre unable to get it as a string, can you post how you're getting it from the user to begin with? (a date field ought to be sending you a string to your controller, not a series of numbers)
You're not understanding something about receiving values from forms: You can NOT receive an integer, a fixnum or anything else other than strings. So, you can't have received 2016-02-13. Instead you got "2016-02-13" or "2016", "02" or "2" and "13" depending on the form. If you're running under Rails, then it got the strings, and through its meta-data understands you want an integer (which really should probably be defined as a string), and it converts it to an integer for you.
Either way, when you write:
(2016-02-13).to_s
(2016-02-13).to_date
you're propagating that misunderstanding into your testing. This is how it MUST be written because you need to be working with strings:
require 'active_support/core_ext/string/conversions'
("2016-02-13").to_s # => "2016-02-13"
("2016-02-13").to_date # => #<Date: 2016-02-13 ((2457432j,0s,0n),+0s,2299161j)>
You can create dates without them being strings though: Ruby's Date initializer allows us to pass the year, month and day value and receive a new Date object:
year, month, day = 2001, 1, 2
date = Date.new(year, month, day) # => #<Date: 2001-01-02 ((2451912j,0s,0n),+0s,2299161j)>
date.year # => 2001
date.month # => 1
date.day # => 2
Moving on...
Parsing dates in Ruby quickly demonstrates it's not a U.S.-centric language. Americans suppose all dates of 01/01/2001 are in "MM/DD/YYYY" but that's a poor assumption because much of the rest of the world uses "DD/MM/YYYY". Not knowing that means that code written under that assumption is doing the wrong thing. Consider this:
require 'date'
date = Date.parse('01/02/2001')
date.month # => 2
date.day # => 1
Obviously something "wrong" is happening, at least for 'mericans. This is very apparent with:
date = Date.parse('01/31/2001')
# ~> -:3:in `parse': invalid date (ArgumentError)
This occurs because there is no month "31". In the previous example of '01/02/2001', that misunderstanding means the programmer thinks it should be "January 2" but the code thinks it's "February 1", and work with that. That can cause major havoc in an enterprise system, or anything dealing with financial calculations, product scheduling, shipping or anything else that works with dates.
Because the code is assuming DD/MM/YYYY format for that sort of string, the sensible things to do are:
KNOW what format your users are going to send dates in. Don't assume, ever. ASK them and make your code capable of dealing with alternates, or tell them what they MUST use and vet out their data prior to actually committing it to your system. Or, provide a GUI that forces them to pick their dates from popups and never allows them to enter it by hand.
Force the date parser to use explicit formats of dates so it can always do the right thing:
Date.strptime('01/31/2001', '%m/%d/%Y') # => #<Date: 2001-01-31 ((2451941j,0s,0n),+0s,2299161j)>
Date.strptime('31/01/2001', '%d/%m/%Y') # => #<Date: 2001-01-31 ((2451941j,0s,0n),+0s,2299161j)>
The last point is the crux of writing code: We're telling the language what to do, not subjecting ourselves, and our employers, to code that's guessing. Give code half a chance and it'll do the wrong thing, so you control it. That's why programming is hard.
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...
I always get great big lines of code at the top of my Rails models. I am looking for suggestions for the best way to break them up with standard Ruby style. For example, one line I am looking at now is this:
delegate :occupation, :location, :picture_url, :homepage_url, :headline, :full_name, :to => :profile, :prefix => true, :allow_nil => true
What is the conventional style for breaking up these long method call lines?
The short answer is it depends.
Basics
For a start you can save few chars using the "new" Ruby hash syntax:
result = very_long_method_name(something: 1, user: user, flange_factor: 1.34)
vs.
result = very_long_method_name(:something => 1, :user => user, :flange_factor => 1.34)
Hash/Arrays
Sometimes you need to initialize an array or hash, especially for hashes it's nice to write them this way:
args = {
first_name: "Aldo",
email: "nospam#mail.example.com",
age: Float::INFINITY
}
The same hash on the same line would be (not as nice):
args = {first_name: "Aldo", email: "nospam#mail.example.com", age: Float::INFINITY}
Various method calls
Some methods require many params or these params have long names:
%table
%thead
%th
%td= t("first_name", scope: "activemodel.lazy_model.not_so_active_model", some_interpolation_argument: "Mr.", suffix: "(Jr.)")
In this case I would probably write it this way:
%table
%thead
%th
%td= t("first_name",
scope: "activemodel.lazy_model.not_so_active_model",
some_interpolation_argument: "Mr.",
suffix: "(Jr.)")
It's still not very beautiful but I guess is less ugly.
class person < ActiveRecord::Base
validates :n_cars, numericality: {
only_integer: true,
greater_than: 2,
odd: true,
message: t("greater_than_2_and_odd",
scope: "activerecord.errors.messages")
}
end
Again, not the most beautiful code on earth but It has some kind of structure.
Also, sometimes you could use variables to split lines. This is just an example, but basicly you name blocks of things (and sometimes after this you realise you can actually move that block in a method)
class person < ActiveRecord::Base
NUMERICALITY_OPTS = {
only_integer: true,
greater_than: 2,
odd: true,
message: t("greater_than_2_and_odd", scope: "activerecord.errors.messages")
}
validates :n_cars, numericality: NUMERICALITY_OPTS
end
Blocks
Speaking of blocks (closures):
User.all.map { |user| user.method_name }
can be written like this:
User.all.map(&:method_name)
If you have proper blocks try to use do-end instead of curly braces:
nicotine_level = User.all.map do |user|
user.smoker? ? (user.age * 12.34) : 0.1234
end
Conditional
Don't use the ternary if operator for complex things:
nicotine_level = user.smoker? ? (user.age * 1.234 + user.other_method) : ((user.age - 123 + user.flange_factor) * 0)
if user.smoker?
nicotine_level = user.age * 1.234 + user.other_method
else
nicotine_level = (user.age - 123 + user.flange_factor) * 0
end
If you have complex if statements like this:
if user.vegetarian? && !user.smoker? && (user.age < 25) && (user.n_girlfriends == 0) && (user.first_name =~ /(A|Z)[0-1]+/)
end
It's probably just better to move things in methods and make things not only shorter but also readable:
if user.healthy? && user.has_a_weird_name?
# Do something
end
# in User
def healthy?
vegetarian? && !smoker? && (age < 25) && (n_girlfriends == 0)
end
def user.has_a_weird_name?
user.first_name =~ /(A|Z)[0-1]+/
end
Long strings
Heredoc is your friend...I always need to google in order to get the syntax right but once you get it right is makes certain things nicer to read:
execute <<-SQL
UPDATE people
SET smoker = 0
OK, this is a very bad example.
SQL
Queries
I tend to do this way for simple cases:
# Totally random example, it's just to give you an idea
def cars_older_than_n_days(days)
Car.select("cars.*, DATEDIFF(NOW(), release_date) AS age")
.joins(:brand)
.where(brand: {country: "German"})
.having("age > ?", days)
end
Sometimes queries are even worst. If I use squeel and the query is very big I tend to use parenthesis like this:
# Again, non-sense query
Person.where {
first_name = "Aldo" |
last_name = "McFlange" |
(
age = "18" &
first_name = "Mike" &
email =~ "%#hotmail.co.uk"
) |
(
person.n_girlfriends > 1 &
(
country = "Italy" |
salary > 1_234_567 |
very_beautiful = true |
(
whatever > 123 &
you_get_the_idea = true
)
)
)
}
I'd say, if possible try to avoid complex queries and split them in smaller scopes or whatever:
scope :healthy_users, lambda {
younger_than(25).
without_car.
non_smoking.
no_girlfriend
}
scope :younger_than, lambda { |age|
where("users.age < ?", age)
}
scope :without_car, lambda {
where(car_id: nil)
}
scope :non_smoking, lambda {
where(smoker: false)
}
scope :no_girlfriend, lambda {
where(n_girlfriends: 0)
}
This would be probably the best way.
The reality
Unfortunately people tend to write long lines and it's bad:
Long lines are difficult to read (There is a reason if printed books don't have super-large pages)
It's true we mostly use 2 screens but when using things like git diff from the console having long lines is painful
Sometimes you work on your 13" laptop with less screen estate
Even if I love to work with 2 screens I like to split my editor to edit 2 files at the same time - long lines force me to use the horizontal scrollbar (most hated thing on earth)
Yes, you can enable word wrapping in your editor but it's still not as nice (IMHO)
I have a ruler in my editor so that I know when I'm about to cross the 80th char on the line.
But rarely cross the line by few chars it's actually nicer than split it.
Conclusion
There are several ways of keep lines under the 80s and often depends on the situation.
The problem with long lines is not just bad style, long lines are often a symptom of too much complexity.
Something along the lines of:
delegate :occupation, :location, :picture_url,
:homepage_url, :headline, :full_name,
:to => :profile, :prefix => true, :allow_nil => true
Or if you like to highlight the option hash (a reasonable thing):
delegate :occupation, :location, :picture_url,
:homepage_url, :headline, :full_name,
:to => :profile, :prefix => true, :allow_nil => true
The idea of leaving that all on a single line strikes me as a craptastic idea, it means you have to scroll an arbitrary amount in order to see what's being delegated. Ew.
I'd probably line stuff up a bit, too, maybe alphabetize.
delegate :full_name, :headline, :homepage_url,
:location, :occupation, :picture_url,
:to => :profile, :prefix => true, :allow_nil => true
If the file didn't have much/any other substantive content, I might put each method symbol on its own line, just to make editing easier. In a larger file, I wouldn't want to take up the space for that.
Edit 2022: I’d probably put each symbol on its own line, including the options. In the long run, it’s just easier to deal with, and I’ve grown lazier.
Not that I ever think about this kind of stuff.
Edit I guess I do :/
These days I might group the delegated methods by "similarity", roughly:
delegate :full_name, :headline,
:location, :occupation,
:homepage_url, picture_url,
to: :profile, prefix: true, allow_nil: true
My jury's hung on 1.9 hash syntax when the value is also a symbol; I think it looks funny. I'm also not sure where I'd indent it to–might lose it anyway during an IDE reformatting, but I kind of like how it looks above if I'm using the new syntax.
Although the question already has two great answers I'd like to refer future readers to the Ruby Style Guide for such matters.
Currently the Source Code Layout section has plenty of information on how to break lines in various situations:
# starting point (line is too long)
def send_mail(source)
Mailer.deliver(to: 'bob#example.com', from: 'us#example.com', subject: 'Important message', body: source.text)
end
# bad (double indent)
def send_mail(source)
Mailer.deliver(
to: 'bob#example.com',
from: 'us#example.com',
subject: 'Important message',
body: source.text)
end
# good
def send_mail(source)
Mailer.deliver(to: 'bob#example.com',
from: 'us#example.com',
subject: 'Important message',
body: source.text)
end
# good (normal indent)
def send_mail(source)
Mailer.deliver(
to: 'bob#example.com',
from: 'us#example.com',
subject: 'Important message',
body: source.text
)
end
# bad - need to consult first line to understand second line
one.two.three.
four
# good - it's immediately clear what's going on the second line
one.two.three
.four
And it often turns out to be a «solution» for overly complex code as #Aldo already have mentioned:
# bad
some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else
# good
if some_condition
nested_condition ? nested_something : nested_something_else
else
something_else
end
From my experience, it seems like the convention is actually not to break up the lines. Most projects I've seen, including the codebase of rails itself, seem to have no problem with having really long unbroken lines.
So I'd say if you want to follow convention, don't break up the lines. If you are determined to break the lines, then there's no widely followed convention for how to do it. You can use whichever coding style that you prefer.
Rails 3.0.3 application. . .
I'm using a virtual attribute in a model to convert a value stored in the database for display based on a user's preference (U.S. or metric units). I'm doing the conversion in the reader method, but when I test my presence validation I get a NoMethodError because the real attribute is nil. Here's the code:
class Weight < ActiveRecord::Base
belongs_to :user
validates :converted_weight, :numericality => {:greater_than_or_equal_to => 0.1}
before_save :convert_weight
attr_accessor :converted_weight
def converted_weight(attr)
self.weight_entry = attr
end
def converted_weight
unless self.user.nil?
if self.user.miles?
return (self.weight_entry * 2.2).round(1)
else
return self.weight_entry
end
else
return nil
end
end
...
This is the line that's causing the problem:
return (self.weight_entry * 2.2).round(1)
I understand why self.weight_entry is nil, but what's the best way to handle this? Should I just throw in an unless self.weight_entry.nil? check in the reader? Or should I perform this conversion somewhere else? (if yes, where?)
Thanks!
Here's what I've done:
Model
validates :weight_entry, :numericality => {:greater_than_or_equal_to => 0.1}
before_save :convert_weight
attr_reader :converted_weight
def converted_weight
unless self.user.nil?
unless self.weight_entry.nil?
if self.user.miles?
return (self.weight_entry * 2.2).round(1)
else
return self.weight_entry
end
end
else
return nil
end
end
Form
<%= f.label :weight_entry, 'Weight' %><br />
<%= f.text_field :weight_entry, :size => 8, :value => #weight.converted_weight %> <strong><%= weight_units %></strong> (<em>Is this not right? Go to your <%= link_to 'profile', edit_user_registration_path %> to change it</em>)
The unless.self.weight_entry.nil? check allows the validation to do it's job. If anyone knows of a better way to do this I'm open to suggestion.
Thanks!
P.S. The before_save convert_weight method converts U.S. units to metric. I want to store values in the same units consistently so if a user changes her preference later previously stored values don't become invalid.
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.