はじめに
業務で、子レコードの作成、更新時に、親の更新日時だけを更新したいことがあってActiveRecordの関連付けで、touchオプションを使えば簡単にできること知ったのでtouchの処理をChatGPTに処理解説してもらった!
どうやって使えばいいかは、今回省略しますのでRailsガイドを見てください。
def before_committed!
touch_deferred_attributes if has_defer_touch_attrs? && persisted?
super
end
-
def before_committed!
:-
before_committed!
メソッドを定義します。 - これは、データベースへのコミットが行われる前に呼び出されるコールバックメソッドです。
-
-
touch_deferred_attributes if has_defer_touch_attrs? && persisted?
:- もし遅延タッチの属性があり(
has_defer_touch_attrs?
がtrue)、かレコードがデータベースに既に存在している場合(persisted?
がtrue)に、touch_deferred_attributes
メソッドを呼び出します。
- もし遅延タッチの属性があり(
-
super
:- 親クラス(この場合は同じメソッド名の親モジュール内のメソッド)を呼び出します。
補足
遅延タッチの属性とは?
遅延タッチ(Deferred Touch)は、ActiveRecordモデルで親レコードの更新日時を更新する際に、実際に更新を行うのではなく、更新が必要な属性を一時的に記憶しておく機能を指します。
具体的には、親レコードの更新が行われる際に、関連する子レコードの更新も必要である場合に、子レコードの更新を遅延させておき、親レコードがコミット(保存)されるタイミングで子レコードを実際に更新するための属性のリストを保持する仕組みです。
def touch_later(*names) # :nodoc:
_raise_record_not_touched_error unless persisted?
@_defer_touch_attrs ||= timestamp_attributes_for_update_in_model
@_defer_touch_attrs |= names.map! do |name|
name = name.to_s
self.class.attribute_aliases[name] || name
end unless names.empty?
@_touch_time = current_time_from_proper_timezone
surreptitiously_touch @_defer_touch_attrs
add_to_transaction
@_new_record_before_last_commit ||= false
# touch the parents as we are not calling the after_save callbacks
self.class.reflect_on_all_associations.each do |r|
if touch = r.options[:touch]
if r.macro == :belongs_to
ActiveRecord::Associations::Builder::BelongsTo.touch_record(self, changes_to_save, r.foreign_key, r.name, touch)
elsif r.macro == :has_one
ActiveRecord::Associations::Builder::HasOne.touch_record(self, r.name, touch)
end
end
end
end
-
def touch_later(*names)
:-
touch_later
メソッドを定義します。 - このメソッドは、遅延してタイムスタンプを更新するためのものです。
-
-
_raise_record_not_touched_error unless persisted?
:- もしレコードが存在していない場合は、エラーを発生させます。
-
@_defer_touch_attrs ||= timestamp_attributes_for_update_in_model
:-
@_defer_touch_attrs
が未定義の場合、timestamp_attributes_for_update_in_model
の結果を代入します。
-
-
@_defer_touch_attrs |= names.map! do |name| ... end unless names.empty?
:-
names
が空でない場合、names
配列内の属性名を処理して、@_defer_touch_attrs
に追加します。
-
-
@_touch_time = current_time_from_proper_timezone
:- 現在の時刻を適切なタイムゾーンで取得し、
@_touch_time
に代入します。
- 現在の時刻を適切なタイムゾーンで取得し、
-
surreptitiously_touch @_defer_touch_attrs
:-
surreptitiously_touch
メソッドを呼び出し、遅延タッチの属性を更新します。
-
-
add_to_transaction
:- トランザクションにレコードを追加します。
-
@_new_record_before_last_commit ||= false
:- インスタンス変数
@_new_record_before_last_commit
が未定義の場合、false
を代入します。
- インスタンス変数
-
self.class.reflect_on_all_associations.each do |r| ... end
:- モデルの関連(アソシエーション)を反映し、それぞれに対して処理を行います。
-
if touch = r.options[:touch]
:- 関連に
:touch
オプションが指定されていれば、その内容を取得します。
- 関連に
-
elsif r.macro == :belongs_to
:- 関連がbelongs_toの場合の処理を行います。
-
elsif r.macro == :has_one
:- 関連がhas_oneの場合の処理を行います。
def touch(*names, time: nil) # :nodoc:
if has_defer_touch_attrs?
names |= @_defer_touch_attrs
super(*names, time: time)
@_defer_touch_attrs, @_touch_time = nil, nil
else
super
end
end
-
def touch(*names, time: nil) # :nodoc:
-
touch
メソッドの定義を開始します。このメソッドは、親レコードの更新日時を更新するためのものであり、引数として任意の属性名(names
)およびtime
を受け取ります。
-
-
if has_defer_touch_attrs?
-
has_defer_touch_attrs?
メソッドがtrueを返すかどうかをチェックします。これは、親レコードの更新日時を遅延させる属性があるかどうかを確認するメソッドです。
-
-
names |= @_defer_touch_attrs
- 遅延させる属性のリスト(
@_defer_touch_attrs
)を引数のnames
に追加します。names
に@_defer_touch_attrs
を追加しています。
- 遅延させる属性のリスト(
-
super(*names, time: time)
- 親クラスの同名メソッドを呼び出します。
super
キーワードは、親クラスの同名のメソッドを呼び出すために使用されます。ここでは、親クラスのtouch
メソッドを呼び出しています。*names
は可変長引数であり、names
の要素をメソッドの引数として渡します。
- 親クラスの同名メソッドを呼び出します。
-
@_defer_touch_attrs, @_touch_time = nil, nil
-
@_defer_touch_attrs
および@_touch_time
をnil
に設定します。これにより、遅延させる属性のリストとタイムスタンプがリセットされます。
-
-
else
- もし
has_defer_touch_attrs?
がfalseの場合、つまり遅延させる属性がない場合に実行される処理を示します。
- もし
-
super
- 親クラスの同名メソッドを呼び出します。
super
キーワードを使用することで、親クラスの同名のメソッドが呼び出されます。ここでは、親クラスのtouch
メソッドが呼び出されます。
- 親クラスの同名メソッドを呼び出します。
参考