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
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.
> 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"
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
···
On 5/15/06, Austin Ziegler <halostatue@gmail.com> wrote:
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
···
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
--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama
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"
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
···
On 5/15/06, Pat Maddox <pergesu@gmail.com> wrote:
On 5/15/06, Austin Ziegler <halostatue@gmail.com> wrote:
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
···
On 5/15/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:
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.
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
···
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.
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.