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
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.
:_)

On 7/14/06, Eric A. [email protected] wrote:

variable, and I don’t want to repeat myself. (Important,
class REXML::Element
class REXML::Element
class Foo

   end

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

Hi –

On Fri, 14 Jul 2006, Eric A. 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:

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

[email protected] wrote:

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.
:_)

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…

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 methodINLINE’ for
MY_MODULE::Main:Class (NoMethodError)

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.

On Fri, 14 Jul 2006, Eric A. 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

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!