Forum: Ruby Weird class-loading issue (or maybe a problem with define_me

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.
42172acdf3c6046f84d644cb0b94642c?d=identicon&s=25 Pat Maddox (pergesu)
on 2006-05-15 17:20
(Received via mailing list)
I'm working on an app and having a strange class loading problem.
I've extracted it to the most basic code possible.

# foo.rb
require "bar"
class Foo
  FOO = "foo"
end

# bar.rb
require "foo"
class Bar
  def self.set_foo(val)
    define_method(:foo) { val }
  end
end

class FooBar < Bar
  set_foo Foo::FOO
end

This works fine if I include bar.rb first:
baggio:~/work/test pergesu$ irb
irb(main):001:0> require "bar"
=> true

but it blows up if I do foo.rb:
irb(main):001:0> require "foo"
NameError: uninitialized constant FooBar::Foo
        from ./bar.rb:10
        from ./foo.rb:1
        from (irb):1

What's even more strange to me is that the FooBar class has no problem
accessing Foo::FOO from within a method, either class or instance:
class FooBar < Bar
  def foo; Foo::FOO; end
  def self.class_foo; Foo::FOO; end
end

irb(main):001:0> require "foo"
=> true
irb(main):002:0> FooBar.new.foo
=> "foo"
irb(main):003:0> FooBar.class_foo
=> "foo"

I've also moved self.class_foo into the Bar superclass, and there were
no problems.  The only issue that pops up then is when I write a class
method in Bar that ends up calling define_method passing in a constant
from the other file.  It only happens when I require "foo", but
require "bar" works fine.

Can anyone tell me what's going on here?  I really don't understand
it.  Also I've foundt that my program works if I require "bar" before
everything...but I don't like that solution.  Most classes don't even
know that that file ever gets required, so it feels ugly that I'd have
to require it at the beginning of every file I use.

Pat
31ab75f7ddda241830659630746cdd3a?d=identicon&s=25 Austin Ziegler (Guest)
on 2006-05-15 17:29
(Received via mailing list)
On 5/15/06, Pat Maddox <pergesu@gmail.com> wrote:
> I'm working on an app and having a strange class loading problem.
> I've extracted it to the most basic code possible.

Change this:
> # foo.rb
> require "bar"
> class Foo
>   FOO = "foo"
> end

to this:

  # foo.rb
  class Foo
    FOO = "foo"
  end
  require "bar"

Better yet, get rid of your circular dependency.

FooBar should be defined in a third file that requires foo.rb and
bar.rb.

-austin
42172acdf3c6046f84d644cb0b94642c?d=identicon&s=25 Pat Maddox (pergesu)
on 2006-05-15 18:07
(Received via mailing list)
On 5/15/06, Austin Ziegler <halostatue@gmail.com> wrote:
>
> to this:
>
>   # foo.rb
>   class Foo
>     FOO = "foo"
>   end
>   require "bar"

I can't, because some of the methods in the Foo class call Bar methods.


> Better yet, get rid of your circular dependency.
>
> FooBar should be defined in a third file that requires foo.rb and bar.rb.

I'm not sure how that fixes anything.  Foo has to create some Bar
objects, and Bar has to have access to some of the Foo constants.  I
didn't talk about that in my initial post, but each class interacts
with each other.

Pat
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-05-15 18:20
(Received via mailing list)
On Tue, 16 May 2006, Pat Maddox wrote:

> I'm not sure how that fixes anything.  Foo has to create some Bar
> objects, and Bar has to have access to some of the Foo constants.  I
> didn't talk about that in my initial post, but each class interacts
> with each other.
>
> Pat

a setup like this will give you that

   harp:~ > cat foo_constants.rb
   module FooConstants
     FOO = 42
   end

   harp:~ > cat foo.rb
   require 'foo_constants'
   class Foo
     include FooConstants
   end

   harp:~ > cat bar.rb
   require 'foo_constants'
   class Bar
     include FooConstants
     def self.set_foo(val)
       define_method(:foo) { val }
     end
   end

   harp:~ > ruby foobar.rb
   42


hard to understand __why__ you'd do that - but factoring module-wise
will
certainly lead to an elegant answer.

regards.

-a
42172acdf3c6046f84d644cb0b94642c?d=identicon&s=25 Pat Maddox (pergesu)
on 2006-05-15 23:10
(Received via mailing list)
On 5/15/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:
>
>
>    42
>
>
> hard to understand __why__ you'd do that - but factoring module-wise will
> certainly lead to an elegant answer.

Thanks for the response Ara.

Do you mean it's hard to understand why I'd set things up the way I
have them?  Or do you mean that factoring the constants out is a non
obvious solution?

Anyway what really interests me is why I only have this problem when
I'm calling the class method that calls define_method inside it.  If
you look at it, instance methods and class methods all work fine.  If
it were purely a class loading issue, shouldn't it blow up in other
cases as well?

Pat
31ab75f7ddda241830659630746cdd3a?d=identicon&s=25 Austin Ziegler (Guest)
on 2006-05-15 23:17
(Received via mailing list)
On 5/15/06, Pat Maddox <pergesu@gmail.com> wrote:
>>> end
>>
>> to this:
>>
>>   # foo.rb
>>   class Foo
>>     FOO = "foo"
>>   end
>>   require "bar"
> I can't, because some of the methods in the Foo class call Bar methods.

Do you mean:

  class Foo
    FOO = foo
    def initialize(mybar)
      @bar = mybar
    end

    def baz
      # ... stuff ...
      @bar.baz
      # ... stuff ...
    end
  end

If that's the case, then you can do it.

>> Better yet, get rid of your circular dependency.
>>
>> FooBar should be defined in a third file that requires foo.rb and
>> bar.rb.
> I'm not sure how that fixes anything.  Foo has to create some Bar
> objects, and Bar has to have access to some of the Foo constants.  I
> didn't talk about that in my initial post, but each class interacts
> with each other.

I think you're going to need to be a bit clearer about what you're
*actually* trying to do, because this sounds like an incredibly bad
design.

Circular dependencies are bad.

If you must absolutely have it, take advantage of Ruby's open classes.
But try to avoid it.

-austin
42172acdf3c6046f84d644cb0b94642c?d=identicon&s=25 Pat Maddox (pergesu)
on 2006-05-16 10:30
(Received via mailing list)
On 5/15/06, Austin Ziegler <halostatue@gmail.com> wrote:
> I think you're going to need to be a bit clearer about what you're
> *actually* trying to do, because this sounds like an incredibly bad
> design.
>
> Circular dependencies are bad.

I went through Ch1 of Refactoring, using rspec to write the tests.
After I was done with all the refactorings, I wanted to be able to
specify a price code for each Price class by calling a class method,
rather than defining one.  I've got all the source code at
http://svn.flpr.org/public/refactoringbdd  It's very small, so it
wouldn't take more than a few minutes to look at.

Here's the original code for one class, with extraneous methods ripped
out:

class ChildrensPrice < Price
  def get_price_code
    Movie::CHILDRENS
  end
end

Here's what I'd like to do:
class Price
  def self.price(val)
    define_method(:get_price_code) { val }
  end
end

class ChildrensPrice < Price
  price Movie::CHILDRENS
end

As you can see, instead of defining get_price_code, I just call
Class.price and it defines the method for me.

Currently the test suite runs fine.  If you open
specs/customer_spec.rb and delete the require "price" line though, it
gives errors.

Pat
93d566cc26b230c553c197c4cd8ac6e4?d=identicon&s=25 Pit Capitain (Guest)
on 2006-05-16 11:41
(Received via mailing list)
Pat Maddox schrieb:
> I've got all the source code at
> http://svn.flpr.org/public/refactoringbdd  It's very small, so it
> wouldn't take more than a few minutes to look at.

Pat, I haven't looked very carefully, but noted two things:

Too many requires: for example, in customer.rb you require "rental", but
you don't use the Rental class. Skip those require statements.

Duplicated knowledge: the connection between price codes and price
subclasses is duplicated in Movie#price_code= and in the price
subclasses themselves. Try to remove this duplication. I don't think it
is a good idea that the Price class has to know something about price
codes, which seems to be a concept of the Movie class.

Regards,
Pit
This topic is locked and can not be replied to.