$ rubocop example.rb
Inspecting 1 file
C
Offenses:
example.rb:2:3: C: ActiveJob/PerformInTransactions: トランザクションの中でperform_laterすなー
Job.set(:wait_until).perform_later
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
example.rb:9:7: C: ActiveJob/PerformInTransactions: トランザクションの中でperform_laterすなー
SomeJob.perform_later(id) # Copが警告を出す
^^^^^^^^^^^^^^^^^^^^^^^^^
1 file inspected, 2 offenses detected
Last active
November 10, 2024 06:20
-
-
Save ikaruga777/b8b637f8db25a4d6ec8a4e40753fbc05 to your computer and use it in GitHub Desktop.
トランザクションの中でperform_laterするのを検知するCopのたたき
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module RuboCop | |
module Cop | |
module ActiveJob | |
class PerformInTransactions < Base | |
MSG = 'トランザクションの中でperform_laterすなー' | |
BUILT_IN_TRANSACTION_METHODS = %i[transaction with_lock].freeze | |
def_node_search :perform_later_call, <<~PATTERN | |
{ | |
# 標準的なperform_later呼び出し | |
(send _ :perform_later ...) | |
# ActiveJob::Baseを継承したクラスメソッドとしての呼び出し | |
(send (const _ _) :perform_later ...) | |
# キューイング関連のメソッド | |
(send _ {:enqueue_at :enqueue_in} ...) | |
# Sidekiq::Workerを使用している場合 | |
(send _ {:perform_async :perform_at :perform_in} ...) | |
# delayed_jobを使用している場合 | |
(send _ :delay ...) | |
(send (send _ :delay) :perform ...) | |
} | |
PATTERN | |
def on_send(node) | |
return unless in_transaction_block?(node) | |
perform_later_call(node.parent.body).each do |statement_node| | |
next if statement_node.break_type? && nested_block?(statement_node) | |
statement = statement(statement_node) | |
message = format(MSG, statement: statement) | |
add_offense(statement_node, message: message) | |
end | |
end | |
private | |
def in_transaction_block?(node) | |
return false unless transaction_method_name?(node.method_name) | |
return false unless (parent = node.parent) | |
parent.block_type? && parent.body | |
end | |
def statement(statement_node) | |
if statement_node.return_type? | |
'return' | |
elsif statement_node.break_type? | |
'break' | |
else | |
statement_node.method_name | |
end | |
end | |
def nested_block?(statement_node) | |
name = statement_node.ancestors.find(&:block_type?).children.first.method_name | |
!transaction_method_name?(name) | |
end | |
def transaction_method_name?(method_name) | |
BUILT_IN_TRANSACTION_METHODS.include?(method_name) || transaction_method?(method_name) | |
end | |
def transaction_method?(method_name) | |
cop_config.fetch('TransactionMethods', []).include?(method_name.to_s) | |
end | |
end | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Model.transaction do | |
Job.set(:wait_until).perform_later | |
end | |
class User < ApplicationRecord | |
def some_method | |
transaction do | |
save! | |
SomeJob.perform_later(id) # Copが警告を出す | |
end | |
end | |
end | |
class User < ApplicationRecord | |
def some_method | |
transaction do | |
save! | |
end | |
SomeJob.perform_later(id) # トランザクションの外なのでsafe | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment