Examples for Money library?


#1

Can anybody share some examples of their Money implementation ?

I’m trying to setup a Model to use this library, but can’t seem to wrap
my
head around how it is exactly supposed to work.
My Model (Foo) looks like:

composed_of :commission, :class_name => “Money”, :mapping => [
%w(commission_cents cents), %w(commission_currency currency) ]

Yet, in script/console, I’m unable to get the correct behaviour (at
least I
think), from the library.

t = Foo.new(:commission => 34)
NoMethodError: undefined method cents' for 34:Fixnum from (eval):3:incommission=’

I’ve tried just playing with the Money object as well, but regardless of
what number I pass into the Money constructor, it always provides me
with
the exact number I specify as the “cents” value. So, If I do:
Money.new(
34.43)… it tells me that the instance has 34 cents.

I must be overlooking something. Any insight would be greatly
appreciated !


#2

On Feb 27, 2006, at 5:06 PM, Dylan S. wrote:

least I think), from the library.

t = Foo.new(:commission => 34)
NoMethodError: undefined method cents' for 34:Fixnum from (eval):3:incommission=’

Try:
t = Foo.new(:commission => Money.new(34))

or

t=Foo.new
t.commission = Money.new(34)


– Tom M.


#3

Hey Tom ! That does work… and after looking at the actual source, it
does
initialize with only cents.
So, that means that I probably need to instantiate a Money object in the
view:
<%= text_field … etc… Money.new(myvalue) %>

…,or, for every attribute in the model that does this composed_of:
def commission=(commission)
Money.new(commission)
end

Is this the correct approach ?.. or am I missing something ?
If this is correct, it would be great to add some of this to the docs !


#4

On Feb 27, 2006, at 8:35 PM, Dylan S. wrote:

Hey Tom ! That does work… and after looking at the actual
source, it does
initialize with only cents. So, that means that I probably need to
instantiate
a Money object in the view:
<%= text_field … etc… Money.new (myvalue) %>

No, that should be totally unnecessary. Creating objects in a view is
a bad
practice in any case…

Get the money value into a non-db backed attribute…or the cents
attribute
since it’s already around, then, upon form handling in the controller:

object = Object.new(params[:object])
object.commission = Money.new(object.cents)

The bottom line is this: when using composed_of, you must assign to it
with an object of the same class of the composed_of, which makes sense
when you think about it…

…,or, for every attribute in the model that does this composed_of:
def commission=(commission)
Money.new(commission)
end

That’s cute! I sort of like it, but I doubt it’s what you really
want, as
it will likely break the composed_of wizardly that allows you to set the
composed_of object in the first place… Perhaps you could name it:

def set_commission(cents,currency=‘USD’)
commision = Money.new(cents,currency)
end

Note the use of currency, and default of ‘USD’ (my favorite currency,
but
perhaps not yours, and perhaps not the best choice for your
application).
No reason to go through the work of implementing Money (a good idea!)
and
not wire it up so you can use the multi-currency functionality without a
lot of work later!


– Tom M.


#5

Great points Tom… and I am desperately trying to make my code less
cute :slight_smile:

So, then there is no way I can just “create” the object when using
composed_of ?
model = MyModel.create(params[:mymodel])

So in my example, my Term model would actually need a non-model
“Commission”
value object lying around ?


#6

On Feb 27, 2006, at 11:11 PM, Dylan S. wrote:

So, then there is no way I can just “create” the object when using
composed_of ?
model = MyModel. create(params[:mymodel])

Maybe not…you might well be able to set up something like in
MyModel…it’s
your idea, after all! This code is untested…

can a default be an attribute and/or method call?

def commission_cents=(cents,currency=self.currency)
self.commission = Money.new(cents,currency)
end

And in the form:

<%= text_field ‘Commission’, :mymodel, :commission_cents %>


#7

On 2/28/06, Tom M. removed_email_address@domain.invalid wrote:

can a default be an attribute and/or method call?

def commission_cents=(cents,currency=self.currency)
self.commission = Money.new(cents,currency)
end

And in the form:

<%= text_field ‘Commission’, :mymodel, :commission_cents %>

Hm, here’s what I have:

class Product
composed_of :price, :class_name => Money, :mapping => [[:price,
:cents]]

other stuff

end

When I do (in my unit tests)
p = Product.create
I get:
NoMethodError: You have a nil object when you didn’t expect it!
The error occured while evaluating nil.round
from
/usr/lib/ruby/gems/1.8/gems/money-1.7.1/lib/money/money.rb:52:in
initialize' from (eval):3:inprice’

Any ideas?


#8

I’m pretty sure you cannot reuse the DB column name
for the composed_of value object.


– Tom M.


#9

your cents column needs to default to zero

On 2/28/06, Joe Van D. removed_email_address@domain.invalid wrote:

NoMethodError: You have a nil object when you didn’t expect it!


Tobi
http://shopify.com - modern e-commerce software
http://typo.leetsoft.com - Open source weblog engine
http://blog.leetsoft.com - Technical weblog


#10

Are you running money 1.7.0 ?

On 2/27/06, Dylan S. removed_email_address@domain.invalid wrote:

think), from the library.

I must be overlooking something. Any insight would be greatly appreciated !


Rails mailing list
removed_email_address@domain.invalid
http://lists.rubyonrails.org/mailman/listinfo/rails


Tobi
http://shopify.com - modern e-commerce software
http://typo.leetsoft.com - Open source weblog engine
http://blog.leetsoft.com - Technical weblog


#11

Thanks for popping in here Tobias :slight_smile: I’m running into a similar
problem as
Joe, and running 1.7.1.


#12

On 2/28/06, Tobias Lütke removed_email_address@domain.invalid wrote:

your idea, after all! This code is untested…

I get:
NoMethodError: You have a nil object when you didn’t expect it!
The error occured while evaluating nil.round
from /usr/lib/ruby/gems/1.8/gems/money-1.7.1/lib/money/money.rb:52:in
initialize' from (eval):3:inprice’

Any ideas?

your cents column needs to default to zero

Ah, ok. I didn’t see that mentioned anywhere. I’ll try it tonight.

Is it ok to have a database column in my products table called
“price”? Or does it need to be “cents”?


#13

On 2/28/06, Joe Van D. removed_email_address@domain.invalid wrote:

MyModel…it’s
<%= text_field ‘Commission’, :mymodel, :commission_cents %>
p = Product.create

Ah, ok. I didn’t see that mentioned anywhere. I’ll try it tonight.

Is it ok to have a database column in my products table called
“price”? Or does it need to be “cents”?

Yeah, I’m confused. Do I do validations on “cents” (which is now a
database column in my products table) or on “price” (which is not a
database column anymore)?

Here’s part of my unit test for the Product class. I’m having a lot
of troubles getting that to run correctly with the Money class and
composed_of.

def test_that_product_has_to_have_a_price_greater_than_zero
p = Product.create
assert p.errors.on(:price)
bad_prices = %w{ 0 asdf -10 40.OO }
bad_prices.each do |bad_price|
p.price = bad_price
p.save
assert p.errors.on(:price), “<#{ bad_price}> is apparently a good
price”
end
good_prices = %w{ 10 23.4 56.99 }
good_prices.each do |price|
p.price = price
p.save
assert_nil p.errors.on(:price)
end
end