理解ActiveRecord::Concern:
參考:include和extend的區別:
https://www.cnblogs.com/chentianwei/p/9408963.html
?
傳統的模塊看起來像:
module Mdef self.included(base) # base(一個類)擴展了一個模塊"ClassMethods", base的類方法就包含了"ClassMethods"模塊中的方法。base.extend ClassMethods # base添加了一個:disabled方法。base.class_eval doscope :disabled, -> { where(disabled: true) }endendmodule ClassMethods...end end
?
使用ActiveSupport::Concern:
?
require 'active_support/concern'module M# M擴展了模塊Concern,可以使用Concern的方法。 extend ActiveSupport::Concern# 當M被一個類包括后,這個類就可以使用塊內的方法了。 included doscope :disabled, -> { where(disabled: true) }end# 當M被一個類包括后,這個類的類方法就擴展了,?的方法就作為類方法使用。 class_methods do...end end
?
gem 'name_of_person'
一個小的gem,為英文網站用戶的注冊名字添加了很多調用的方法。
https://github.com/basecamp/name_of_person/tree/master/lib/name_of_person
- 加載了gem后,
- ActiveRecord::Base包含了模塊HasPersonName,?就可以使用lib/name_of_person/has_person_name.rb中的方法:類方法has_person_name.
- 在Rails app中, app/model/user.rb, 使用has_person_name方法后,就include包含了模塊Assignable。 User的實例就新增了2個實例方法,這兩個方法會調用模塊PersonName中的方法
- @user.name=: 調用PersonName.full(name)方法,@user的first_name, last_name屬性被分配值。
- @user.name:? 返回一個PersonName.new對象,這個對象可以使用:
- full | initials | familiar 等定義在模塊PersonName中的方法。
- first | last
使用方法:
1 . User類必須包括first_name, last_name2個屬性,添加validates :first_name, :last_name, presence: true
2. 當實例化一個@user時,代碼內部調用name= 方法為first_name, last_name屬性分配值!
(這里不是很理解,是否是devise這個gem,當發現必須驗證first_name, last_name后,自動調用name=方法?)
3. 之后通過@user.name.xxx就可以使用不同的名和姓的組合。
?
分析:先看三張圖:
圖2
?
?
圖3:
?
?
?
@user.name的內部運行機制:
首先是一個判斷:
if @user.first_nameNameOfPerson::PersonName.new(@user.first_name, @user.last_name) end
如果first_name存在,則新增一個PersonName對象,調用initialize方法
def initialize(first, last = nil)raise ArgumentError, "First name is required" unless first.present?@first, @last = first, lastsuper fullend
然后調用full這個方法,進行if判斷
def full@full ||= last.present? ? "#{first} #{last}" : firstend
分析:
如果@user.last_name存在(last.present?),則 把@user的兩個name屬性合并,并分配給@full對象。
最后返回一個PersonName對象實例,?內部包括@first, @full, 及@last(根據@user決定是否存在)
?
@user.name = "Dav Tom"內部運行分析:
def name=(name)full_name = NameOfPerson::PersonName.full(name)self.first_name, self.last_name = full_name.try(:first), full_name.try(:last)end
?
首先:調用模塊PersonName的類方法full。
- 把傳入的字符串參數分成first, last變量
- 如果first變量存在,則新建一個PersonName對象
- 之后的分析和@ueser.name相同。?
def self.full(full_name)first, last = full_name.to_s.strip.split(/\s+/, 2)new(first, last) if first.present?end
?