关于 ruby 中常量的说明

Clarification about constants in ruby

我正在尝试构建一个休息客户端,因为很多几乎相同,我想我应该将操作放入一个模块中,然后扩展模块以获得那组操作并保留唯一位通过将它们定义为常量来分开,相同的常量但每条路线的设置不同所以最终结果是不同的 URL 但相同的操作。

module Common
    def list
        "some_url/#{Route_Name}.json"
    end
end

class Posts
    extend Common
    Route_Name = 'posts'
end

class Comments
    extend Common
    Route_Name = 'comments'
end


Comments.list
#=> "some_url/comments.json"  what I expect to be outputted

但它只是出错,在这个例子中它会出错uninitialized constant Common::Route_Name

如何让 Route_Name 成为我期望的样子?

编辑:

我通过将 Route_Name 更改为 @route_name 来解决问题,但问题是 Route_Name 是常数,它永远不会改变,所以使用起来感觉不对一个实例变量,即使它确实有效。

我做过类似的事情,我在模块中定义了几个控制器操作(索引、创建等),然后在我想要的控制器中定义了 include MyModule使用它们。

此时,在我的模块中,我可以调用 self.controller_name 来获取我的实际来源。

因此,与其尝试设置 Route_Name,不如使用 self.controller_name.tableizeself.class.tableize

之类的方法推导它
正如您提到的

@route_name 是一个实例变量并且可以工作,但我认为您实际要寻找的是 @@route_name 这将使它成为 class 级别变量(每个 class,而不是每个实例一个)。

改用const_get。这是一个方法调用(与通过静态名称查找相比)。意思是,它将从祖先层次结构的底部开始不断查找。

module Common
    def list
        "some_url/#{const_get('Route_Name')}.json"
    end
end

class Posts
    extend Common
    Route_Name = 'posts'
end

class Comments
    extend Common
    Route_Name = 'comments'
end


Comments.list # => "some_url/comments.json"

@mtamhankar 关于从 class 名称派生路线有一个很好的观点。可能是这样的(使用来自 ActiveSupport 的 tableize

module Common
    def list
        "some_url/#{name.tableize}.json"
    end
end

A ruby 方法从该方法的 owner 查找常量,而不是接收方或接收方的 class。当您访问常量时,ruby 解释器沿着调用模块的 Module.nesting 或 class.

查找常量
X = 0

module A
  X = 1

  module B
    p Module.nesting  #=> [A::B, A]

    X = 2

    def self.x
      X
    end
  end
end

A::B.x  # Try commenting out any 'X = ?' to see the difference

当您调用 A::B.x 时,ruby 尝试在命名空间 A::B 中查找常量 X(即尝试查找 A::B::X),如果找到后,它会立即停止查找并 returns 该值。如果找不到,那么 ruby 会尝试在 A 中寻找 X,如果仍然找不到,ruby 会尝试在顶级命名空间中寻找 X,这是 Object.

因为 classes 和模块有一个 class/module 方法 name 其中 returns class/module 的名称,你可以重构你的 Common 像这样的模块:

module Common
  def list
    "some_url/#{name.downcase}.json"
  end
end

并歼灭所有Route_Name = 'xxx'

Ruby does constant lookup 首先按名称空间(大致:您嵌套它们的位置)然后按祖先(您继承的)。因为 list 是在 Common 中声明的,查找从 Common::Route_Name 开始,不能再继续了。

但是,当调用 list 时,它会在 CommentsPosts 上调用,让您可以通过 self 访问它们。您可以使用 self::Route_Name 让查找从您想要的地方开始,而不是让 Ruby 去查找常量本身。

module Common
  def list
    "some_url/#{self::Route_Name}.json"
  end
end

class Posts
  extend Common
  Route_Name = 'posts'
end

class Comments
  extend Common
  Route_Name = 'comments'
end

puts Comments.list #=> some_url/comments.json
puts Posts.list    #=> some_url/posts.json