何度も気になって調べてるのでメモしとく
かたっぱしからコールバック仕込んで実行順番とか確認
family(0|1) - (n)member なモデルです
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
class Member < ApplicationRecord
belongs_to :family, optional: true, touch: true
validate :some_validation
def some_validation
errors.add('error') if name&.include?('hoge')
end
module CallbackCheck
class << self
def callback_kinds
{
before: %i[validation save create update destroy],
after: %i[validation save create update destroy commit rollback initialize find],
around: %i[save create update destroy]
}
end
def included(k)
callback_kinds.each do |timing, actions|
actions.each do |action|
cb = "#{timing}_#{action}"
cb_method = "callback_#{timing}_#{action}"
k.define_method cb_method do |&block|
puts cb
block.call if block && timing == :around
end
k.send(cb.to_sym, cb_method.to_sym)
end
end
end
end
end
include CallbackCheck
end
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
> Member.create
after_initialize
before_validation
after_validation
before_save
around_save
before_create
around_create
TRANSACTION (0.2ms) BEGIN
Member Create (0.3ms) INSERT INTO `members` (`name`, `family_id`, ...
after_create
after_save
TRANSACTION (6.2ms) COMMIT
after_commit
=> #<Member:0x00007fb6dfa27090 id: 6, name: nil, family_id: nil, creat...
|
after_saveの後でタッチ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
> Member.create(family: Family.first)
Family Load (0.3ms) SELECT `families`.* FROM `families` ORDER BY `families`.`id` ASC LIMIT 1
after_initialize
before_validation
after_validation
before_save
around_save
before_create
around_create
TRANSACTION (0.3ms) BEGIN
Member Create (0.3ms) INSERT INTO `members` (`name`, `family_id`...
after_create
after_save
Family Update (0.5ms) UPDATE `families` SET `families`.`updated_at` = ...
TRANSACTION (5.6ms) COMMIT
after_commit
=> #<Member:0x00007fb6df8b42d0 id: 7, name: nil, family_id: 1, created_at...
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
> m = Member.new
after_initialize
=> #<Member:0x00007fb6dfe42508 id: nil, name: nil, family_id: ni...
irb(main):010:0> m.save
before_validation
after_validation
before_save
around_save
before_create
around_create
TRANSACTION (0.3ms) BEGIN
Member Create (0.4ms) INSERT INTO `members` (`name`, `family_...
after_create
after_save
TRANSACTION (9.9ms) COMMIT
after_commit
=> true
|
1
2
3
4
5
6
7
|
> m = Member.new(name: "hoge")
after_initialize
=> #<Member:0x00007fb6df9fcca0 id: nil, name: "hoge", family_id: n...
irb(main):012:0> m.save
before_validation
after_validation
=> false
|
findでもafter_initialize走るのか。
まぁそりゃそうか。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
> m = Member.find(1)
Member Load (0.4ms) SELECT `members`.* FROM `members` WHERE `members`.`id` = 1 LIMIT 1
after_find
after_initialize
=> #<Member:0x00007fb6dfb27be8 id: 1, name: "赤柴", family_id: nil, ...
irb(main):016:0> m.update(name: "foo")
before_validation
after_validation
before_save
around_save
before_update
around_update
TRANSACTION (0.3ms) BEGIN
Member Update (0.4ms) UPDATE `members` SET `members`.`name` = 'foo'...
after_update
after_save
TRANSACTION (4.8ms) COMMIT
after_commit
=> true
|
違和感なし。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
> m.name = 'bar'
=> "bar"
irb(main):021:0> m.save!
before_validation
after_validation
before_save
around_save
before_update
around_update
TRANSACTION (0.3ms) BEGIN
Member Update (0.4ms) UPDATE `members` SET `members`.`name` = 'bar', `members`.`updated_at` = '2023-02-10 08:10:12.901602' WHERE `members`.`id` = 1
after_update
after_save
TRANSACTION (8.7ms) COMMIT
after_commit
=> true
|
Memberに以下を追加
1
2
3
4
5
|
after_commit :recommit
def recommit
update(name: self.name + "_recommit")
end
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
> m.name = 'baz'
=> "baz"
irb(main):003:0> m.save
before_validation
after_validation
before_save
around_save
before_update
around_update
TRANSACTION (0.3ms) BEGIN
Member Update (0.4ms) UPDATE `members` SET `members`.`name` = 'baz',...
after_update
after_save
TRANSACTION (9.1ms) COMMIT
after_commit
before_validation
after_validation
before_save
around_save
before_update
around_update
TRANSACTION (0.2ms) BEGIN
Member Update (0.4ms) UPDATE `members` SET `members`.`name` = 'baz_recommit',...
after_update
after_save
TRANSACTION (2.9ms) COMMIT
after_commit
before_validation
after_validation
before_save
around_save
before_update
around_update
TRANSACTION (0.2ms) BEGIN
Member Update (0.2ms) UPDATE `members` SET `members`.`name` = 'baz_recommit_recommit', ...
after_update
after_save
TRANSACTION (3.2ms) COMMIT
after_commit
|
以降nameの桁があふれるまでループしてエラー
やべぇ
モデル単体でこんなロジックが必要になるはずはない
機能で閉じたクラス作ってそのトランザクションの中で解決すべきだと改めて思った。
callbackに :if つけれるよとか同僚に言われたけど無駄に複雑すぎるしテストしにくいし良いこと無くない?
コメント