Forum: Ruby on Rails Extending model

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
967f758fa9de78e7fe8425469f7a7acc?d=identicon&s=25 David Barrett (Guest)
on 2005-12-23 05:52
(Received via mailing list)
Hi,

I'm looking for a way to extend one of my models to allow some level
of abstraction between what goes into it and how it is stored.

For example, say I have a Product and I want to set it's price. A
person using the website will type the price in euros. Internally, I'd
like to store the price as an integer value of cents.

I can currently do this with some ugly code in the controller, but I'd
like the model to just accept a price in euros and automatically
convert it to cents.

In plain Ruby, I could do something like this:

-  # Take in prices in euros, store them as cents internally (doesn't
work right :( )
-  def price
-    unless cents.nil?
-      return cents / 100.0
-    else
-      return cents
-    end
-  end

-  def price=(euros)
-    cents = euros * 100
-  end

But it doesn't work in RoR. Is there something I'm missing? I have a
horrible feeling there should be a 'self' somewhere in there...

David Barrett

--
Site: http://antidis.com/
C64e63b70be7dfed8b0742540b8b27e5?d=identicon&s=25 Mark Reginald James (Guest)
on 2005-12-23 08:17
(Received via mailing list)
David Barrett wrote:

> -  def price=(euros)
> - ..>cents = euros * 100
> - .end
>   .....................................................................
> But it doesn't work in RoR. Is there something I'm missing? I have a  .
> horrible feeling there should be a 'self' somewhere in there...........

--
We develop, watch us RoR, in numbers too big to ignore.
967f758fa9de78e7fe8425469f7a7acc?d=identicon&s=25 David Barrett (Guest)
on 2005-12-23 16:52
(Received via mailing list)
I'm sorry, I don't understand.

I'm quite new to both Rails and Ruby. If this is a scoping problem, I
don't know exactly why and I don't know how to fix it.

Dave

On 12/23/05, Mark Reginald James <mrj@bigpond.net.au> wrote:
> We develop, watch us RoR, in numbers too big to ignore.
>
> _______________________________________________
> Rails mailing list
> Rails@lists.rubyonrails.org
> http://lists.rubyonrails.org/mailman/listinfo/rails
>


--
Site: http://antidis.com/
5ceea097a3b29cb6a5da6705926410f4?d=identicon&s=25 Gerret Apelt (Guest)
on 2005-12-23 17:13
(Received via mailing list)
Dave,

have a look at the before_save callback [1], which lets you do do
stuff like euro/cent conversion before a record is saved.

If you want conversion to happen before the record is saved, I would
use a specialized setter method. Supposing your column is called
'cents' and you want to be able to set the value of that column by
passing in Euros, do something like this in your model:

def price_in_euros=(euros)
  self[:cents] = euros.nil? ? 0 : euros * 100
end


cheers,
Gerret

[1]
http://api.rubyonrails.com/classes/ActiveRecord/Ca...
34c220239b2a54b32d329c227fab3da4?d=identicon&s=25 Paul Bernays (Guest)
on 2005-12-23 17:14
(Received via mailing list)
On 23/12/2005 2:50 PM, David Barrett wrote:
> Hi,
>
> I'm looking for a way to extend one of my models to allow some level
> of abstraction between what goes into it and how it is stored.
>
> <snip>
>
> But it doesn't work in RoR. Is there something I'm missing? I have a
> horrible feeling there should be a 'self' somewhere in there...

You're talking about Facade Columns, where the data stored in the
database is in a different format to how you handle it in the
application. When you overwrite accessor methods, you need to use the
read_attribute and write_attribute methods to get the data from/put the
data to the database.

Your price example should be as simple as:

def price
  read_attribute('price') / 100.0
end

def price=(euros)
  write_attribute('price', euros * 100)
end


HTH
967f758fa9de78e7fe8425469f7a7acc?d=identicon&s=25 David Barrett (Guest)
on 2005-12-23 17:26
(Received via mailing list)
Thanks guys, both of you; this is fantastic. No more messy code in the
controller! :)

Gerret, in your example code, you access cents through self[:cents].
That seems to be the major difference between your code and my
(non-working) code. What I find confusing, is that for READING, just
using 'cents' works. (the 'price' method worked, but 'price=' did
not).

Is this an inconsistency in AR, or Ruby, or is there something I'm
missing?

Thanks again,
Dave

On 12/23/05, Paul Bernays <paul@bernays.org> wrote:
>
> end
> http://lists.rubyonrails.org/mailman/listinfo/rails
>


--
Site: http://antidis.com/
5ceea097a3b29cb6a5da6705926410f4?d=identicon&s=25 Gerret Apelt (Guest)
on 2005-12-23 17:44
(Received via mailing list)
Dave,

> (non-working) code. What I find confusing, is that for READING, just
> using 'cents' works. (the 'price' method worked, but 'price=' did
> not).
>
> Is this an inconsistency in AR, or Ruby, or is there something I'm missing?

Internally AR keeps your attribute values in a Hash. When you write
self[:cents] = 10
you're writing directly to the attributes Hash. If instead you write
cents = 10
you're initializing a local variables with value 10.

As for reading attributes, AR gives you a shortcut. When you just use
"cents" inside your model instance, and there is no variable or method
'cents' in your current scope, then ActiveRecord::Base will intercept
your call, and figure out that you're trying to call a method that has
the same name as one of the attributes. AR will  then be nice enough
to return you the attribute in question.

cheers
gerret
C64e63b70be7dfed8b0742540b8b27e5?d=identicon&s=25 Mark Reginald James (Guest)
on 2005-12-23 18:02
(Received via mailing list)
Gerret Apelt wrote:

> Internally AR keeps your attribute values in a Hash. When you write
> self[:cents] = 10
> you're writing directly to the attributes Hash. If instead you write
> cents = 10
> you're initializing a local variables with value 10.

What I tried to suggest with my too-cute post (sorry, Christmasy mood)
was that David write

    self.cents = ...

which is the same as using self[:cents].

That's one of the major traps of Ruby: calls to setter methods
inside its class require a self receiver, otherwise the variable
is interpreted as a local.  I don't fully understand why it has
to be this way.


--
We develop, watch us RoR, in numbers too big to ignore.
967f758fa9de78e7fe8425469f7a7acc?d=identicon&s=25 David Barrett (Guest)
on 2005-12-23 18:05
(Received via mailing list)
Thanks Gerret, that makes sense.

Dave

On 12/23/05, Gerret Apelt <gerret.apelt@gmail.com> wrote:
> you're writing directly to the attributes Hash. If instead you write
> cheers
> gerret
> _______________________________________________
> Rails mailing list
> Rails@lists.rubyonrails.org
> http://lists.rubyonrails.org/mailman/listinfo/rails
>


--
Site: http://antidis.com/
5ceea097a3b29cb6a5da6705926410f4?d=identicon&s=25 Gerret Apelt (Guest)
on 2005-12-24 11:50
(Received via mailing list)
Mark,

> That's one of the major traps of Ruby: calls to setter methods
> inside its class require a self receiver, otherwise the variable
> is interpreted as a local.  I don't fully understand why it has
> to be this way.

I didn't fully understand that myself. Looking about for the answer I
found [1]; here's the relevant portion:

"Sidebar: Using Accessors Within a Class

Why did we write self.leftChannel in the example on page 74? Well,
there's a hidden gotcha with writable attributes. Normally, methods
within a class can invoke other methods in the same class and its
superclasses in functional form (that is, with an implicit receiver of
self). However, this doesn't work with attribute writers. Ruby sees
the assignment and decides that the name on the left must be a local
variable, not a method call to an attribute writer."

[1] http://www.rubycentral.com/book/tut_expressions.html

cheers
Gerret
C64e63b70be7dfed8b0742540b8b27e5?d=identicon&s=25 Mark Reginald James (Guest)
on 2005-12-24 19:51
(Received via mailing list)
Gerret Apelt wrote:

> [1] http://www.rubycentral.com/book/tut_expressions.html
Thanks Gerret.

Here's a recent thread from comp.lang.ruby discussing this, with
contributions from Mats:

http://groups.google.com/group/comp.lang.ruby/brow...

And another discussion:

http://groups.google.com/group/comp.lang.ruby/brow...


--
We develop, watch us RoR, in numbers too big to ignore.
This topic is locked and can not be replied to.