Forward references?

I am trying to figure out what the cleanest way is to express a class that has
subclasses.

Imagine, if you will:
  class Foo
    def initialize
      @bar = Foo::Bar.new(self)
    end
  end

  class Foo::Bar
    def initialize(foo)
      @foo = foo
    end
  end

Is that a good rubyish way to do this? If I put Foo::Bar in
its own file, should foo.rb be responsible for requiring it?
What originally got me thinking was that my habit is to put requires,
includes, and such at the top of the file... But then the
declaration "class Foo::Bar" refers to an undefined constant.

Of course, I can write:
  class Foo
  end
  require 'foo/bar.rb'

Is there a clear preference here in Ruby Style?

-s

I am trying to figure out what the cleanest way is to express a class that has
subclasses.

First of all, your example is about nested classes and not subclasses.

Imagine, if you will:
  class Foo
    def initialize
      @bar = Foo::Bar.new(self)
    end
  end

  class Foo::Bar
    def initialize(foo)
      @foo = foo
    end
  end

Is that a good rubyish way to do this? If I put Foo::Bar in
its own file, should foo.rb be responsible for requiring it?
What originally got me thinking was that my habit is to put requires,
includes, and such at the top of the file... But then the
declaration "class Foo::Bar" refers to an undefined constant.

That's not an issue as long as you evaluate that only after the constant has been defined:

irb(main):001:0> def test() Foo.new end
=> nil
irb(main):002:0> class Foo; end
=> nil
irb(main):003:0> test
=> #<Foo:0x7ff95f40>

Of course, I can write:
  class Foo
  end
  require 'foo/bar.rb'

Is there a clear preference here in Ruby Style?

I would not put these in different files as Foo depends on Foo::Bar and Foo::Bar depends on Foo.

I'd do

class Foo
   def initialize
     @bar = Bar.new self
   end

   class Bar
     def initialize(foo)
       @foo = foo
     end
   end
end

irb(main):012:0> Foo.new
=> #<Foo:0x7ff7883c @bar=#<Foo::Bar:0x7ff78738 @foo=#<Foo:0x7ff7883c ...>>>

Another question is whether you really want nested classes or maybe rather classes in the same namespace. Basically I create nested classes if the nested class only makes sense in the context of the outer class. The situation is however different than in Java where all this is much more strict (and thus clear).

Kind regards

  robert

···

On 26.05.2007 13:12, Peter Seebach wrote:

The fact that "Foo::Bar" is undefined in Foo#initialize doesn't matter to
Ruby's parser. The whole notion of "forward references" is really more at
home in languages that build symbol-tables at compile time. Ruby just deals
with the names themselves.

Given that, I'd look at the application semantics for a clue to your
question. Foo makes reference to Foo::Bar. If they're that closely related,
why would you then not simply include the definition of Bar inside the
definition of Foo?

Two possible reasons: you want to segregate them textually to make your code
easier to read, or you want to support multiple definitions of Bar, to be
determined at runtime. In either case, you can place the require statements
where it makes the most sense for the user of Foo. You can defend yourself
against Bar being required ahead of Foo by coding it like this:

class Foo
    class Bar
      ...
    end
end

···

On 5/26/07, Peter Seebach <seebs@seebs.net> wrote:

I am trying to figure out what the cleanest way is to express a class that
has
subclasses.

Imagine, if you will:
        class Foo
          def initialize
            @bar = Foo::Bar.new(self)
          end
        end

        class Foo::Bar
          def initialize(foo)
            @foo = foo
          end
        end

Is that a good rubyish way to do this? If I put Foo::Bar in
its own file, should foo.rb be responsible for requiring it?
What originally got me thinking was that my habit is to put requires,
includes, and such at the top of the file... But then the
declaration "class Foo::Bar" refers to an undefined constant.

I would say yes. My rule of thumb is that any file that explicitly
calls some API must explicitly require/use/#include/... that API.
IOW, batteries *should* be included; don't force your users to make
extra trips to the shops.

Regards,

Jeremy Henty

···

On 2007-05-26, Peter Seebach <seebs@seebs.net> wrote:

  class Foo
    def initialize
      @bar = Foo::Bar.new(self)
    end
  end

[snip]

... If I put Foo::Bar in its own file, should foo.rb be responsible
for requiring it?

First of all, your example is about nested classes and not subclasses.

I knew that. Maybe I shouldn't be on decaf.

class Foo
  def initialize
    @bar = Bar.new self
  end

  class Bar
    def initialize(foo)
      @foo = foo
    end
  end
end

Ooh.

Another question is whether you really want nested classes or maybe
rather classes in the same namespace. Basically I create nested classes
if the nested class only makes sense in the context of the outer class.
The situation is however different than in Java where all this is much
more strict (and thus clear).

Classes in the same namespace might be a better choice. I
think I may just do that. Thinking about it, my assumption that
the nested classes only made sense in the context of an
outer class may be wrong. Since I can't see a reason to enforce that
assumption, I'll just remove it, and move them all into a namespace
together.

-s

···

In message <5bqjfiF2u1b1vU1@mid.individual.net>, Robert Klemme writes:

The fact that "Foo::Bar" is undefined in Foo#initialize doesn't matter to
Ruby's parser. The whole notion of "forward references" is really more at
home in languages that build symbol-tables at compile time. Ruby just deals
with the names themselves.

  class A::B
  end
  class A
  end
  --> t.rb:1: uninitialized constant A (NameError)

So I can't just do them in ANY order.

Two possible reasons: you want to segregate them textually to make your code
easier to read, or you want to support multiple definitions of Bar, to be
determined at runtime. In either case, you can place the require statements
where it makes the most sense for the user of Foo. You can defend yourself
against Bar being required ahead of Foo by coding it like this:

The rationale was essentially to segregate them textually. I like
having moderately self-contained things in separate files, even if they
have some necessary cohesion.

Hmm.

I had the brief notion of writing
  module A
    require 'b.rb'
  end
and ending up with a class named A::B, but B ends up outside
of A anyway. So much for my clever idea. :slight_smile:

-s

···

In message <3a94cf510705260432r5157b607wae9cd16ce61aa6a7@mail.gmail.com>, "Fran cis Cianfrocca" writes:

I think your rule of thumb is absolutely right. Always make your code as as
easy as possible to use.

However...
I have started, coding my source files *without* including all the things
they might require, just to be ready for an eventual Ruby deployment model
that pulls together pieces of code running in different processes, using DI
to make sure everything is included at runtime. I know that must sound
weird. It does put more pressure on the users of a given code module, since
they have to know more about the includes required by things that they
include.

···

On 5/26/07, Jeremy Henty <onepoint@starurchin.org> wrote:

On 2007-05-26, Peter Seebach <seebs@seebs.net> wrote:
I would say yes. My rule of thumb is that any file that explicitly
calls some API must explicitly require/use/#include/... that API.
IOW, batteries *should* be included; don't force your users to make
extra trips to the shops.

In this construction you're defining B inside the naming context of A, which
doesn't exist when the *parser* (which obeys Ruby scoping rules like any
other Ruby program) needs to use the value of A. That's why I suggested
something like this:

class A
  class B
    def bar
    end
  end
end

class A
  def initialize
    @b = B.new
  end
end

You see the difference? You don't have to define class A before you define
class B, but you DO need to *declare* A before you can present it to Ruby's
parser in a construction like A::xxx.

Of course your original question related to style rather than parser
behavior. You don't say whether you come from a Java background, where the
relationship between classes and source files is much stricter than it is in
Ruby. With closely related Ruby classes like yours, I often define them in
separate text blocks, in the same file:

module MyApplication
    class A
        def initialize
            @b = B.new
        end
    end
end

···

On 5/26/07, Peter Seebach <seebs@seebs.net> wrote:

        class A::B
        end
        class A
        end
        --> t.rb:1: uninitialized constant A (NameError)

So I can't just do them in ANY order.

#------------------------------

module MyApplication
    class A
        class B
        end
    end
end

In this construction you're defining B inside the naming context of A, which
doesn't exist when the *parser* (which obeys Ruby scoping rules like any
other Ruby program) needs to use the value of A.

Right. Thus my forward-reference solution:

  class A
  end

  class A::B
  ...
  end

  class A
  ...
  end

Which is mostly picked up from C's "struct foo;" forward reference
declarations.

You see the difference? You don't have to define class A before you define
class B, but you DO need to *declare* A before you can present it to Ruby's
parser in a construction like A::xxx.

Right.

Of course your original question related to style rather than parser
behavior. You don't say whether you come from a Java background, where the
relationship between classes and source files is much stricter than it is in
Ruby.

Mostly C and perl, although I read and write a fair number of languages
now.

module MyApplication
   class A
       def initialize
           @b = B.new
       end
   end
end

#------------------------------

module MyApplication
   class A
       class B
       end
   end
end

Makes sense. I'm now doing:

  module MyApplication
    class A
    ...
    end

    class B
    ...
    end
  end

because really, I don't have an unambiguous top-level class, so using a module
for namespace is clearer.

-s

···

In message <3a94cf510705260514u31029d2fr95bb8f96c5520c23@mail.gmail.com>, "Francis Cianfrocca" writes: