我的一个项目中有如下文件夹结构:
bar.rb
require File.expand_path(File.dirname(__FILE__) + "/bar/other_bar.rb")
class Bar
puts "running BarBase"
end
酒吧/ other_bar.rb
module Bar
class OtherBar
puts "running module Bar with class OtherBar"
end
end
如果我现在运行ruby bar.rb
我就明白了:
运行带有类OtherBar的模块栏 bar.rb:3:in'':Bar不是类(TypeError)
我想要一个类似于rails模型继承结构的结构。我怎样才能解决这个问题?据我所知,ruby不支持开箱即用。这种情况有解决办法吗?
答案 0 :(得分:31)
Bar
不能是模块和类,它们是不同的东西。
将bar.rb
更改为module Bar
或将other_bar.rb
更改为class 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
答案 1 :(得分:2)
只需使用class Bar
代替module Bar
。在Ruby中,可以重新打开类并将其添加到。
答案 2 :(得分:0)
建议不要将类用作命名空间,尤其是在使用Rails的情况下。
如果要将Bar类保留在lib/bar.rb
中,可以选择将其他类移至lib/bars.rb
中Bars命名空间中。
更新:此行为在ruby 2.5中已修复
请参阅:https://blog.bigbinary.com/2017/10/18/ruby-2.5-has-removed-top-level-constant-lookup.html