34

在我的一个项目中,我的文件夹结构如下所示:

    • bar.rb
    • 酒吧
      • other_bar.rb
      • another_bar.rb
      • next_bar.rb
      • ...

bar.rb

require File.expand_path(File.dirname(__FILE__) + "/bar/other_bar.rb")

class Bar
  puts "running BarBase"
end

酒吧/其他酒吧.rb

module Bar
  class OtherBar
    puts "running module Bar with class OtherBar"
  end
end

如果我现在运行,ruby bar.rb我会得到:

使用类 OtherBar
bar.rb:3:in 运行模块 Bar `':Bar 不是类(TypeError)

我想有一个类似于 rails 模型继承结构的结构。我怎样才能解决这个问题?据我所知,ruby 不支持这个开箱即用。这种情况有解决方法吗?

4

3 回答 3

44

Bar不能是模块和类,它们是不同的东西。

更改bar.rbmodule Bar或更改other_bar.rbclass Bar

无论是哪一种,都必须是一致的。你不能把一个换成另一个。问题是应该是哪个?如果Bar是其他类的容器并且只有几个全局单例方法?然后是一个module。但如果它可以被实例化,那么它就是一个class.

是的,你可以嵌套类。这是完全可以接受的:

class Bar
  class OtherBar
    puts "running module Bar with class OtherBar"
  end
end

Bar::OtherBar.new # yay!

模块和类可以以任何你认为合适的方式嵌套在其中。


编辑一些带注释的示例以帮助清除这一切:

module Foo

  # Foo::A
  class A
    # simple namespaced class
  end

  # Foo::B, inherits from Foo::A
  class B < A
    # inherting from a class in the same namespace
  end

  # modify Foo::B
  class B
    # When modifying an existing class you don't need to define the superclass
    # again. It will raise an error if you reopen a class and define a different
    # superclass. But leaving it off is fine.
  end

  # nested module Foo::Inner
  module Inner

    # Foo::Inner::C 
    class C
      # simple more deeply namespaced class
    end

    # Foo::Inner::D, inherits from Foo::A
    class D < A
      # inherits from a class in a parent namespace

      # works because ruby looks upward in the nesting chain to find missing constants.
    end

    # Foo::Inner::Foo
    class Foo
      # simple nested class with the same name as something in a parent namespace

      # This is a totally different Foo, because it's in a different namespace
    end

    # Foo::Inner::E, inherits from Foo::Inner::Foo
    class E < Foo
      # class inhereting from another class in the same namespace

      # Foo::Inner::Foo is "closer" than the global Foo, so that gets found as the superclass
    end

    # Foo::Inner::F, which mixes in the gloabl module Foo
    class F
      # the :: constant prefix says to start looking in the global namespace
      # so here we include the top level module Foo, and not the "closer" in namespace Foo::Inner::Foo
      include ::Foo

      # This is an error. This attempts to include the class Foo::Inner::Foo since thats the closest by namespace
      # thing that matches the constant Foo. (you can't include classes, only modules)
      # You need the :: prefix to grab the global Foo module
      include Foo
    end

  end
end

# Z decalred in the global namespace, which inherits from the deeply nested class Foo::Inner::C
class Z < Foo::Inner::C
  # Any class anywhere can inherit from any other class in any namespace.
  # Just drill in!
end

# the following 2 declarations at this point would be identical

# This defines a class deep with in a namespace
class Foo::Inner::Foo::Bar < Foo::A
end

# same as above, but reopens each namespace
module Foo
  module Inner
    class Foo
      class Bar < ::Foo::A
      end
    end
  end
end
于 2012-11-07T00:23:18.083 回答
6

只需使用class Bar而不是module Bar. 在 Ruby 中,可以重新打开和添加类。

于 2012-11-07T00:22:47.983 回答
1

建议不要将类用作命名空间,尤其是在使用 Rails 时。

如果您想保留 Bar 类lib/bar.rb,一个选项是将 Bars 命名空间中的其他类移到lib/bars.rb.

在此处阅读有关原因的更多信息:https ://blog.jetbrains.com/ruby/2017/03/why-you-should-not-use-a-class-as-a-namespace-in-rails-applications/

更新:此行为已在 ruby​​ 2.5 中修复

请参阅:https ://blog.bigbinary.com/2017/10/18/ruby-2.5-has-removed-top-level-constant-lookup.html

于 2020-10-13T08:21:13.770 回答