2

Suppose I have the following DataMapper model:

class Payment
  include DataMapper::Resource

  property :id, Serial
  property :amount, Decimal, precision: 8, scale: 2
end

Then I do the following:

p = Payment.new(:amount => 12.3245)

This payment will be invalid (at least with DataMapper 1.2), saying that Amount must be a number.

Of course, amount is a number (shakes fist); it just has more decimal places than the property will accept. If I do p.amount = p.amount.round(2), the payment will be valid.

I could write a setter to take away this annoyance:

def amount=(val)
  @amount = val.round(2)
end

...but then I have the annoyance of writing a bunch of identical setters on different models.

I would much prefer to handle this with the same, sensible rule for all Decimal properties, system-wide. Namely, since you know your own scale, round to that scale before saving.

Is this something that can be handled with a configuration option, or maybe an initializer?

Nathan Long
  • 122,748
  • 97
  • 336
  • 451

2 Answers2

2

You could monkey patch DataMapper's Decimal class (original at dm-core-1.2.0/lib/dm-core/property/decimal.rb):

module DataMapper
  class Property
    class Decimal
      alias :original_typecast_to_primitive :typecast_to_primitive
      def typecast_to_primitive(value)
        typecasted = original_typecast_to_primitive(value)
        typecasted.round(@scale) if typecasted.respond_to?(:round)
      end
    end
  end
end

or you could define you own property type NiceDecimal with the new behavior.

Nathan Long
  • 122,748
  • 97
  • 336
  • 451
ujifgc
  • 2,215
  • 2
  • 19
  • 21
  • Nice suggestion. Although I wouldn't want to always round to 2; I'd want to always round to whatever scale that property specified. – Nathan Long Jun 19 '12 at 13:29
  • Okay, then do `typecast_to_numeric(value.round(@scale), :to_d)`. – ujifgc Jun 20 '12 at 07:57
  • Hope you don't mind my modification. The improvements I made are: 1) I'm only rounding values that `respond_to?` round (eg, not blank strings) and 2) if the original method gets updated, this uses the updated version. (I couldn't use `super` because we're not subclassing, so `alias` serves the same purpose.) – Nathan Long Jun 20 '12 at 15:18
0

Have you considered writing your own DataMapper data types for each of your scale categories? That way, there's no monkey-patching and you can specify as many cases as you want without littering any existing class with if / switch statements. DataMapper has a mechanism for this, see a list of custom data types in the "Available Types" section of the properties docs.

fullstackplus
  • 1,061
  • 3
  • 17
  • 31