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,