这是Rails Casts#250身份验证中的代码:
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
attr_accessor :password
before_save :encrypt_password
...
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
在encrypt_password
中,为什么在生成password_hash
时,传递给hash_secret
的参数是password_salt
而不是self.password_salt
?为什么在这种情况下它会自动识别实例变量?
答案 0 :(得分:0)
Ruby有一个回退机制。考虑这个例子:
class SomeClass
def my_method
"my method"
end
def other_method_with_local_variable
my_method = "lala"
puts my_method
end
def other_method
my_method
end
end
现在让我们在控制台中测试这些方法:
1.9.3p448 :016 > SomeClass.new.other_method # outputs instance method value, because there were none local variables inside the method and it falled back to instance scope
=> "my method"
1.9.3p448 :017 > SomeClass.new.other_method_with_local_variable # outputs "lala" because it's the value of lcoal variable
lala
不理解这个概念的人经常过度使用self
,这让经验丰富的Ruby开发人员的眼睛流血:D
<强> UPD 强>
似乎您将实例方法与实例变量混淆。如果您来自其他OOP语言,则与实例var最接近的类比将是私有属性/属性。当然在Ruby中,无论如何都可以通过一些变通方法来实现它。例如:
class Person
def initialize(name)
@name = name
end
end
p = Person.new("John")
# => #<Person:0x007fbe910cc680 @name="John">
p.name #produces error, because object doesn't have such method, only a private property
#NoMethodError: undefined method `name' for #<Person:0x007fbe910cc680 @name="John">
p.instance_variables # we can always get list of instance variables
# => [:@name]
p.instance_variable_get(:@name) # access them
# => "John"
p.instance_variable_set(:@name, "Ben") # and set them
# => "Ben"
p
# <Person:0x007fbe910cc680 @name="Ben">
以这种方式混淆对象内部被认为是非常糟糕的习惯,我们应该使用该类的公共接口。
在许多语言中,人们通常会开始为这些属性定义访问者/设置者。在类中查看类似set_name
,get_name
的方法是很常见的。 Ruby语法允许和约定建议这样做:
class Person
def initialize(name)
@name = name
end
def name
@name
end
def name=(new_name)
@name = new_name
end
end
1.9.3p448 :015 > p = Person.new("John")
# => #<Person:0x007f8b8b20ad28 @name="John">
1.9.3p448 :016 > p.name
# => "John"
1.9.3p448 :017 > p.name = "Ben"
# => "Ben"
1.9.3p448 :018 > p
# => #<Person:0x007f8b8b20ad28 @name="Ben">
在Your对象中定义它们是很常见的,因此Ruby提供了快捷方式。我们可以像这样重构我们的课程:
class Person
attr_accessor :name
def initialize(name)
@name = name
end
end
1.9.3p448 :009 > p = Person.new("John")
# => #<Person:0x007f8091233a48 @name="John">
1.9.3p448 :010 > p.name
# => "John"
1.9.3p448 :011 > p.name = "Ben"
# => "Ben"
1.9.3p448 :012 > p
# => #<Person:0x007f8091233a48 @name="Ben">
我们实际上可以验证attr_accessor
是否完全相同:
1.9.3p448 :013 > Person.instance_methods.grep(/^name=?/)
# => [:name, :name=]
现在回到ActiveRecord。它以相同的方式为您的列定义方法,因此对于name
列,它将定义方法name
和name=
。不同之处在于,AR对象要复杂得多,并且那些调用读/写属性的东西不仅仅是设置实例变量@name
。
如果您已经理解了变量/方法范围的概念,那么您可能应该清楚何时使用self
以及这些方法调用实际上做了什么。
小摘要:
self
上调用它self
)时,您需要明确说出self.name =
,否则Ruby会将其视为本地变量的创建。如果你在本地范围内有一个同名变量,你还需要在阅读时使用self
,但这是一个非常糟糕的做法,我怀疑你会遇到这样的。答案 1 :(得分:0)
简而言之:如果您访问方法或局部变量,则无需编写self
。但是,如果要为方法分配值,则始终需要self
。如果不使用self
,Ruby会将值赋给局部变量。