forked from rubocop/rubocop-rails
-
Notifications
You must be signed in to change notification settings - Fork 0
/
active_support_on_load.rb
90 lines (83 loc) · 4.17 KB
/
active_support_on_load.rb
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# frozen_string_literal: true
module RuboCop
module Cop
module Rails
# Checks for Rails framework classes that are patched directly instead of using Active Support load hooks. Direct
# patching forcibly loads the framework referenced, using hooks defers loading until it's actually needed.
#
# @safety
# While using lazy load hooks is recommended, it changes the order in which is code is loaded and may reveal
# load order dependency bugs.
#
# @example
#
# # bad
# ActiveRecord::Base.include(MyClass)
#
# # good
# ActiveSupport.on_load(:active_record) { include MyClass }
class ActiveSupportOnLoad < Base
extend AutoCorrector
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
RESTRICT_ON_SEND = %i[prepend include extend].freeze
LOAD_HOOKS = {
'ActionCable' => 'action_cable',
'ActionCable::Channel::Base' => 'action_cable_channel',
'ActionCable::Connection::Base' => 'action_cable_connection',
'ActionCable::Connection::TestCase' => 'action_cable_connection_test_case',
'ActionController::API' => 'action_controller',
'ActionController::Base' => 'action_controller',
'ActionController::TestCase' => 'action_controller_test_case',
'ActionDispatch::IntegrationTest' => 'action_dispatch_integration_test',
'ActionDispatch::Request' => 'action_dispatch_request',
'ActionDispatch::Response' => 'action_dispatch_response',
'ActionDispatch::SystemTestCase' => 'action_dispatch_system_test_case',
'ActionMailbox::Base' => 'action_mailbox',
'ActionMailbox::InboundEmail' => 'action_mailbox_inbound_email',
'ActionMailbox::Record' => 'action_mailbox_record',
'ActionMailbox::TestCase' => 'action_mailbox_test_case',
'ActionMailer::Base' => 'action_mailer',
'ActionMailer::TestCase' => 'action_mailer_test_case',
'ActionText::Content' => 'action_text_content',
'ActionText::Record' => 'action_text_record',
'ActionText::RichText' => 'action_text_rich_text',
'ActionView::Base' => 'action_view',
'ActionView::TestCase' => 'action_view_test_case',
'ActiveJob::Base' => 'active_job',
'ActiveJob::TestCase' => 'active_job_test_case',
'ActiveRecord::Base' => 'active_record',
'ActiveStorage::Attachment' => 'active_storage_attachment',
'ActiveStorage::Blob' => 'active_storage_blob',
'ActiveStorage::Record' => 'active_storage_record',
'ActiveStorage::VariantRecord' => 'active_storage_variant_record',
'ActiveSupport::TestCase' => 'active_support_test_case'
}.freeze
RAILS_5_2_LOAD_HOOKS = {
'ActiveRecord::ConnectionAdapters::SQLite3Adapter' => 'active_record_sqlite3adapter'
}.freeze
RAILS_7_1_LOAD_HOOKS = {
'ActiveRecord::TestFixtures' => 'active_record_fixtures',
'ActiveModel::Model' => 'active_model',
'ActionText::EncryptedRichText' => 'action_text_encrypted_rich_text',
'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter' => 'active_record_postgresqladapter',
'ActiveRecord::ConnectionAdapters::Mysql2Adapter' => 'active_record_mysql2adapter',
'ActiveRecord::ConnectionAdapters::TrilogyAdapter' => 'active_record_trilogyadapter'
}.freeze
def on_send(node)
receiver, method, arguments = *node # rubocop:disable InternalAffairs/NodeDestructuring
return unless arguments && (hook = hook_for_const(receiver&.const_name))
preferred = "ActiveSupport.on_load(:#{hook}) { #{method} #{arguments.source} }"
add_offense(node, message: format(MSG, prefer: preferred, current: node.source)) do |corrector|
corrector.replace(node, preferred)
end
end
def hook_for_const(const_name)
hook = LOAD_HOOKS[const_name]
hook ||= RAILS_5_2_LOAD_HOOKS[const_name] if target_rails_version >= 5.2
hook ||= RAILS_7_1_LOAD_HOOKS[const_name] if target_rails_version >= 7.1
hook
end
end
end
end
end