Problems with the money gem

I have a new app (Rails 2.3.8) that uses lots of money fields. I'm
using the money gem (3.0.5) and the acts_as_money plugin. I've written
a number of rspec model examples and everything seems to be working
fine there.

My problem is in defining forms for new & edit. As I've done in past
projects for complex layouts, I extracted the basic form to a partial
and include it in the new & edit views.

However, the model object is created and left with nulls in the
fields, and causes the money gem to complain with:
    "undefined method `subunit_to_unit' for nil:NilClass".

I thought I could use something like after_initialize() to hook into
the creation of a new object in Rails and set all the money attributes
to zero, but that didn't work (and several posts recommended against
that for performance reasons)...

Any suggestions on a clean way to hook my model object and make sure
it's got zeros for all the money values when the new object is
instantiated?

Jon Seidel wrote:

I have a new app (Rails 2.3.8) that uses lots of money fields. I'm
using the money gem (3.0.5) and the acts_as_money plugin. I've written
a number of rspec model examples and everything seems to be working
fine there.

My problem is in defining forms for new & edit. As I've done in past
projects for complex layouts, I extracted the basic form to a partial
and include it in the new & edit views.

However, the model object is created and left with nulls in the
fields, and causes the money gem to complain with:
    "undefined method `subunit_to_unit' for nil:NilClass".

I thought I could use something like after_initialize() to hook into
the creation of a new object in Rails and set all the money attributes
to zero, but that didn't work (and several posts recommended against
that for performance reasons)...

Any suggestions on a clean way to hook my model object and make sure
it's got zeros for all the money values when the new object is
instantiated?

Why not define a default value for the field on the DB side? This has
the added advantage of ensuring that there is no way to accidentally
create a record without these values even if the Rails app is bypassed.

Best,

I do have default values specified, but these only take effect when
writing to the database, now when the new object is created.

Please quote when replying, otherwise the discussion becomes impossible
to follow.

Jon Seidel wrote:

I do have default values specified, but these only take effect when
writing to the database, now when the new object is created.

Yes, of course. If you need them before that, put them in the
constructor.

(In other words: use Ruby's object model to your advantage! :slight_smile: )

Best,

I've done all that; but since this is an ActiveRecord object, I was
hoping there was a way to do this in one spot, through a Rails hook,
rather than having to remember to code this each time.

The way I encapsulated all this was to create an initialize_to_zeros
method and now I call that each time after doing a model.new

...jon

Jon Seidel wrote:

I've done all that;

Done all what? You failed to quote what you were referring to.

but since this is an ActiveRecord object, I was
hoping there was a way to do this in one spot,

There is. Use the constructor as I already suggested. Do you not know
how to do this?

through a Rails hook,
rather than having to remember to code this each time.

Right. That's what constructors do.

The way I encapsulated all this was to create an initialize_to_zeros
method and now I call that each time after doing a model.new

Unnecessary. Just put that in the constructor.

Martin...

It would be more helpful to me if you were to actually describe what
you are talking about.

If what you are saying is "override initialize" like so:
<pre>
Class Recipe < ActiveRecord::Base
  def initialize (name, yield_amount, yield_unit)
    self.unit_type = get_unit_type(yield_unit)
    self.name = name
    self.yield_amount = yield_amount
    self.yield_unit = yield_unit
  end
end
<pre/>
that's not a good idea. All the posts I have read recommend against
this because Active Record doesn't always use initialize when creating
new instances.

If what you are saying is "add parameters to new, like so (or with a
hash):
<pre>
user = User.new(:name => "David", :occupation => "Code Artist")
<pre/>
that's exactly what I want to avoid having to do every time I create a
new instance.

If there's another approach that I don't know about, I'm all ears.

Thanks...jon

Jon Seidel wrote:

Martin...

That's not my name. Look again.

It would be more helpful to me if you were to actually describe what
you are talking about.

I did. If you don't know what a constructor is, then you need to review
your basic Ruby in order to use Rails effectively.

If what you are saying is "override initialize"

That is basically what I am saying, but you should start the overridden
initializer by calling super.

like so:
<pre>
Class Recipe < ActiveRecord::Base
  def initialize (name, yield_amount, yield_unit)
    self.unit_type = get_unit_type(yield_unit)
    self.name = name
    self.yield_amount = yield_amount
    self.yield_unit = yield_unit
  end
end
<pre/>
that's not a good idea. All the posts I have read recommend against
this because Active Record doesn't always use initialize when creating
new instances.

I'm not sure that's still true, but if it is, then you can use the
after_initialize or after_create callbacks.

If what you are saying is "add parameters to new, like so (or with a
hash):
<pre>
user = User.new(:name => "David", :occupation => "Code Artist")
<pre/>
that's exactly what I want to avoid having to do every time I create a
new instance.

I know.

If there's another approach that I don't know about, I'm all ears.

Callbacks, as mentioned above.

Thanks...jon

Best,

Sorry, Marnen...

Anyway, as I mentioned before, after_create is way too late in the
sequence of things to do any good and after_initialize is recommended
against (as is after_find) because of performance reasons.

I believe I know what a constructor is, so your snide comments
continue to be of no help.

I'll look elsewhere...thanks...jon

Please quote when replying! It's impossible to follow the discussion if
you don't.

Jon Seidel wrote:

Sorry, Marnen...

Anyway, as I mentioned before, after_create is way too late in the
sequence of things to do any good and after_initialize is recommended
against (as is after_find) because of performance reasons.

Only because it's called for each object. In this case, that's exactly
what you need, so use it. The docs seem to say that you shouldn't be
using it *if you don't need to*, but in this case you need to.

Don't reject the purpose-made solution that's right in front of your
face.

I believe I know what a constructor is, so your snide comments
continue to be of no help.

Snide comments? Sorry, that's not at all what I meant. You seemed not
to know, so I was trying to encourage you to find out.

I'll look elsewhere...thanks...jon

No need to look elsewhere. after_initialize is what you need.

No point to look elsewhere. Nothing else will do the trick.

Best,