forked from rubocop/rubocop-rails
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rake_environment.rb
128 lines (111 loc) · 3.78 KB
/
rake_environment.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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# frozen_string_literal: true
module RuboCop
module Cop
module Rails
# Checks for Rake tasks without the `:environment` task
# dependency. The `:environment` task loads application code for other
# Rake tasks. Without it, tasks cannot make use of application code like
# models.
#
# You can ignore the offense if the task satisfies at least one of the
# following conditions:
#
# * The task does not need application code.
# * The task invokes the `:environment` task.
#
# @safety
# Probably not a problem in most cases, but it is possible that calling `:environment` task
# will break a behavior. It's also slower. E.g. some task that only needs one gem to be
# loaded to run will run significantly faster without loading the whole application.
#
# @example
# # bad
# task :foo do
# do_something
# end
#
# # good
# task foo: :environment do
# do_something
# end
#
class RakeEnvironment < Base
extend AutoCorrector
MSG = 'Include `:environment` task as a dependency for all Rake tasks.'
def_node_matcher :task_definition?, <<~PATTERN
(block $(send nil? :task ...) ...)
PATTERN
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
task_definition?(node) do |task_method|
return if task_name(task_method) == :default
return if with_dependencies?(task_method)
add_offense(task_method) do |corrector|
if with_arguments?(task_method)
new_task_dependency = correct_task_arguments_dependency(task_method)
corrector.replace(task_arguments(task_method), new_task_dependency)
else
task_name = task_method.first_argument
new_task_dependency = correct_task_dependency(task_name)
corrector.replace(task_name, new_task_dependency)
end
end
end
end
private
def correct_task_arguments_dependency(task_method)
"#{task_arguments(task_method).source} => :environment"
end
def correct_task_dependency(task_name)
if task_name.sym_type?
"#{task_name.source.delete(':|\'|"')}: :environment"
else
"#{task_name.source} => :environment"
end
end
def task_name(node)
first_arg = node.first_argument
case first_arg&.type
when :sym, :str
first_arg.value.to_sym
when :hash
return nil if first_arg.children.size != 1
pair = first_arg.children.first
key = pair.children.first
case key.type
when :sym, :str
key.value.to_sym
end
end
end
def task_arguments(node)
node.arguments[1]
end
def with_arguments?(node)
node.arguments.size > 1 && node.arguments[1].array_type?
end
def with_dependencies?(node)
first_arg = node.first_argument
return false unless first_arg
if first_arg.hash_type?
with_hash_style_dependencies?(first_arg)
else
task_args = node.arguments[1]
return false unless task_args
return false unless task_args.hash_type?
with_hash_style_dependencies?(task_args)
end
end
def with_hash_style_dependencies?(hash_node)
deps = hash_node.pairs.first&.value
return false unless deps
case deps.type
when :array
!deps.values.empty?
else
true
end
end
end
end
end
end