Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I extend a ruby class from a class defined in a module?

Tags:

ruby

I have the following files:

file.rb

require_relative 'foo/bar'
baz = Foo::Stuff::Baz.new
# do stuff

foo/bar.rb

require_relative 'stuff/baz'
module Foo
    class Bar
        def initialize
            # do stuff
        end
    end
end

foo/stuff/baz.rb

module Foo
    module Stuff
        class Baz < Bar
        end
    end
end

I get the following error:

`': uninitialized constant Foo::Stuff::Bar (NameError)

Is there something I'm doing wrong here? Is this even possible in Ruby? In case it matters, I'm only doing this because I need to inherit the initialize method specifically.

like image 963
sluther Avatar asked Jan 05 '17 22:01

sluther


3 Answers

It works just fine when you put them in the same script :

module Foo
  class Bar
    def initialize
      # do stuff
    end
  end
end

module Foo
  module Stuff
    class Baz < Bar
    end
  end
end

p Foo::Stuff::Baz.ancestors
#=> [Foo::Stuff::Baz, Foo::Bar, Object, Kernel, BasicObject]

So it must be a problem with the way or order in which you require your files.

Also, if you just need just one specific method from Foo::Bar in Foo::Stuff::Baz, you could put this method in a module, and include this module in both classes.

like image 71
Eric Duminil Avatar answered Nov 15 '22 05:11

Eric Duminil


Your foo/stuff/baz.rb does not contain any require statement and you tell nothing about a main programm. So I think you just don't load the code.

Ruby has no automatic loading depending on folder path, you must explicitly load the source code. In your case you need a require_relative '../bar' in the file foo/stuff/baz.rb. Then the class Foo::Bar is known:

require_relative '../bar'

module Foo
    module Stuff
        class Baz < Bar
        end
    end
  end

  p Foo::Stuff::Baz.new
  p Foo::Stuff::Baz.ancestors

The result:

#<Foo::Stuff::Baz:0x00000002ff3c30>
[Foo::Stuff::Baz, Foo::Bar, Object, Kernel, BasicObject]

The initialize-method of Foo::Bar is executed.


A more realistic architecture would be the usage of a main file where you load all code files, e.g.:

foo.rb
foo/bar.rb
foo/stuff/baz.rb

and foo.rb would contain:

require_relative 'foo/bar'
require_relative 'foo/stuff/baz'
like image 22
knut Avatar answered Nov 15 '22 06:11

knut


Foo::Bar is defined. You can also access ::Foo::Bar ("root" module) when there are issues finding the right namespace.

like image 30
Eric Platon Avatar answered Nov 15 '22 04:11

Eric Platon