如何在 RuboCop 中更正 `Style/ClassVars`?
How to correct `Style/ClassVars` in RuboCop?
我看到 issue with using class variables with Ruby; however, it seems RuboCop's documentation 对于如何解决问题是不够的。
现在,我可以忽略它了。鉴于我的项目,这并不重要。但是,我只想知道 Rubocop 试图告诉我做什么,因为它没有意义。
用 Ruby 2.5.1
在 irb 0.9.6
中执行提供的 code 得到:
class A
@test = 10
end
#=> 10
class A
def test
@@test # you can access class variable without offense
end
end
#=> :test
A.new.test
Traceback (most recent call last):
3: from /Users/Ricky/.rbenv/versions/2.5.1/bin/irb:11:in `<main>'
2: from (irb):12
1: from (irb):9:in `test'
NameError (uninitialized class variable @@test in A)
Did you mean? @test
所以,没有。显然,我们不能毫无冒犯地访问 class 变量。 irb 非常 被冒犯了。但是,ruby 建议使用 @test
。也许这只是一个错字?让我们试试吧:
class A
@test = 10
def test
@test # you can access class variable without offense
end
end
#=> :test
A.new.test
#=> nil
因此,从未定义实例变量。 RuboCop 在这里想说什么?
您忽略了变量范围之间的区别。
class A
@test = 42
end
上面在class范围内声明了一个实例变量。它可以访问为
A.instance_variable_get(:@test)
#⇒ 42
您可以为此变量定义访问器:
class A
@test = 42
def self.test
@test
end
end
A.test #⇒ 42
它在实例之间共享,要从实例访问它,您应该参考 class:
# ⇓⇓⇓⇓⇓ HERE
A.new.class.test #⇒ 42
以下代码在 class 实例:
上声明了一个实例变量
class A
def initialize
@test = 42
end
end
可以从 A
的 个实例 访问:
A.new.instance_variable_get(:@test)
#⇒ 42
Class 变量在 class 层次结构中使用时有一些缺点,这就是 [可能] 为什么 Rubocop 建议不要使用 class 变量(或它建议的任何东西——老实说从来没有用过它,因为它带来的伤害多于帮助恕我直言。)
在您的第一个片段中,您错过了 @
。正确的代码是:
class A
# ⇓⇓ HERE
@@test = 10
end
class A
def test
@@test # you can access class variable without offense
end
end
最佳答案的浓缩版:
class A
@test = 10
def test
@test # the instance's instance variable, which will be nil
class.instance_variable_get(:@thing) # the class's instance variable, which you set to 10
end
end
2023年初,问题依旧。因为 rubocop 文档不是 post 有关 ruby.
中 OOP 复杂性的信息的地方
不喜欢使用 class 变量来自于我们使用 class 继承时的意外行为。但是我们爱看代码,不爱看描述,文档里明明写着:
You have to be careful when setting a value for a class variable; if a class has been inherited, changing the value of a class variable also affects the inheriting classes. This means that it's almost always better to use a class instance variable instead.
我想补充 Alexey Matyushkin 的回答,并用简单的例子展示 class 变量的行为。并解释这会导致什么。
我确认 rubocop 文档中的代码是某种胡说八道:
# good
class A
@test = 10
end
class A
def test
@@test # you can access class variable without offense
end
end
class A
def self.test(name)
class_variable_get("@@#{name}") # you can access without offense
end
end
begin
puts A.new.test
rescue => e
puts e.message
end
begin
puts A.test 'test'
rescue => e
puts e.message
end
puts "RUBY_VERSION: #{RUBY_VERSION}"
=>>>
uninitialized class variable @@test in A
Did you mean? @test
uninitialized class variable @@test in A
Did you mean? @test
RUBY_VERSION: 2.5.3
rubocop 真正想告诉我们的是什么。
puts 'When we use "classic" class variables:'
class A
@@var = 10
cattr_accessor :var
end
class Aa < A
end
puts Aa.var, '- the child class has inherited the methods and the value of the variable.'
Aa.var = 20
puts A.var, '- but the variable of the parent class was implicitly changed (bad)!'
puts 'When we use class instance variables:'
class B
@test = 10
class << self
attr_accessor :test
end
end
class Bb < B
end
puts Bb.test, '- the child class has inherited the methods, but not the value of the variable (this is also bad)!'
Bb.test = 20
puts B.test, '- a change in the child class does not lead to a change in the parent.'
=>>>
When we use "classic" class variables:
10
- the child class has inherited the methods and the value of the variable.
20
- but the variable of the parent class was implicitly changed (bad)!
When we use class instance variables:
- the child class has inherited the methods, but not the value of the variable (this is also bad)!
10
- a change in the child class does not lead to a change in the parent.
这有什么大不了的?这有什么害处?
修改 BIG 程序的一种方法是继承 class 并对其进行自己的更改。通常项目很复杂,有很多隐式依赖(说实话 =)),如果你直接对 class 进行更改,项目会崩溃。因此,我们使用继承,child class 用于具有自己设置的新服务,或者 child class 改变程序的一部分的行为。而如果在继承的过程中,childclass突然改变了基class,那么继承就失去了意义!失去灵活性。
但任何问题都需要结合上下文来看待。如果你是一个人在写一个微型项目,那么@@ var 也没什么问题。你只需要理解。
我看到 issue with using class variables with Ruby; however, it seems RuboCop's documentation 对于如何解决问题是不够的。
现在,我可以忽略它了。鉴于我的项目,这并不重要。但是,我只想知道 Rubocop 试图告诉我做什么,因为它没有意义。
用 Ruby 2.5.1
在 irb 0.9.6
中执行提供的 code 得到:
class A
@test = 10
end
#=> 10
class A
def test
@@test # you can access class variable without offense
end
end
#=> :test
A.new.test
Traceback (most recent call last):
3: from /Users/Ricky/.rbenv/versions/2.5.1/bin/irb:11:in `<main>'
2: from (irb):12
1: from (irb):9:in `test'
NameError (uninitialized class variable @@test in A)
Did you mean? @test
所以,没有。显然,我们不能毫无冒犯地访问 class 变量。 irb 非常 被冒犯了。但是,ruby 建议使用 @test
。也许这只是一个错字?让我们试试吧:
class A
@test = 10
def test
@test # you can access class variable without offense
end
end
#=> :test
A.new.test
#=> nil
因此,从未定义实例变量。 RuboCop 在这里想说什么?
您忽略了变量范围之间的区别。
class A
@test = 42
end
上面在class范围内声明了一个实例变量。它可以访问为
A.instance_variable_get(:@test)
#⇒ 42
您可以为此变量定义访问器:
class A
@test = 42
def self.test
@test
end
end
A.test #⇒ 42
它在实例之间共享,要从实例访问它,您应该参考 class:
# ⇓⇓⇓⇓⇓ HERE
A.new.class.test #⇒ 42
以下代码在 class 实例:
上声明了一个实例变量class A
def initialize
@test = 42
end
end
可以从 A
的 个实例 访问:
A.new.instance_variable_get(:@test)
#⇒ 42
Class 变量在 class 层次结构中使用时有一些缺点,这就是 [可能] 为什么 Rubocop 建议不要使用 class 变量(或它建议的任何东西——老实说从来没有用过它,因为它带来的伤害多于帮助恕我直言。)
在您的第一个片段中,您错过了 @
。正确的代码是:
class A
# ⇓⇓ HERE
@@test = 10
end
class A
def test
@@test # you can access class variable without offense
end
end
最佳答案的浓缩版:
class A
@test = 10
def test
@test # the instance's instance variable, which will be nil
class.instance_variable_get(:@thing) # the class's instance variable, which you set to 10
end
end
2023年初,问题依旧。因为 rubocop 文档不是 post 有关 ruby.
中 OOP 复杂性的信息的地方不喜欢使用 class 变量来自于我们使用 class 继承时的意外行为。但是我们爱看代码,不爱看描述,文档里明明写着:
You have to be careful when setting a value for a class variable; if a class has been inherited, changing the value of a class variable also affects the inheriting classes. This means that it's almost always better to use a class instance variable instead.
我想补充 Alexey Matyushkin 的回答,并用简单的例子展示 class 变量的行为。并解释这会导致什么。
我确认 rubocop 文档中的代码是某种胡说八道:
# good
class A
@test = 10
end
class A
def test
@@test # you can access class variable without offense
end
end
class A
def self.test(name)
class_variable_get("@@#{name}") # you can access without offense
end
end
begin
puts A.new.test
rescue => e
puts e.message
end
begin
puts A.test 'test'
rescue => e
puts e.message
end
puts "RUBY_VERSION: #{RUBY_VERSION}"
=>>>
uninitialized class variable @@test in A
Did you mean? @test
uninitialized class variable @@test in A
Did you mean? @test
RUBY_VERSION: 2.5.3
rubocop 真正想告诉我们的是什么。
puts 'When we use "classic" class variables:'
class A
@@var = 10
cattr_accessor :var
end
class Aa < A
end
puts Aa.var, '- the child class has inherited the methods and the value of the variable.'
Aa.var = 20
puts A.var, '- but the variable of the parent class was implicitly changed (bad)!'
puts 'When we use class instance variables:'
class B
@test = 10
class << self
attr_accessor :test
end
end
class Bb < B
end
puts Bb.test, '- the child class has inherited the methods, but not the value of the variable (this is also bad)!'
Bb.test = 20
puts B.test, '- a change in the child class does not lead to a change in the parent.'
=>>>
When we use "classic" class variables:
10
- the child class has inherited the methods and the value of the variable.
20
- but the variable of the parent class was implicitly changed (bad)!
When we use class instance variables:
- the child class has inherited the methods, but not the value of the variable (this is also bad)!
10
- a change in the child class does not lead to a change in the parent.
这有什么大不了的?这有什么害处?
修改 BIG 程序的一种方法是继承 class 并对其进行自己的更改。通常项目很复杂,有很多隐式依赖(说实话 =)),如果你直接对 class 进行更改,项目会崩溃。因此,我们使用继承,child class 用于具有自己设置的新服务,或者 child class 改变程序的一部分的行为。而如果在继承的过程中,childclass突然改变了基class,那么继承就失去了意义!失去灵活性。
但任何问题都需要结合上下文来看待。如果你是一个人在写一个微型项目,那么@@ var 也没什么问题。你只需要理解。