rubocop 0.70.0 → 0.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -10
- data/config/default.yml +50 -491
- data/lib/rubocop.rb +5 -53
- data/lib/rubocop/ast/builder.rb +2 -0
- data/lib/rubocop/ast/node.rb +1 -1
- data/lib/rubocop/ast/node/float_node.rb +12 -0
- data/lib/rubocop/ast/node/int_node.rb +12 -0
- data/lib/rubocop/ast/node/mixin/numeric_node.rb +21 -0
- data/lib/rubocop/ast/node/resbody_node.rb +1 -6
- data/lib/rubocop/cached_data.rb +1 -1
- data/lib/rubocop/config.rb +35 -6
- data/lib/rubocop/config_loader.rb +2 -2
- data/lib/rubocop/config_loader_resolver.rb +0 -6
- data/lib/rubocop/cop/cop.rb +0 -4
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +55 -0
- data/lib/rubocop/cop/layout/class_structure.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +3 -1
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +4 -0
- data/lib/rubocop/cop/layout/indent_first_argument.rb +6 -2
- data/lib/rubocop/cop/layout/indent_first_parameter.rb +7 -3
- data/lib/rubocop/cop/layout/indent_heredoc.rb +0 -1
- data/lib/rubocop/cop/layout/indentation_consistency.rb +13 -12
- data/lib/rubocop/cop/layout/indentation_width.rb +8 -4
- data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +2 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_alignment.rb +4 -0
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +20 -22
- data/lib/rubocop/cop/style/commented_keyword.rb +1 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +2 -1
- data/lib/rubocop/cop/style/float_division.rb +94 -0
- data/lib/rubocop/cop/style/format_string.rb +7 -3
- data/lib/rubocop/cop/style/if_inside_else.rb +42 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +1 -1
- data/lib/rubocop/cop/style/ternary_parentheses.rb +12 -2
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -0
- data/lib/rubocop/cop/style/word_array.rb +2 -2
- data/lib/rubocop/cop/style/zero_length_predicate.rb +1 -1
- data/lib/rubocop/node_pattern.rb +84 -5
- data/lib/rubocop/options.rb +0 -2
- data/lib/rubocop/processed_source.rb +5 -1
- data/lib/rubocop/rspec/cop_helper.rb +0 -1
- data/lib/rubocop/rspec/shared_contexts.rb +0 -17
- data/lib/rubocop/rspec/support.rb +0 -1
- data/lib/rubocop/runner.rb +6 -7
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop/yaml_duplication_checker.rb +8 -2
- metadata +7 -69
- data/lib/rubocop/cop/mixin/target_rails_version.rb +0 -16
- data/lib/rubocop/cop/rails/action_filter.rb +0 -117
- data/lib/rubocop/cop/rails/active_record_aliases.rb +0 -48
- data/lib/rubocop/cop/rails/active_record_override.rb +0 -82
- data/lib/rubocop/cop/rails/active_support_aliases.rb +0 -69
- data/lib/rubocop/cop/rails/application_job.rb +0 -40
- data/lib/rubocop/cop/rails/application_record.rb +0 -40
- data/lib/rubocop/cop/rails/assert_not.rb +0 -44
- data/lib/rubocop/cop/rails/belongs_to.rb +0 -102
- data/lib/rubocop/cop/rails/blank.rb +0 -164
- data/lib/rubocop/cop/rails/bulk_change_table.rb +0 -289
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +0 -91
- data/lib/rubocop/cop/rails/date.rb +0 -161
- data/lib/rubocop/cop/rails/delegate.rb +0 -132
- data/lib/rubocop/cop/rails/delegate_allow_blank.rb +0 -37
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +0 -91
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +0 -45
- data/lib/rubocop/cop/rails/environment_comparison.rb +0 -68
- data/lib/rubocop/cop/rails/exit.rb +0 -67
- data/lib/rubocop/cop/rails/file_path.rb +0 -108
- data/lib/rubocop/cop/rails/find_by.rb +0 -55
- data/lib/rubocop/cop/rails/find_each.rb +0 -51
- data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +0 -25
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +0 -106
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +0 -117
- data/lib/rubocop/cop/rails/http_status.rb +0 -179
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +0 -94
- data/lib/rubocop/cop/rails/inverse_of.rb +0 -246
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +0 -175
- data/lib/rubocop/cop/rails/link_to_blank.rb +0 -98
- data/lib/rubocop/cop/rails/not_null_column.rb +0 -67
- data/lib/rubocop/cop/rails/output.rb +0 -49
- data/lib/rubocop/cop/rails/output_safety.rb +0 -99
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +0 -107
- data/lib/rubocop/cop/rails/presence.rb +0 -124
- data/lib/rubocop/cop/rails/present.rb +0 -153
- data/lib/rubocop/cop/rails/read_write_attribute.rb +0 -74
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +0 -111
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +0 -136
- data/lib/rubocop/cop/rails/reflection_class_name.rb +0 -37
- data/lib/rubocop/cop/rails/refute_methods.rb +0 -76
- data/lib/rubocop/cop/rails/relative_date_constant.rb +0 -93
- data/lib/rubocop/cop/rails/request_referer.rb +0 -56
- data/lib/rubocop/cop/rails/reversible_migration.rb +0 -286
- data/lib/rubocop/cop/rails/safe_navigation.rb +0 -87
- data/lib/rubocop/cop/rails/save_bang.rb +0 -316
- data/lib/rubocop/cop/rails/scope_args.rb +0 -29
- data/lib/rubocop/cop/rails/skips_model_validations.rb +0 -87
- data/lib/rubocop/cop/rails/time_zone.rb +0 -238
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +0 -105
- data/lib/rubocop/cop/rails/unknown_env.rb +0 -63
- data/lib/rubocop/cop/rails/validation.rb +0 -109
- data/lib/rubocop/rspec/shared_examples.rb +0 -59
@@ -1,289 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Rails
|
6
|
-
# This Cop checks whether alter queries are combinable.
|
7
|
-
# If combinable queries are detected, it suggests to you
|
8
|
-
# to use `change_table` with `bulk: true` instead.
|
9
|
-
# This option causes the migration to generate a single
|
10
|
-
# ALTER TABLE statement combining multiple column alterations.
|
11
|
-
#
|
12
|
-
# The `bulk` option is only supported on the MySQL and
|
13
|
-
# the PostgreSQL (5.2 later) adapter; thus it will
|
14
|
-
# automatically detect an adapter from `development` environment
|
15
|
-
# in `config/database.yml` when the `Database` option is not set.
|
16
|
-
# If the adapter is not `mysql2` or `postgresql`,
|
17
|
-
# this Cop ignores offenses.
|
18
|
-
#
|
19
|
-
# @example
|
20
|
-
# # bad
|
21
|
-
# def change
|
22
|
-
# add_column :users, :name, :string, null: false
|
23
|
-
# add_column :users, :nickname, :string
|
24
|
-
#
|
25
|
-
# # ALTER TABLE `users` ADD `name` varchar(255) NOT NULL
|
26
|
-
# # ALTER TABLE `users` ADD `nickname` varchar(255)
|
27
|
-
# end
|
28
|
-
#
|
29
|
-
# # good
|
30
|
-
# def change
|
31
|
-
# change_table :users, bulk: true do |t|
|
32
|
-
# t.string :name, null: false
|
33
|
-
# t.string :nickname
|
34
|
-
# end
|
35
|
-
#
|
36
|
-
# # ALTER TABLE `users` ADD `name` varchar(255) NOT NULL,
|
37
|
-
# # ADD `nickname` varchar(255)
|
38
|
-
# end
|
39
|
-
#
|
40
|
-
# @example
|
41
|
-
# # bad
|
42
|
-
# def change
|
43
|
-
# change_table :users do |t|
|
44
|
-
# t.string :name, null: false
|
45
|
-
# t.string :nickname
|
46
|
-
# end
|
47
|
-
# end
|
48
|
-
#
|
49
|
-
# # good
|
50
|
-
# def change
|
51
|
-
# change_table :users, bulk: true do |t|
|
52
|
-
# t.string :name, null: false
|
53
|
-
# t.string :nickname
|
54
|
-
# end
|
55
|
-
# end
|
56
|
-
#
|
57
|
-
# # good
|
58
|
-
# # When you don't want to combine alter queries.
|
59
|
-
# def change
|
60
|
-
# change_table :users, bulk: false do |t|
|
61
|
-
# t.string :name, null: false
|
62
|
-
# t.string :nickname
|
63
|
-
# end
|
64
|
-
# end
|
65
|
-
#
|
66
|
-
# @see https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_table
|
67
|
-
# @see https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html
|
68
|
-
class BulkChangeTable < Cop
|
69
|
-
MSG_FOR_CHANGE_TABLE = <<~MSG.chomp
|
70
|
-
You can combine alter queries using `bulk: true` options.
|
71
|
-
MSG
|
72
|
-
MSG_FOR_ALTER_METHODS = <<~MSG.chomp
|
73
|
-
You can use `change_table :%<table>s, bulk: true` to combine alter queries.
|
74
|
-
MSG
|
75
|
-
|
76
|
-
MYSQL = 'mysql'
|
77
|
-
POSTGRESQL = 'postgresql'
|
78
|
-
|
79
|
-
MIGRATION_METHODS = %i[change up down].freeze
|
80
|
-
|
81
|
-
COMBINABLE_TRANSFORMATIONS = %i[
|
82
|
-
primary_key
|
83
|
-
column
|
84
|
-
string
|
85
|
-
text
|
86
|
-
integer
|
87
|
-
bigint
|
88
|
-
float
|
89
|
-
decimal
|
90
|
-
numeric
|
91
|
-
datetime
|
92
|
-
timestamp
|
93
|
-
time
|
94
|
-
date
|
95
|
-
binary
|
96
|
-
boolean
|
97
|
-
json
|
98
|
-
virtual
|
99
|
-
remove
|
100
|
-
change
|
101
|
-
timestamps
|
102
|
-
remove_timestamps
|
103
|
-
].freeze
|
104
|
-
|
105
|
-
COMBINABLE_ALTER_METHODS = %i[
|
106
|
-
add_column
|
107
|
-
remove_column
|
108
|
-
remove_columns
|
109
|
-
change_column
|
110
|
-
add_timestamps
|
111
|
-
remove_timestamps
|
112
|
-
].freeze
|
113
|
-
|
114
|
-
MYSQL_COMBINABLE_TRANSFORMATIONS = %i[
|
115
|
-
rename
|
116
|
-
index
|
117
|
-
remove_index
|
118
|
-
].freeze
|
119
|
-
|
120
|
-
MYSQL_COMBINABLE_ALTER_METHODS = %i[
|
121
|
-
rename_column
|
122
|
-
add_index
|
123
|
-
remove_index
|
124
|
-
].freeze
|
125
|
-
|
126
|
-
POSTGRESQL_COMBINABLE_TRANSFORMATIONS = %i[
|
127
|
-
change_default
|
128
|
-
].freeze
|
129
|
-
|
130
|
-
POSTGRESQL_COMBINABLE_ALTER_METHODS = %i[
|
131
|
-
change_column_default
|
132
|
-
change_column_null
|
133
|
-
].freeze
|
134
|
-
|
135
|
-
def on_def(node)
|
136
|
-
return unless support_bulk_alter?
|
137
|
-
return unless MIGRATION_METHODS.include?(node.method_name)
|
138
|
-
return unless node.body
|
139
|
-
|
140
|
-
recorder = AlterMethodsRecorder.new
|
141
|
-
|
142
|
-
node.body.each_child_node(:send) do |send_node|
|
143
|
-
if combinable_alter_methods.include?(send_node.method_name)
|
144
|
-
recorder.process(send_node)
|
145
|
-
else
|
146
|
-
recorder.flush
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
recorder.offensive_nodes.each { |n| add_offense_for_alter_methods(n) }
|
151
|
-
end
|
152
|
-
|
153
|
-
def on_send(node)
|
154
|
-
return unless support_bulk_alter?
|
155
|
-
return unless node.command?(:change_table)
|
156
|
-
return if include_bulk_options?(node)
|
157
|
-
return unless node.block_node
|
158
|
-
|
159
|
-
send_nodes = node.block_node.body.each_child_node(:send).to_a
|
160
|
-
|
161
|
-
transformations = send_nodes.select do |send_node|
|
162
|
-
combinable_transformations.include?(send_node.method_name)
|
163
|
-
end
|
164
|
-
|
165
|
-
add_offense_for_change_table(node) if transformations.size > 1
|
166
|
-
end
|
167
|
-
|
168
|
-
private
|
169
|
-
|
170
|
-
# @param node [RuboCop::AST::SendNode] (send nil? :change_table ...)
|
171
|
-
def include_bulk_options?(node)
|
172
|
-
# arguments: [{(sym :table)(str "table")} (hash (pair (sym :bulk) _))]
|
173
|
-
options = node.arguments[1]
|
174
|
-
return false unless options
|
175
|
-
|
176
|
-
options.hash_type? &&
|
177
|
-
options.keys.any? { |key| key.sym_type? && key.value == :bulk }
|
178
|
-
end
|
179
|
-
|
180
|
-
def database
|
181
|
-
cop_config['Database'] || database_from_yaml
|
182
|
-
end
|
183
|
-
|
184
|
-
def database_from_yaml
|
185
|
-
return nil unless database_yaml
|
186
|
-
|
187
|
-
case database_yaml['adapter']
|
188
|
-
when 'mysql2'
|
189
|
-
MYSQL
|
190
|
-
when 'postgresql'
|
191
|
-
POSTGRESQL
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
def database_yaml
|
196
|
-
return nil unless File.exist?('config/database.yml')
|
197
|
-
|
198
|
-
yaml = YAML.load_file('config/database.yml')
|
199
|
-
return nil unless yaml.is_a? Hash
|
200
|
-
|
201
|
-
config = yaml['development']
|
202
|
-
return nil unless config.is_a?(Hash)
|
203
|
-
|
204
|
-
config
|
205
|
-
rescue Psych::SyntaxError
|
206
|
-
nil
|
207
|
-
end
|
208
|
-
|
209
|
-
def support_bulk_alter?
|
210
|
-
case database
|
211
|
-
when MYSQL
|
212
|
-
true
|
213
|
-
when POSTGRESQL
|
214
|
-
# Add bulk alter support for PostgreSQL in 5.2.0
|
215
|
-
# @see https://github.com/rails/rails/pull/31331
|
216
|
-
target_rails_version >= 5.2
|
217
|
-
else
|
218
|
-
false
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
def combinable_alter_methods
|
223
|
-
case database
|
224
|
-
when MYSQL
|
225
|
-
COMBINABLE_ALTER_METHODS + MYSQL_COMBINABLE_ALTER_METHODS
|
226
|
-
when POSTGRESQL
|
227
|
-
COMBINABLE_ALTER_METHODS + POSTGRESQL_COMBINABLE_ALTER_METHODS
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
def combinable_transformations
|
232
|
-
case database
|
233
|
-
when MYSQL
|
234
|
-
COMBINABLE_TRANSFORMATIONS + MYSQL_COMBINABLE_TRANSFORMATIONS
|
235
|
-
when POSTGRESQL
|
236
|
-
COMBINABLE_TRANSFORMATIONS + POSTGRESQL_COMBINABLE_TRANSFORMATIONS
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
# @param node [RuboCop::AST::SendNode]
|
241
|
-
def add_offense_for_alter_methods(node)
|
242
|
-
# arguments: [{(sym :table)(str "table")} ...]
|
243
|
-
table_node = node.arguments[0]
|
244
|
-
return unless table_node.is_a? RuboCop::AST::BasicLiteralNode
|
245
|
-
|
246
|
-
message = format(MSG_FOR_ALTER_METHODS, table: table_node.value)
|
247
|
-
add_offense(node, message: message)
|
248
|
-
end
|
249
|
-
|
250
|
-
# @param node [RuboCop::AST::SendNode]
|
251
|
-
def add_offense_for_change_table(node)
|
252
|
-
add_offense(node, message: MSG_FOR_CHANGE_TABLE)
|
253
|
-
end
|
254
|
-
|
255
|
-
# Record combinable alter methods and register offensive nodes.
|
256
|
-
class AlterMethodsRecorder
|
257
|
-
def initialize
|
258
|
-
@nodes = []
|
259
|
-
@offensive_nodes = []
|
260
|
-
end
|
261
|
-
|
262
|
-
# @param new_node [RuboCop::AST::SendNode]
|
263
|
-
def process(new_node)
|
264
|
-
# arguments: [{(sym :table)(str "table")} ...]
|
265
|
-
table_node = new_node.arguments[0]
|
266
|
-
if table_node.is_a? RuboCop::AST::BasicLiteralNode
|
267
|
-
flush unless @nodes.all? do |node|
|
268
|
-
node.arguments[0].value.to_s == table_node.value.to_s
|
269
|
-
end
|
270
|
-
@nodes << new_node
|
271
|
-
else
|
272
|
-
flush
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
def flush
|
277
|
-
@offensive_nodes << @nodes.first if @nodes.size > 1
|
278
|
-
@nodes = []
|
279
|
-
end
|
280
|
-
|
281
|
-
def offensive_nodes
|
282
|
-
flush
|
283
|
-
@offensive_nodes
|
284
|
-
end
|
285
|
-
end
|
286
|
-
end
|
287
|
-
end
|
288
|
-
end
|
289
|
-
end
|
@@ -1,91 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Rails
|
6
|
-
# This cop checks the migration for which timestamps are not included
|
7
|
-
# when creating a new table.
|
8
|
-
# In many cases, timestamps are useful information and should be added.
|
9
|
-
#
|
10
|
-
# @example
|
11
|
-
# # bad
|
12
|
-
# create_table :users
|
13
|
-
#
|
14
|
-
# # bad
|
15
|
-
# create_table :users do |t|
|
16
|
-
# t.string :name
|
17
|
-
# t.string :email
|
18
|
-
# end
|
19
|
-
#
|
20
|
-
# # good
|
21
|
-
# create_table :users do |t|
|
22
|
-
# t.string :name
|
23
|
-
# t.string :email
|
24
|
-
#
|
25
|
-
# t.timestamps
|
26
|
-
# end
|
27
|
-
#
|
28
|
-
# # good
|
29
|
-
# create_table :users do |t|
|
30
|
-
# t.string :name
|
31
|
-
# t.string :email
|
32
|
-
#
|
33
|
-
# t.datetime :created_at, default: -> { 'CURRENT_TIMESTAMP' }
|
34
|
-
# end
|
35
|
-
#
|
36
|
-
# # good
|
37
|
-
# create_table :users do |t|
|
38
|
-
# t.string :name
|
39
|
-
# t.string :email
|
40
|
-
#
|
41
|
-
# t.datetime :updated_at, default: -> { 'CURRENT_TIMESTAMP' }
|
42
|
-
# end
|
43
|
-
class CreateTableWithTimestamps < Cop
|
44
|
-
MSG = 'Add timestamps when creating a new table.'
|
45
|
-
|
46
|
-
def_node_matcher :create_table_with_block?, <<-PATTERN
|
47
|
-
(block
|
48
|
-
(send nil? :create_table ...)
|
49
|
-
(args (arg _var))
|
50
|
-
_)
|
51
|
-
PATTERN
|
52
|
-
|
53
|
-
def_node_matcher :create_table_with_timestamps_proc?, <<-PATTERN
|
54
|
-
(send nil? :create_table (sym _) ... (block-pass (sym :timestamps)))
|
55
|
-
PATTERN
|
56
|
-
|
57
|
-
def_node_search :timestamps_included?, <<-PATTERN
|
58
|
-
(send _var :timestamps ...)
|
59
|
-
PATTERN
|
60
|
-
|
61
|
-
def_node_search :created_at_or_updated_at_included?, <<-PATTERN
|
62
|
-
(send _var :datetime
|
63
|
-
{(sym {:created_at :updated_at})(str {"created_at" "updated_at"})}
|
64
|
-
...)
|
65
|
-
PATTERN
|
66
|
-
|
67
|
-
def on_send(node)
|
68
|
-
return unless node.command?(:create_table)
|
69
|
-
|
70
|
-
parent = node.parent
|
71
|
-
|
72
|
-
if create_table_with_block?(parent)
|
73
|
-
if parent.body.nil? || !time_columns_included?(parent.body)
|
74
|
-
add_offense(parent)
|
75
|
-
end
|
76
|
-
elsif create_table_with_timestamps_proc?(node)
|
77
|
-
# nothing to do
|
78
|
-
else
|
79
|
-
add_offense(node)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
private
|
84
|
-
|
85
|
-
def time_columns_included?(node)
|
86
|
-
timestamps_included?(node) || created_at_or_updated_at_included?(node)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
@@ -1,161 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Rails
|
6
|
-
# This cop checks for the correct use of Date methods,
|
7
|
-
# such as Date.today, Date.current etc.
|
8
|
-
#
|
9
|
-
# Using `Date.today` is dangerous, because it doesn't know anything about
|
10
|
-
# Rails time zone. You must use `Time.zone.today` instead.
|
11
|
-
#
|
12
|
-
# The cop also reports warnings when you are using `to_time` method,
|
13
|
-
# because it doesn't know about Rails time zone either.
|
14
|
-
#
|
15
|
-
# Two styles are supported for this cop. When EnforcedStyle is 'strict'
|
16
|
-
# then the Date methods `today`, `current`, `yesterday`, and `tomorrow`
|
17
|
-
# are prohibited and the usage of both `to_time`
|
18
|
-
# and 'to_time_in_current_zone' are reported as warning.
|
19
|
-
#
|
20
|
-
# When EnforcedStyle is 'flexible' then only `Date.today` is prohibited
|
21
|
-
# and only `to_time` is reported as warning.
|
22
|
-
#
|
23
|
-
# @example EnforcedStyle: strict
|
24
|
-
# # bad
|
25
|
-
# Date.current
|
26
|
-
# Date.yesterday
|
27
|
-
# Date.today
|
28
|
-
# date.to_time
|
29
|
-
#
|
30
|
-
# # good
|
31
|
-
# Time.zone.today
|
32
|
-
# Time.zone.today - 1.day
|
33
|
-
#
|
34
|
-
# @example EnforcedStyle: flexible (default)
|
35
|
-
# # bad
|
36
|
-
# Date.today
|
37
|
-
# date.to_time
|
38
|
-
#
|
39
|
-
# # good
|
40
|
-
# Time.zone.today
|
41
|
-
# Time.zone.today - 1.day
|
42
|
-
# Date.current
|
43
|
-
# Date.yesterday
|
44
|
-
# date.in_time_zone
|
45
|
-
#
|
46
|
-
class Date < Cop
|
47
|
-
include ConfigurableEnforcedStyle
|
48
|
-
|
49
|
-
MSG = 'Do not use `Date.%<method_called>s` without zone. Use ' \
|
50
|
-
'`Time.zone.%<day>s` instead.'
|
51
|
-
|
52
|
-
MSG_SEND = 'Do not use `%<method>s` on Date objects, because they ' \
|
53
|
-
'know nothing about the time zone in use.'
|
54
|
-
|
55
|
-
BAD_DAYS = %i[today current yesterday tomorrow].freeze
|
56
|
-
|
57
|
-
DEPRECATED_METHODS = [
|
58
|
-
{ deprecated: 'to_time_in_current_zone', relevant: 'in_time_zone' }
|
59
|
-
].freeze
|
60
|
-
|
61
|
-
DEPRECATED_MSG = '`%<deprecated>s` is deprecated. ' \
|
62
|
-
'Use `%<relevant>s` instead.'
|
63
|
-
|
64
|
-
def on_const(node)
|
65
|
-
mod, klass = *node.children
|
66
|
-
# we should only check core Date class (`Date` or `::Date`)
|
67
|
-
return unless (mod.nil? || mod.cbase_type?) && method_send?(node)
|
68
|
-
|
69
|
-
check_date_node(node.parent) if klass == :Date
|
70
|
-
end
|
71
|
-
|
72
|
-
def on_send(node)
|
73
|
-
return unless node.receiver && bad_methods.include?(node.method_name)
|
74
|
-
|
75
|
-
return if safe_chain?(node) || safe_to_time?(node)
|
76
|
-
|
77
|
-
check_deprecated_methods(node)
|
78
|
-
|
79
|
-
add_offense(node, location: :selector,
|
80
|
-
message: format(MSG_SEND, method: node.method_name))
|
81
|
-
end
|
82
|
-
alias on_csend on_send
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
|
-
def check_deprecated_methods(node)
|
87
|
-
DEPRECATED_METHODS.each do |relevant:, deprecated:|
|
88
|
-
next unless node.method_name == deprecated.to_sym
|
89
|
-
|
90
|
-
add_offense(node, location: :selector,
|
91
|
-
message: format(DEPRECATED_MSG,
|
92
|
-
deprecated: deprecated,
|
93
|
-
relevant: relevant))
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def check_date_node(node)
|
98
|
-
chain = extract_method_chain(node)
|
99
|
-
|
100
|
-
return if (chain & bad_days).empty?
|
101
|
-
|
102
|
-
method_name = (chain & bad_days).join('.')
|
103
|
-
|
104
|
-
day = method_name
|
105
|
-
day = 'today' if method_name == 'current'
|
106
|
-
|
107
|
-
add_offense(node, location: :selector,
|
108
|
-
message: format(MSG,
|
109
|
-
method_called: method_name,
|
110
|
-
day: day))
|
111
|
-
end
|
112
|
-
|
113
|
-
def extract_method_chain(node)
|
114
|
-
[node, *node.each_ancestor(:send)].map(&:method_name)
|
115
|
-
end
|
116
|
-
|
117
|
-
# checks that parent node of send_type
|
118
|
-
# and receiver is the given node
|
119
|
-
def method_send?(node)
|
120
|
-
return false unless node.parent&.send_type?
|
121
|
-
|
122
|
-
node.parent.receiver == node
|
123
|
-
end
|
124
|
-
|
125
|
-
def safe_chain?(node)
|
126
|
-
chain = extract_method_chain(node)
|
127
|
-
|
128
|
-
(chain & bad_methods).empty? || !(chain & good_methods).empty?
|
129
|
-
end
|
130
|
-
|
131
|
-
def safe_to_time?(node)
|
132
|
-
return unless node.method?(:to_time)
|
133
|
-
|
134
|
-
if node.receiver.str_type?
|
135
|
-
zone_regexp = /([+-][\d:]+|\dZ)\z/
|
136
|
-
|
137
|
-
node.receiver.str_content.match(zone_regexp)
|
138
|
-
else
|
139
|
-
node.arguments.one?
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
def good_days
|
144
|
-
style == :strict ? [] : %i[current yesterday tomorrow]
|
145
|
-
end
|
146
|
-
|
147
|
-
def bad_days
|
148
|
-
BAD_DAYS - good_days
|
149
|
-
end
|
150
|
-
|
151
|
-
def bad_methods
|
152
|
-
%i[to_time to_time_in_current_zone]
|
153
|
-
end
|
154
|
-
|
155
|
-
def good_methods
|
156
|
-
style == :strict ? [] : TimeZone::ACCEPTED_METHODS
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|