Initialize a variable in an extended class

Interesting problem. There is undoubtedly much to
will learn from suggested solutions. Here it is:

I'm working with xhtml data, and want to extend
the REXML Element type with an inline? method that
returns true when
   %w(a b code em emphasis i strong tt).member?(name)

That would be a simple addition to the Element class,
except that the program already contains an @inline
variable, and I don't want to repeat myself. (Important,
because <br> is not considered an inline tag at the
moment. If at some point it turns out to be a good idea,
I want one list to modify, rather than two.)

The question is: How do I make the contents of the
variable known to the Element class?

Possibilities:

···

-------------
  a) Use a global variable. This should work:
       class REXML::Element
         def inline?
          return $inline.member?(name)
         end
       end

     Of course, we don't much like global variables.
     But that's one way to single-source the data and
     make it available in two different classes.

  b) Use a class variable:
       class REXML::Element
         @@inline = %w(a b code em emphasis i strong tt)
         def inline?
          return @@inline.member?(name)
         end
       end

     Note: For a long time it wasn't clear to me that
     it is impossible to initialize an instance variable
     outside of a method. Coming from the Java world, I
     just assumed that this would be legal:
       class Foo
         @bar = 2
       end

     But it isn't. (A fact that needs to be spelled out
     somewhere, for Java types.) Class variables can be
     initialized outside of a method, though, so this
     solution works--but it violates the DRY principle.

     Is there a way to use the data stored in the @inline
     variable? Perhaps by using eval or instance_eval?

  c) Add an an additional initialize call? Does this
     work?
       class REXML::Element
         def initialize
           @inline = %w(a b code em emphasis i strong tt)
         end

         def inline?
          return @inline.member?(name)
         end
       end

     This also leaves the DRY problem untouched, but
     I find myself wondering whether initialize()
     invocations are additive, as well. (I thought I
     saw that once, but have been unable to find any
     reference to the concept.)

  d) Do some other clever thing with closures or eval,
     so the Element object can reference data defined
     in my class? Is there a way to do that?

Looking forward to responses. Much learning awaits.
:_)

Interesting problem. There is undoubtedly much to
will learn from suggested solutions. Here it is:

I'm working with xhtml data, and want to extend
the REXML Element type with an inline? method that
returns true when
   %w(a b code em emphasis i strong tt).member?(name)

That would be a simple addition to the Element class,
except that the program already contains an @inline
variable, and I don't want to repeat myself. (Important,
because <br> is not considered an inline tag at the
moment. If at some point it turns out to be a good idea,
I want one list to modify, rather than two.)

The question is: How do I make the contents of the
variable known to the Element class?

Possibilities:
-------------
  a) Use a global variable. This should work:
       class REXML::Element
         def inline?
          return $inline.member?(name)
         end
       end

     Of course, we don't much like global variables.
     But that's one way to single-source the data and
     make it available in two different classes.

  b) Use a class variable:
       class REXML::Element
         @@inline = %w(a b code em emphasis i strong tt)
         def inline?
          return @@inline.member?(name)
         end
       end

     Note: For a long time it wasn't clear to me that
     it is impossible to initialize an instance variable
     outside of a method. Coming from the Java world, I
     just assumed that this would be legal:
       class Foo
         @bar = 2
       end

     But it isn't. (A fact that needs to be spelled out
     somewhere, for Java types.) Class variables can be
     initialized outside of a method, though, so this
     solution works--but it violates the DRY principle.

     Is there a way to use the data stored in the @inline
     variable? Perhaps by using eval or instance_eval?

  c) Add an an additional initialize call? Does this
     work?
       class REXML::Element
         def initialize
           @inline = %w(a b code em emphasis i strong tt)
         end

         def inline?
          return @inline.member?(name)
         end
       end

     This also leaves the DRY problem untouched, but
     I find myself wondering whether initialize()
     invocations are additive, as well. (I thought I
     saw that once, but have been unable to find any
     reference to the concept.)

  d) Do some other clever thing with closures or eval,
     so the Element object can reference data defined
     in my class? Is there a way to do that?

Looking forward to responses. Much learning awaits.
:_)

Use a constant:

      class REXML::Element
        INLINE = %w(a b code em emphasis i strong tt)
        def inline?
         INLINE.member?(name)
        end
      end

BTW

Coming from the Java world, I
     just assumed that this would be legal:
       class Foo
         @bar = 2
       end

     But it isn't.

It is, it just doesn't do what you were expecting. Instead of
initialising an instance variable belong to an instance of the class,
it initialises an instance variable belonging to the instance of Class
that is the class definition. A class is an object too.

Regards,
Sean

···

On 7/14/06, Eric Armstrong <Eric.Armstrong@sun.com> wrote:

Hi --

   Note: For a long time it wasn't clear to me that
   it is impossible to initialize an instance variable
   outside of a method. Coming from the Java world, I
   just assumed that this would be legal:
     class Foo
       @bar = 2
     end

   But it isn't. (A fact that needs to be spelled out
   somewhere, for Java types.)

I'm not sure what you mean by "somewhere". It gets talked about and
explained regularly here, as well as in various books, wikis, etc.
The workings of instance variables are not a closely guarded secret
:slight_smile:

When you see this:

   @var

you are seeing an instance variable belonging to whatever object is,
at that point, playing the role of "self" (the default object).

At the top level of a class definition block, "self" is the class
object (Foo in your example).

So in your example, you've got an instance variable belonging to the
class object Foo. There's no connection at all between this instance
variable and any instance variable that may come into being later when
some other object is "self" (including an instance of Foo).

David

···

On Fri, 14 Jul 2006, Eric Armstrong wrote:

--
http://www.rubypowerandlight.com => Ruby/Rails training & consultancy
Ruby for Rails => RUBY FOR RAILS (reviewed on
                                     Slashdot, 7/12/2006!)
http://dablog.rubypal.com => D[avid ]A[. ]B[lack's][ Web]log
dblack@wobblini.net => me

why not a module?

   module ElementExtension
     TAGS = %w( i p foo bar )

     def inline?
       TAGS.include? name
     end
   end

   REXML::Element.extend ElementExtension

-a

···

On Fri, 14 Jul 2006, Eric Armstrong wrote:

d) Do some other clever thing with closures or eval,
   so the Element object can reference data defined
   in my class? Is there a way to do that?

--
suffering increases your inner strength. also, the wishing for suffering
makes the suffering disappear.
- h.h. the 14th dali lama

Sean O'Halpin wrote:

Use a constant:

     class REXML::Element
       INLINE = %w(a b code em emphasis i strong tt)
       def inline?
        INLINE.member?(name)
       end
     end

Yup. Another good alernative. Do I then I share that
constant with the surrounding program like this:

    INLINE = REXML::Element::INLINE

It would be better to access the constant defined
in my class:

   class Main
     INLINE = %w(a b code em emphasis i strong tt)
     attr_reader :INLINE

     class REXML::Element
       def inline?
         return Main.INLINE.member?(name)
       end
     end
   end

But that doesn't seem to work..
   error: in `inline?': undefined method `INLINE' for
   MY_MODULE::Main:Class (NoMethodError)

BTW

Coming from the Java world, I
     just assumed that this would be legal:
       class Foo
         @bar = 2
       end

     But it isn't.

It is, it just doesn't do what you were expecting. Instead of
initialising an instance variable belong to an instance of the class,
it initialises an instance variable belonging to the instance of Class
that is the class definition. A class is an object too.

Ah ha! I knew it was there when the class is being read, but not when the object is constructed. I didn't quite realize that it is still there as part of the class definition. That helps to refine my mental model. Thanks much.

dblack@wobblini.net wrote:

   Note: For a long time it wasn't clear to me that
   it is impossible to initialize an instance variable
   outside of a method. Coming from the Java world, I
   just assumed that this would be legal:
     class Foo
       @bar = 2
     end

   But it isn't. (A fact that needs to be spelled out
   somewhere, for Java types.)

I'm not sure what you mean by "somewhere". It gets talked about and
explained regularly here, as well as in various books, wikis, etc.
The workings of instance variables are not a closely guarded secret
:slight_smile:

Thanks for the smiley. I haven't been spending enough time here, it seems.
:_)

When you see this:

  @var

you are seeing an instance variable belonging to whatever object is,
at that point, playing the role of "self" (the default object).

At the top level of a class definition block, "self" is the class
object (Foo in your example).

So in your example, you've got an instance variable belonging to the
class object Foo. There's no connection at all between this instance
variable and any instance variable that may come into being later when
some other object is "self" (including an instance of Foo).

Getting clearer all the time.
thanks.

Now for the bigger problem... how to reuse data when
I'm extending an existing class...

···

On Fri, 14 Jul 2006, Eric Armstrong wrote:

Yes! That is indeed clever. I overlooked
the extend method.

I'm probababy going to be kicking myself for
not figuring out the rest of the solution,
but I'll ask anyway:

    What's the best way to share TAGS between
    this module and my main module?

Also:
   Where is the best place to read about
   modules. I have yet to grok their essence,
   as shown by this example and my HTree
   question!

···

ara.t.howard@noaa.gov wrote:

On Fri, 14 Jul 2006, Eric Armstrong wrote:

d) Do some other clever thing with closures or eval,
   so the Element object can reference data defined
   in my class? Is there a way to do that?

why not a module?

  module ElementExtension
    TAGS = %w( i p foo bar )

    def inline?
      TAGS.include? name
    end
  end

  REXML::Element.extend ElementExtension

-a