10

I'm having an issue with the forms and the money gem.

This is my problem:

  1. I create a record which has an "amount" field (mapped to money object). Let's say I enter 10 (dollars).
  2. The money gem converts it to 1000 (cents)
  3. I edit the same record and the form pre-populates the amount field as 1000
  4. If I save the record without changing anything, it will convert the 1000 (dollars) to 100000 (cents)

How do I make it display the pre-populated amount in dollars instead of cents?

Edit:

I tried editing the _form.html like this:

= f.text_field(:amount, :to_money)

and I get this error:

undefined method `merge' for :to_money:Symbol
David
  • 7,310
  • 6
  • 41
  • 63
  • It's 1345. I think the form is retrieving the stored value without converting it back to dollars. – David Jan 31 '11 at 23:36
  • So how come 1,000 is being converted to 100 and not displaying 1,000?! Something wrong there. Secondly (I have not used the money gem) but I suspect that the attribute reader for the amount field is not converting the value. Or maybe this needs to be done by you and not the gem? Some code you post will help. Also, do a inspect on the loaded record and see what the value of the amount field is. – Zabba Jan 31 '11 at 23:40
  • Sorry that was a typo. It pre-populates as 1000. – David Feb 01 '11 at 00:09
  • I guess you need to populate the form field after you convert it to dollars (probably using the `to_money` field or something like that) – Zabba Feb 01 '11 at 00:14
  • I tried (see my edit) but the form html doesn't recognize **to_money** – David Feb 01 '11 at 00:28

4 Answers4

12

Given a migration as follows:

class CreateItems < ActiveRecord::Migration
  def self.up
    create_table :items do |t|
      t.integer :cents
      t.string :currency
      t.timestamps
    end
  end

  def self.down
    drop_table :items
  end
end

And a model as follows:

class Item < ActiveRecord::Base
  composed_of :amount,
    :class_name  => "Money",
    :mapping     => [%w(cents cents), %w(currency currency_as_string)],
    :constructor => Proc.new { |cents, currency| Money.new(cents || 0, currency || Money.default_currency) },
    :converter   => Proc.new { |value| value.respond_to?(:to_money) ? value.to_money : raise(ArgumentError, "Can't conver #{value.class} to Money") }
end

Then this form code should work perfectly (I just tested under Rails 3.0.3), properly displaying and saving the dollar amount every time you save/edit. (This is using the default scaffold update/create methods).

<%= form_for(@item) do |f| %>
  <div class="field">
    <%= f.label :amount %><br />
    <%= f.text_field :amount %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>
Dylan Markow
  • 123,080
  • 26
  • 284
  • 201
  • Your example made me catch the problem. In my migration I have t.integer amount and in my model i have composed_of :amount. This caused a conflict. Thanks. – David Feb 01 '11 at 02:23
  • 3
    Thanks for this answer. Just and FYI for others - where `[%w(cents cents)`... is written, the first 'cents' represents your column in `:items` and the second `cents` is a method of the gem. – Patrick Connor Apr 16 '11 at 13:21
4

You can now edit monetized fields directly (money-rails 1.3.0):

# add migration
add_column :products, :price, :price_cents

# set monetize for this field inside the model
class Product
  monetize :price_cents
end

# inside form use .price instead of .price_cents method
f.text_field :price

See https://stackoverflow.com/a/30763084/46039

Community
  • 1
  • 1
Brian Low
  • 11,605
  • 4
  • 58
  • 63
3

If you have multiple money fields in your table and you can't name them all "cents".

class CreateItems < ActiveRecord::Migration
  def self.up
    create_table :items do |t|
      t.integer :purchase_price_cents
      t.string :currency
      t.timestamps
    end
  end

  def self.down
    drop_table :items
  end
end

which would change your model to

class Item < ActiveRecord::Base

  composed_of :purchase_price,
    :class_name  => "Money",
    :mapping     => [%w(purchase_price_cents cents), %w(currency currency_as_string)],
    :constructor => Proc.new { |purchase_price_cents, currency| Money.new(purchase_price_cents || 0, currency || Money.default_currency) },
    :converter   => Proc.new { |value| value.respond_to?(:to_money) ? value.to_money : raise(ArgumentError, "Can't convert #{value.class} to Money") }

end
seehad
  • 161
  • 7
  • Very good answer. Giving an example with a column "purchase_price_cents" is less ambiguous because it makes it clear which one is the column and which one is the function. (%w(purchase_price_cents cents) vs. %w(cents cents) – Christoph Apr 25 '12 at 17:17
-1

monetizing and the simple form, the steps as follows:

  1. Migration

add_monetize :table, :amount

  1. Model with validation

monetize :amount_cents, allow_nil: true, numericality: {greater_than: 0}

  1. Controller permit params (dont use amount_cents here)

params.require(:model).permit(:amount)

  1. simple form input

when it's saved it will be saved in cents in amount_cents column in db

Manav
  • 561
  • 3
  • 10
  • 20