簡體   English   中英

模型before_save中的可變范圍

[英]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_nameget_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列,它將定義方法namename= 區別在於,AR對象要復雜得多,並且那些調用讀/寫屬性的作用不只是設置實例變量@name

如果您了解變量/方法范圍的概念,那么您應該清楚何時使用self以及這些方法調用的實際作用。

小總結:

  • 實例var 不是方法 ,它們可以稱為對象的私有屬性
  • 我們定義訪問器方法來訪問實例變量,通常使用attr_accessor,attr_reader(只讀屬性)方法
  • 當訪問某些變量時,Ruby在本地范圍內搜索變量,如果找不到,Ruby會以為它是實例方法並self調用來進行拍攝
  • 寫入accessor( self.name = )時,您需要明確地說出self ,否則Ruby將其視為本地變量的創建。 如果您在本地范圍內具有相同名稱的變量,則在讀取時還需要使用self ,但這是一種非常不好的做法,我懷疑您會遇到這種情況。

簡而言之:如果您訪問方法或局部變量,則無需編寫self 但是,如果要為方法分配值,則始終需要self 如果您不使用self ,Ruby會將值分配給局部變量。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM