Forum: Ruby I Broke Ruby (i.e. Missing the Brutally Obvious)

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.
Michael J. (Guest)
on 2006-02-04 10:44
It's been yet another late Friday.  It's practically a lifestyle by this
point.  (I'm glad my wife understands.) So anyway, things were going
fine until I tried to get two ruby files to require each other.
Suddenly the class defined in the first file was considered
"uninitialized."

Here's some code demonstrating the error:

Type into IRB:
  require "a"
  NameError: uninitialized constant A
        from ./b.rb:3
        from ./a.rb:1
        from (irb):3

Place these files in the same directory you call irb in:

  File a.rb
    require "b"

    class A < Array
      # Do nothing
    end

  File b.rb
    require "a"

    class B < A
      # Do nothing
    end

Where's the brutally obvious mistake I made?  Why would A be an
uninitialized constant?
George O. (Guest)
on 2006-02-04 11:40
(Received via mailing list)
Michael J. <removed_email_address@domain.invalid> writes:

>     class B < A
>       # Do nothing
>     end
>
> Where's the brutally obvious mistake I made?  Why would A be an
> uninitialized constant?

Because it hasn't been defined when "class B < A" is evaluated.
Here's what happens, step by step:

- irb evaluates: require "a"
  + ruby parses the 'a.rb' file into an internal representation, doing
    syntax checking along the way
  + ruby evaluates the first expression: require "b"
    * ruby parses the 'b.rb' file into an internal representation,
      doing syntax checking along the way
    * ruby evaluates the first expression: require "a".  As 'a.rb' has
      already been required, this does effectively nothing (see `ri
      require').
    * ruby evaluates the next expression: class B < A ... end.  As A
      is not defined, ruby raises the exception you see.

Why does 'a.rb' require 'b.rb' ?  Circular requires are a bad thing.
You'll need to break it up.

One option is to just have the toplevel file just have a whole
sequence of requires, and have each source file define what it needs
to without messing around with any requires whatsoever.

Hope this helps.
Michael J. (Guest)
on 2006-02-06 00:59
George O. wrote:
> Michael J. <removed_email_address@domain.invalid> writes:
>
>>     class B < A
>>       # Do nothing
>>     end
>>
>> Where's the brutally obvious mistake I made?  Why would A be an
>> uninitialized constant?
>
> Because it hasn't been defined when "class B < A" is evaluated.
> Here's what happens, step by step:
>
> - irb evaluates: require "a"
>   + ruby parses the 'a.rb' file into an internal representation, doing
>     syntax checking along the way
>   + ruby evaluates the first expression: require "b"
>     * ruby parses the 'b.rb' file into an internal representation,
>       doing syntax checking along the way
>     * ruby evaluates the first expression: require "a".  As 'a.rb' has
>       already been required, this does effectively nothing (see `ri
>       require').
>     * ruby evaluates the next expression: class B < A ... end.  As A
>       is not defined, ruby raises the exception you see.
>
> Why does 'a.rb' require 'b.rb' ?  Circular requires are a bad thing.
> You'll need to break it up.
>
> One option is to just have the toplevel file just have a whole
> sequence of requires, and have each source file define what it needs
> to without messing around with any requires whatsoever.
>
> Hope this helps.

Thanks for your help, George.  That's really interesting how the ruby
parser works.

If instead I did, require "b" in irb, it would have worked because the
following sequence would have happened:

1. Parse b.rb until hitting the line: require "a"
2. Parse a.rb (in the process, learning about class A)
3. Continue parsing b.rb
4. Class B's definition (subclassed to A) now makes sense to the parser
and everything works.

Crazy stuff.  It makes me want to write a programming language.

In the end, after reading your post, I decided to move class B into the
bottom of a.rb and my unit tests passed.  It just seemed simpler than
creating a file that just requires all the different class files.

(Thanks again for taking the time to explain how the parser worked)
This topic is locked and can not be replied to.