active_record-updated_at的disable如何影响block中的表现
总结:使用Thread.current。(相当于ThreadLocal,任何“非传参、非实例变量“的共享变量)
active_record-updated_at一经require,便会 :
simply patches ActiveRecord::Relation#update_all to automatically specify updated_at as Time.current when:
1.An updated_at column exists on the table
2.An updated_at value is not explicitly specified in the query
但也可通过disable来取消为某次update恢复“无updated_at”
ActiveRecord::UpdatedAt.disable do
User.update_all(role: “member”)
User.find(123).update_column(name: "Sean")
end
该block并没有接受任何AR作为参数,block中的User如何知道自己是否被disable了?
答案:用Thread.current来交互。源码很短,如下:
disable会设置Thread.current["UpdatedAt::DISABLED"]为true,然后才执行block,最后确保恢复原state
require "active_record"
require_relative "active_record/updated_at/relation"
module ActiveRecord
module UpdatedAt
ActiveRecord::Relation.send(:include, Relation)
STATE = "#{name}::DISABLED".freeze
class << self
def disable(state = true)
disabled_was = Thread.current[STATE]
Thread.current[STATE] = state
yield
ensure
Thread.current[STATE] = disabled_was
end
def enable(&block)
disable(false, &block)
end
def enabled?
!Thread.current[STATE]
end
end
end
end
UpdatedAt.enabled?是默认为true的。
包装原update_all,若确为true,且有updated_at栏位,且没填值,则拼接上updated_at = Time.current,再去update_all
module ActiveRecord
module UpdatedAt
module Relation
def self.included(base)
base.class_eval do
alias_method :update_all_without_updated_at, :update_all
alias_method :update_all, :update_all_with_updated_at
end
end
def update_all_with_updated_at(query, *args, &block)
attribute_exists = column_names.include?("updated_at")
already_specified = Array(query).flatten.grep(/\bupdated_at\b/).any?
enabled = UpdatedAt.enabled?
updated_at = Time.current
if attribute_exists && !already_specified && enabled
case query
when Array
query.first << ", updated_at = ?"
query << updated_at
when Hash
query[:updated_at] = updated_at
when String
query = ["#{query}, updated_at = ?", updated_at]
end
end
update_all_without_updated_at(query, *args, &block)
end
end
end
end