[英]Why some Ruby constants (classes, modules) should be explicitly required in Rails?
[英]Scope of Constants in Ruby Modules
我在mixin模块中有一个恒定范围的问题。 假设我有类似的东西
module Auth
USER_KEY = "user" unless defined? USER_KEY
def authorize
user_id = session[USER_KEY]
def
end
除非已经定义,否则USER_KEY常量应默认为“user”。 现在我可以将它混合到几个地方,但在其中一个地方USER_KEY需要不同,所以我们可能会有这样的东西
class ApplicationController < ActionController::Base
USER_KEY = "my_user"
include Auth
def test_auth
authorize
end
end
我希望USER_KEY在授权中使用时会是“my_user”,因为它已经定义了,但它仍然是“user”,取自USER_KEY的模块定义。 任何人都知道如何授权使用USER_KEY的类版本?
您在Auth
声明(甚至有条件地)的USER_KEY
全局称为Auth::USER_KEY
。 虽然包括模块可以以非完全合格的方式引用密钥,但它并没有“混入”包含模块。
如果您希望每个包含模块(例如ApplicationController
)能够定义自己的USER_KEY
,请尝试以下方法:
module Auth
DEFAULT_USER_KEY = 'user'
def self.included(base)
unless base.const_defined?(:USER_KEY)
base.const_set :USER_KEY, Auth::DEFAULT_USER_KEY
end
end
def authorize
user_id = session[self.class.const_get(:USER_KEY)]
end
end
class ApplicationController < ActionController::Base
USER_KEY = 'my_user'
include Auth
end
但是,如果你要解决所有这些问题,你可以将其作为一个类方法:
module Auth
DEFAULT_USER_KEY = 'user'
def self.included(base)
base.extend Auth::ClassMethods
base.send :include, Auth::InstanceMethods
end
module ClassMethods
def user_key
Auth::DEFAULT_USER_KEY
end
end
module InstanceMethods
def authorize
user_id = session[self.class.user_key]
end
end
end
class ApplicationController < ActionController::Base
def self.user_key
'my_user'
end
end
或类级访问者:
module Auth
DEFAULT_USER_KEY = 'user'
def self.included(base)
base.send :attr_accessor :user_key unless base.respond_to?(:user_key=)
base.user_key ||= Auth::DEFAULT_USER_KEY
end
def authorize
user_id = session[self.class.user_key]
end
end
class ApplicationController < ActionController::Base
include Auth
self.user_key = 'my_user'
end
常量在Ruby中没有全局范围。 常量可以在任何范围内显示,但您必须指定常量的位置。 当您开始新的类,模块或def时,您开始一个新的范围,如果您想要另一个范围的常量,您必须指定在哪里找到它。
X = 0
class C
X = 1
module M
X = 2
class D
X = 3
puts X # => 3
puts C::X # => 1
puts C::M::X # => 2
puts M::X # => 2
puts ::X # => 0
end
end
end
这是一个简单的解决方案。
变化:
USER_KEY
是否存在。 。
module Auth
USER_KEY = "user"
def authorize
user_key = self.class.const_defined?(:USER_KEY) ? self.class::USER_KEY : USER_KEY
user_id = session[user_key]
def
end
说明
你所看到的行为并不是特定于rails,而是由于ruby查找常量,如果没有通过::
:(我称之为上面的“默认”)显式作用域。 使用“当前正在执行的代码的词法范围”查找常量。 这意味着ruby首先在执行代码的模块(或类)中查找常量,然后向外移动到每个连续的封闭模块(或类),直到找到在该范围上定义的常量。
在您的控制器中,您调用authorize
。 但是当authorize
正在执行时,当前正在执行的代码在Auth
。 这就是查找常量的地方。 如果Auth没有USER_KEY
,但是封闭模块有,那么将使用封闭的模块。 例:
module Outer
USER_KEY = 'outer_key'
module Auth
# code here can access USER_KEY without specifying "Outer::"
# ...
end
end
这种情况的一个特例是顶级执行环境,它被视为属于类Object
。
USER_KEY = 'top-level-key'
module Auth
# code here can access the top-level USER_KEY (which is actually Object::USER_KEY)
# ...
end
一个缺陷是使用作用域运算符( ::
:)定义模块或类:
module Outer
USER_KEY = 'outer_key'
end
module Outer::Auth
# methods here won't be able to use USER_KEY,
# because Outer isn't lexically enclosing Auth.
# ...
end
请注意,常量可以比定义的方法更晚定义。 查询仅在访问USER_KEY时发生,因此这也适用:
module Auth
# don't define USER_KEY yet
# ...
end
# you can't call authorize here or you'll get an uninitialized constant error
Auth::USER_KEY = 'user'
# now you can call authorize.
如果您的项目在Rails中,或者至少使用ActiveSupport
模块,则可以显着减少必要的逻辑糖:
module Auth
extend ActiveSupport::Concern
included do
# set a global default value
unless self.const_defined?(:USER_KEY)
self.const_set :USER_KEY, 'module_user'
end
end
end
class ApplicationController < ActionController::Base
# set an application default value
USER_KEY = "default_user"
include Auth
end
class SomeController < ApplicationController
# set a value unique to a specific controller
USER_KEY = "specific_user"
end
我很惊讶没有人建议这种方法,看看OP的场景如何在Rails应用程序中存在......
OP问题的解决方案远比其他答案显示的简单得多:
module Foo
THIS_CONST = 'foo'
def show_const
self.class::THIS_CONST
end
end
class Bar
include Foo
THIS_CONST ='bar'
def test_it
show_const
end
end
class Baz
include Foo
def test_it
show_const
end
end
2.3.1 :004 > r = Bar.new
=> #<Bar:0x000000008be2c8>
2.3.1 :005 > r.test_it
=> "bar"
2.3.1 :006 > z = Baz.new
=> #<Baz:0x000000008658a8>
2.3.1 :007 > z.test_it
=> "foo"
@ james-a-rosen的回答给了我尝试这个的灵感。 我不想去他的路线,因为我有几个常数在几个类之间共享,每个都有不同的值,他的方法看起来像很多打字。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.