Skip to content

Commit 20c8b38

Browse files
committed
add rails/perform_job_in_after_commit_callback
1 parent ffe7d35 commit 20c8b38

File tree

2 files changed

+194
-0
lines changed

2 files changed

+194
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# frozen_string_literal: true
2+
3+
Synvert::Rewriter.new 'rails', 'perform_job_in_after_commit_callback' do
4+
configure(parser: Synvert::PRISM_PARSER)
5+
6+
description <<~EOS
7+
It prefers performing a job in after_commit callback.
8+
9+
```ruby
10+
class User < ApplicationRecord
11+
after_create :send_notification
12+
13+
def send_notification
14+
NotificationJob.perform_later(self)
15+
end
16+
end
17+
```
18+
19+
=>
20+
21+
```ruby
22+
class User < ApplicationRecord
23+
after_create_commit :send_notification
24+
25+
def send_notification
26+
NotificationJob.perform_later(self)
27+
end
28+
end
29+
```
30+
EOS
31+
32+
call_helper 'ruby/parse'
33+
definitions = load_data :ruby_definitions
34+
job_classes = definitions.find_classes_by_superclass('ApplicationJob').map(&:full_name) +
35+
definitions.find_classes_by_superclass('Sidekiq::Job').map(&:full_name)
36+
mailer_classes = definitions.find_classes_by_superclass('ApplicationMailer').map(&:name)
37+
38+
within_files Synvert::RAILS_MODEL_FILES do
39+
find_node '.class_node' do
40+
callback_names_with_actions = {}
41+
42+
find_node node_type: 'call_node', receiver: nil, name: { in: %i[after_create after_update after_save] } do
43+
callback_names_with_actions[node.arguments.arguments.first.to_value.to_s] =
44+
NodeMutation::ReplaceAction.new(node, :name, with: '{{name}}_commit', adapter: mutation_adapter)
45+
end
46+
47+
find_node node_type: 'def_node', name: { in: callback_names_with_actions.keys } do
48+
if_exist_node ".call_node[receiver=~/\\A(#{job_classes.join('|')})/][name IN (perform_later perform_async perform_in perform_at)]" do
49+
add_action(callback_names_with_actions[node.name.to_s])
50+
end
51+
if_exist_node ".call_node[receiver=~/\\A(#{mailer_classes.join('|')})/][name=deliver_later]" do
52+
add_action(callback_names_with_actions[node.name.to_s])
53+
end
54+
end
55+
end
56+
end
57+
end
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
RSpec.describe 'Rails perform job in after_commit callback' do
6+
before { load_helpers(%w[helpers/parse_ruby]) }
7+
8+
let(:rewriter_name) { 'rails/perform_job_in_after_commit_callback' }
9+
10+
context 'ApplicationJob' do
11+
let(:fake_file_paths) { ['app/jobs/notification_job.rb', 'app/models/user.rb'] }
12+
let(:test_contents) { [<<~EOF.strip, <<~EOF.strip] }
13+
class NotificationJob < ApplicationJob
14+
end
15+
EOF
16+
class User < ApplicationRecord
17+
after_create :send_notification
18+
after_save :update_cache
19+
20+
def send_notification
21+
NotificationJob.perform_later(self)
22+
end
23+
24+
def update_cache
25+
Cache.update(self)
26+
end
27+
end
28+
EOF
29+
let(:test_rewritten_contents) { [<<~EOF.strip, <<~EOF.strip] }
30+
class NotificationJob < ApplicationJob
31+
end
32+
EOF
33+
class User < ApplicationRecord
34+
after_create_commit :send_notification
35+
after_save :update_cache
36+
37+
def send_notification
38+
NotificationJob.perform_later(self)
39+
end
40+
41+
def update_cache
42+
Cache.update(self)
43+
end
44+
end
45+
EOF
46+
47+
include_examples 'convertable with multiple files'
48+
end
49+
50+
context 'Sidekiq' do
51+
let(:fake_file_paths) { ['app/jobs/notification_job.rb', 'app/models/user.rb'] }
52+
let(:test_contents) { [<<~EOF.strip, <<~EOF.strip] }
53+
class NotificationJob
54+
include Sidekiq::Job
55+
56+
def perform(user)
57+
end
58+
end
59+
EOF
60+
class User < ApplicationRecord
61+
after_create :send_notification
62+
after_save :update_cache
63+
64+
def send_notification
65+
NotificationJob.perform_async(self)
66+
end
67+
68+
def update_cache
69+
Cache.update(self)
70+
end
71+
end
72+
EOF
73+
let(:test_rewritten_contents) { [<<~EOF.strip, <<~EOF.strip] }
74+
class NotificationJob
75+
include Sidekiq::Job
76+
77+
def perform(user)
78+
end
79+
end
80+
EOF
81+
class User < ApplicationRecord
82+
after_create_commit :send_notification
83+
after_save :update_cache
84+
85+
def send_notification
86+
NotificationJob.perform_async(self)
87+
end
88+
89+
def update_cache
90+
Cache.update(self)
91+
end
92+
end
93+
EOF
94+
95+
include_examples 'convertable with multiple files'
96+
end
97+
98+
context 'Mailer deliver_later' do
99+
let(:fake_file_paths) { ['app/mailers/user_mailer.rb', 'app/models/user.rb'] }
100+
let(:test_contents) { [<<~EOF.strip, <<~EOF.strip] }
101+
class UserMailer < ApplicationMailer
102+
end
103+
EOF
104+
class User < ApplicationRecord
105+
after_create :send_notification
106+
after_save :update_cache
107+
108+
def send_notification
109+
UserMailer.welcome(self).deliver_later
110+
end
111+
112+
def update_cache
113+
Cache.update(self)
114+
end
115+
end
116+
EOF
117+
let(:test_rewritten_contents) { [<<~EOF.strip, <<~EOF.strip] }
118+
class UserMailer < ApplicationMailer
119+
end
120+
EOF
121+
class User < ApplicationRecord
122+
after_create_commit :send_notification
123+
after_save :update_cache
124+
125+
def send_notification
126+
UserMailer.welcome(self).deliver_later
127+
end
128+
129+
def update_cache
130+
Cache.update(self)
131+
end
132+
end
133+
EOF
134+
135+
include_examples 'convertable with multiple files'
136+
end
137+
end

0 commit comments

Comments
 (0)