[英]Variable scope in model before_save
這是從頭開始在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
? 為什么在這種情況下會自動識別實例變量?
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">
在對象中定義它們很常見,因此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.name =
)時,您需要明確地說出self
,否則Ruby將其視為本地變量的創建。 如果您在本地范圍內具有相同名稱的變量,則在讀取時還需要使用self
,但這是一種非常不好的做法,我懷疑您會遇到這種情況。 簡而言之:如果您訪問方法或局部變量,則無需編寫self
。 但是,如果要為方法分配值,則始終需要self
。 如果您不使用self
,Ruby會將值分配給局部變量。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.