-1

I need a way to only allow values like:

Ok: 23.55, 232.43, 300.34 2.34

Not ok: 23.4, 43.344, 343.454, 230, 34

I have a regex in my model but it seems to allow me to save values like 200, 344, 23. I need to restrict things so that I'm only allowed to submit form when values are entered in the format of my Ok list.

Here is my model:

class Garment
    include ActiveAttr::Model
    #include ActiveModel::Validations
    extend CarrierWave::Mount

    attribute :price

    mount_uploader :image, ImageUploader

    price_regex = /\A(?:[1-9]+[0-9]*|0)(?:\.[0-9]{2})?\z/

    validates :price,       :presence      => true,
                            :numericality  => { :less_than => 301.00, :greater_than => 0.00 },
                            :format        => {
                                                 :with => price_regex,
                                                 :message => "Price must be entered in the correct format e.g. 23.45, 203.43 not 43.3 or 234.5"
                                              } 

This is how I save the price entered into the form field:

  def create
      @garment = Garment.new(params[:garment])
      if @garment.valid?
      garment = Parse::Object.new("Garments")
      garment["price"] = params[:garment][:price].to_f
      garment.save

      flash[:success] = "Garment successfully added to store!"
      redirect_to '/adminpanel/show'
    else
      render "new"
    end
  end

I thought my regex was fine but I think I may need to tweak it more. I was wondering maybe I could some how check the value for a decimal and if it hasn't got one add one with 2 zeros after it before it is saved.

However I think the easiest most sensible way would be to do something before the actual form is submitted.

Would appreciate some help

Thanks for your help

engineersmnky
  • 25,495
  • 2
  • 36
  • 52
LondonGuy
  • 10,778
  • 11
  • 79
  • 151

3 Answers3

2

Try a custom validator rather than a regex?

validate :valid_price_format

def valid_price_format
  unless price.split('.')[1].try(:length) == 2
    self.errors.add(:price,  I18n.t('.invalid_format') )
  end
end

Edited based on comments.

You can scope your translation if it's looking in the wrong place:

I18n.t('en.my.translation.location.invalid_format')

or

I18n.t('invalid_format', scope: 'en.my.translation.location')
A Fader Darkly
  • 3,516
  • 1
  • 22
  • 28
2

Floats have quirks See Here.

Also 23.4 is a valid float. floats do not hold onto trailing zeros past the tenths place so 23.40 comes out as 23.4. You are better off storing prices as integer in cents e.g.

def price=(money)
  #to_d returns a BigDecimal you could use to_f if you prefer
  self.price = money.to_d * 100 if money
end
def price
   #to_d returns a BigDecimal you could use to_f if you prefer
   #bu BigDecimal is more accurate in comparisons
  price.to_d / 100 if price
end
def display_price
  #this will retun the price as in $XXXX.XX format as a String
  sprintf("$%0.2f",price.to_f)
end

This way when you set it it will automatically convert it to an Integer and when using the getter method it will return a BigDecimal. Optionally you could leave off display_price method and use the helper method number_to_currency(price) in your views which will also add in commas and other configurable items. See number_to_currency

Also if you decide to forgo the above you can store them as decimals with the appropriate format using a migration like

add_column :table_name,:price, :decimal, precision: 8, scale: 2

Which will store them with 2 decimal places.

Update as a note

garment["price"] = params[:garment][:price].to_f

means that even if the user enters 12.00 it will be passed to save as 12.0 because of what is stated above. So you are assuming all prices do not end with a 0. Otherwise the next line

garment.save 

Will fail silently and it will not be saved in the manor that you expect.

Also you are on rails 4 but not using strong_parameters?

@garment = Garment.new(params[:garment])

shouldn't this be

@garment = Garment.new(garment_params)

where garment_params utilizes a require and permit statement.

engineersmnky
  • 25,495
  • 2
  • 36
  • 52
  • This is all true. Upvote. However, the OP seems to be trying to detect whether people have entered a value using an incorrect currency format. (2.2 is not valid). As all inputs send strings over the web until parsed, this is possible. – A Fader Darkly Aug 14 '14 at 17:21
  • 1
    @AFaderDarkly Thanks for the upvote and I am aware of the OP request. I just thought I would point out that he is making it more complex than necessary. Why does it matter if the user says it costs `12` dollars or `12.00` dollars since either way `12 == 12.0 #=> true`. I don't understand why the format needs to be implied unless it is for viewing purposes in which case there are better solutions. Also I updated my answer becuase based on his implementation `12.00` will become `12.0` no matter what. – engineersmnky Aug 14 '14 at 17:24
  • This app is a basically an administration panel for an iOS app. I just wanted to make sure everything was sorted before saving from the admin panel so I wouldn't need to make any changes to the app code. The app is completed and all testing is done so I'd rather not touch it if I don't have to. Also the last time I used rails was version 3. I will have to update this code. Thanks for your input. I'm going to check out your links. – LondonGuy Aug 14 '14 at 18:28
  • @LondonGuy pleae take careful note of the fact that with this regex your save call will fail **silently** because of the type conversion. – engineersmnky Aug 14 '14 at 18:30
  • My back end is parse.com and not ActiveRecord so won't be able to use part of your example. I've actually removed the regex and using A Fader Darkly's answer. What are your opinions on that? It seems to be working as expected. – LondonGuy Aug 14 '14 at 18:36
  • @LondonGuy I have no issue with his answer(actually just +1 it) just be careful with `float`s. I certainly understand not wanting to mess with something that is not "broken", Just be wary if you are using these numbers in arithmetic in your application. – engineersmnky Aug 14 '14 at 18:47
  • Marked you up. Those links were helpful. – LondonGuy Aug 14 '14 at 19:02
0

/^[1-9]*[0-9]+\.[0-9][0-9]$/ matches the "OK" values and rejects the "Not OK" values.

Share and enjoy.