has_many option explanation

I am maintaining an application that has an account model which has many
payments. So the account model has a has_many relationship as shown
below:

class Account < ActiveRecord::Base
....
  has_many :payments, :order => "created_at ASC" do
    def charge(amount, paying_for)
      Payment.charge(proxy_owner, amount, paying_for)
    end

    def commit(action, amount, paying_for)
      Payment.commit(proxy_owner, action, amount, paying_for)
    end

    def gift(days = nil)
      Payment.gift(proxy_owner, days)
    end
  end
.....
end

This may be an RoR 101 question, but why would someone define methods in
a has_many block? Can't this be simply called as:

account_inst.payments.charge(amount, paying_for) for example?

I do not understand what is the benefit of defining (or proxying ) class
methods belonging to the many side of this relationship in this manner?

Thanks for your time.

Bharat

I am maintaining an application that has an account model which has many
payments. So the account model has a has_many relationship as shown
below:

class Account < ActiveRecord::Base
....
has_many :payments, :order => "created_at ASC" do
def charge(amount, paying_for)
Payment.charge(proxy_owner, amount, paying_for)
end

def commit\(action, amount, paying\_for\)
  Payment\.commit\(proxy\_owner, action, amount, paying\_for\)
end

def gift\(days = nil\)
  Payment\.gift\(proxy\_owner, days\)
end

end
.....
end

This may be an RoR 101 question, but why would someone define methods in
a has_many block? Can't this be simply called as:

It's nicer to write some_account.payments.gift than it is to write
Payment.gift(some_account). You get the association scoping for free
which can help you avoid mistakes and make things more readable.

account_inst.payments.charge(amount, paying_for) for example?

That is precisely what the association extension methods that have
been defined allow you to do.
One trick that is not well known enough is that if foo is a class
method on payments then you can call
account_inst.payments.foo() and it will call the class method foo
except that everything will be scoped to only act on those payments
belong to foo. This won't work with the existing charge method since
it requires an account as its first parameter.

Fred

Thanks Fred. You wrote:

"This won't work with the existing charge method since
it requires an account as its first parameter."

Was this an oversight since the first parameter of the existing charge
method is amount as shown below?

   def charge(amount, paying_for)
      Payment.charge(proxy_owner, amount, paying_for)
    end

Nice explanation.

Regards,

Bharat

Thanks Fred. You wrote:

"This won't work with the existing charge method since
it requires an account as its first parameter."

Was this an oversight since the first parameter of the existing charge
method is amount as shown below?

No - what i meant that the charge method you would get for free on the
payments association (because of the existence of Payment.charge)
wouldn't do the write thing.

Fred