rubocop-rails 2.0.1 → 2.19.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +52 -5
- data/config/default.yml +726 -32
- data/config/obsoletion.yml +17 -0
- data/lib/rubocop/cop/mixin/active_record_helper.rb +106 -0
- data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +32 -0
- data/lib/rubocop/cop/mixin/class_send_node_helper.rb +20 -0
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +40 -0
- data/lib/rubocop/cop/mixin/index_method.rb +165 -0
- data/lib/rubocop/cop/mixin/migrations_helper.rb +26 -0
- data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +112 -0
- data/lib/rubocop/cop/rails/action_controller_test_case.rb +47 -0
- data/lib/rubocop/cop/rails/action_filter.rb +11 -21
- data/lib/rubocop/cop/rails/action_order.rb +116 -0
- data/lib/rubocop/cop/rails/active_record_aliases.rb +23 -24
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +143 -0
- data/lib/rubocop/cop/rails/active_record_override.rb +3 -6
- data/lib/rubocop/cop/rails/active_support_aliases.rb +13 -22
- data/lib/rubocop/cop/rails/active_support_on_load.rb +70 -0
- data/lib/rubocop/cop/rails/add_column_index.rb +61 -0
- data/lib/rubocop/cop/rails/after_commit_override.rb +81 -0
- data/lib/rubocop/cop/rails/application_controller.rb +36 -0
- data/lib/rubocop/cop/rails/application_job.rb +9 -4
- data/lib/rubocop/cop/rails/application_mailer.rb +39 -0
- data/lib/rubocop/cop/rails/application_record.rb +9 -9
- data/lib/rubocop/cop/rails/arel_star.rb +47 -0
- data/lib/rubocop/cop/rails/assert_not.rb +8 -10
- data/lib/rubocop/cop/rails/attribute_default_block_value.rb +90 -0
- data/lib/rubocop/cop/rails/belongs_to.rb +12 -24
- data/lib/rubocop/cop/rails/blank.rb +40 -36
- data/lib/rubocop/cop/rails/bulk_change_table.rb +40 -35
- data/lib/rubocop/cop/rails/compact_blank.rb +111 -0
- data/lib/rubocop/cop/rails/content_tag.rb +93 -0
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +22 -15
- data/lib/rubocop/cop/rails/date.rb +41 -36
- data/lib/rubocop/cop/rails/default_scope.rb +61 -0
- data/lib/rubocop/cop/rails/delegate.rb +33 -29
- data/lib/rubocop/cop/rails/delegate_allow_blank.rb +9 -10
- data/lib/rubocop/cop/rails/deprecated_active_model_errors_methods.rb +168 -0
- data/lib/rubocop/cop/rails/dot_separated_keys.rb +71 -0
- data/lib/rubocop/cop/rails/duplicate_association.rb +56 -0
- data/lib/rubocop/cop/rails/duplicate_scope.rb +46 -0
- data/lib/rubocop/cop/rails/duration_arithmetic.rb +98 -0
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +76 -31
- data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +82 -0
- data/lib/rubocop/cop/rails/enum_hash.rb +75 -0
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +30 -12
- data/lib/rubocop/cop/rails/environment_comparison.rb +70 -22
- data/lib/rubocop/cop/rails/environment_variable_access.rb +67 -0
- data/lib/rubocop/cop/rails/exit.rb +7 -13
- data/lib/rubocop/cop/rails/expanded_date_range.rb +102 -0
- data/lib/rubocop/cop/rails/file_path.rb +48 -31
- data/lib/rubocop/cop/rails/find_by.rb +43 -24
- data/lib/rubocop/cop/rails/find_by_id.rb +94 -0
- data/lib/rubocop/cop/rails/find_each.rb +42 -18
- data/lib/rubocop/cop/rails/freeze_time.rb +79 -0
- data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +4 -3
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +62 -25
- data/lib/rubocop/cop/rails/helper_instance_variable.rb +32 -4
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +61 -32
- data/lib/rubocop/cop/rails/http_status.rb +27 -23
- data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +96 -0
- data/lib/rubocop/cop/rails/i18n_locale_assignment.rb +37 -0
- data/lib/rubocop/cop/rails/i18n_locale_texts.rb +110 -0
- data/lib/rubocop/cop/rails/ignored_columns_assignment.rb +50 -0
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +9 -16
- data/lib/rubocop/cop/rails/index_by.rb +65 -0
- data/lib/rubocop/cop/rails/index_with.rb +68 -0
- data/lib/rubocop/cop/rails/inquiry.rb +39 -0
- data/lib/rubocop/cop/rails/inverse_of.rb +33 -27
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +62 -32
- data/lib/rubocop/cop/rails/link_to_blank.rb +31 -32
- data/lib/rubocop/cop/rails/mailer_name.rb +90 -0
- data/lib/rubocop/cop/rails/match_route.rb +120 -0
- data/lib/rubocop/cop/rails/migration_class_name.rb +63 -0
- data/lib/rubocop/cop/rails/negate_include.rb +42 -0
- data/lib/rubocop/cop/rails/not_null_column.rb +16 -12
- data/lib/rubocop/cop/rails/order_by_id.rb +51 -0
- data/lib/rubocop/cop/rails/output.rb +29 -10
- data/lib/rubocop/cop/rails/output_safety.rb +9 -4
- data/lib/rubocop/cop/rails/pick.rb +64 -0
- data/lib/rubocop/cop/rails/pluck.rb +96 -0
- data/lib/rubocop/cop/rails/pluck_id.rb +59 -0
- data/lib/rubocop/cop/rails/pluck_in_where.rb +71 -0
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +14 -19
- data/lib/rubocop/cop/rails/presence.rb +54 -26
- data/lib/rubocop/cop/rails/present.rb +40 -37
- data/lib/rubocop/cop/rails/rake_environment.rb +112 -0
- data/lib/rubocop/cop/rails/read_write_attribute.rb +56 -18
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +33 -45
- data/lib/rubocop/cop/rails/redundant_foreign_key.rb +77 -0
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +257 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +34 -32
- data/lib/rubocop/cop/rails/redundant_travel_back.rb +57 -0
- data/lib/rubocop/cop/rails/reflection_class_name.rb +56 -7
- data/lib/rubocop/cop/rails/refute_methods.rb +56 -35
- data/lib/rubocop/cop/rails/relative_date_constant.rb +52 -33
- data/lib/rubocop/cop/rails/render_inline.rb +41 -0
- data/lib/rubocop/cop/rails/render_plain_text.rb +71 -0
- data/lib/rubocop/cop/rails/request_referer.rb +10 -11
- data/lib/rubocop/cop/rails/require_dependency.rb +38 -0
- data/lib/rubocop/cop/rails/response_parsed_body.rb +57 -0
- data/lib/rubocop/cop/rails/reversible_migration.rb +122 -82
- data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +66 -0
- data/lib/rubocop/cop/rails/root_join_chain.rb +72 -0
- data/lib/rubocop/cop/rails/root_pathname_methods.rb +238 -0
- data/lib/rubocop/cop/rails/root_public_path.rb +59 -0
- data/lib/rubocop/cop/rails/safe_navigation.rb +55 -43
- data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +50 -0
- data/lib/rubocop/cop/rails/save_bang.rb +89 -63
- data/lib/rubocop/cop/rails/schema_comment.rb +104 -0
- data/lib/rubocop/cop/rails/scope_args.rb +8 -3
- data/lib/rubocop/cop/rails/short_i18n.rb +71 -0
- data/lib/rubocop/cop/rails/skips_model_validations.rb +53 -16
- data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +87 -0
- data/lib/rubocop/cop/rails/strip_heredoc.rb +56 -0
- data/lib/rubocop/cop/rails/table_name_assignment.rb +44 -0
- data/lib/rubocop/cop/rails/three_state_boolean_column.rb +73 -0
- data/lib/rubocop/cop/rails/time_zone.rb +83 -67
- data/lib/rubocop/cop/rails/time_zone_assignment.rb +37 -0
- data/lib/rubocop/cop/rails/to_formatted_s.rb +46 -0
- data/lib/rubocop/cop/rails/to_s_with_argument.rb +78 -0
- data/lib/rubocop/cop/rails/top_level_hash_with_indifferent_access.rb +49 -0
- data/lib/rubocop/cop/rails/transaction_exit_statement.rb +99 -0
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +40 -49
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +172 -0
- data/lib/rubocop/cop/rails/unknown_env.rb +52 -21
- data/lib/rubocop/cop/rails/unused_ignored_columns.rb +76 -0
- data/lib/rubocop/cop/rails/validation.rb +54 -23
- data/lib/rubocop/cop/rails/where_equals.rb +102 -0
- data/lib/rubocop/cop/rails/where_exists.rb +138 -0
- data/lib/rubocop/cop/rails/where_missing.rb +118 -0
- data/lib/rubocop/cop/rails/where_not.rb +101 -0
- data/lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb +55 -0
- data/lib/rubocop/cop/rails_cops.rb +78 -8
- data/lib/rubocop/rails/inject.rb +1 -1
- data/lib/rubocop/rails/schema_loader/schema.rb +191 -0
- data/lib/rubocop/rails/schema_loader.rb +61 -0
- data/lib/rubocop/rails/version.rb +5 -1
- data/lib/rubocop/rails.rb +3 -1
- data/lib/rubocop-rails.rb +22 -0
- metadata +120 -19
- data/bin/setup +0 -7
@@ -3,104 +3,104 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks for the use of Time methods without zone.
|
7
7
|
#
|
8
|
-
# Built on top of Ruby on Rails style guide (https://
|
8
|
+
# Built on top of Ruby on Rails style guide (https://rails.rubystyle.guide#time)
|
9
9
|
# and the article http://danilenko.org/2012/7/6/rails_timezones/
|
10
10
|
#
|
11
|
-
# Two styles are supported for this cop. When EnforcedStyle is 'strict'
|
12
|
-
# then only use of Time.zone is allowed.
|
11
|
+
# Two styles are supported for this cop. When `EnforcedStyle` is 'strict'
|
12
|
+
# then only use of `Time.zone` is allowed.
|
13
13
|
#
|
14
14
|
# When EnforcedStyle is 'flexible' then it's also allowed
|
15
|
-
# to use Time
|
15
|
+
# to use `Time#in_time_zone`.
|
16
16
|
#
|
17
|
-
# @
|
18
|
-
#
|
17
|
+
# @safety
|
18
|
+
# This cop's autocorrection is unsafe because it may change handling time.
|
19
19
|
#
|
20
|
+
# @example
|
20
21
|
# # bad
|
21
22
|
# Time.now
|
22
|
-
# Time.parse('2015-03-
|
23
|
-
#
|
24
|
-
# # bad
|
25
|
-
# Time.current
|
26
|
-
# Time.at(timestamp).in_time_zone
|
23
|
+
# Time.parse('2015-03-02T19:05:37')
|
27
24
|
#
|
28
25
|
# # good
|
26
|
+
# Time.current
|
29
27
|
# Time.zone.now
|
30
|
-
# Time.zone.parse('2015-03-
|
28
|
+
# Time.zone.parse('2015-03-02T19:05:37')
|
29
|
+
# Time.zone.parse('2015-03-02T19:05:37Z') # Respect ISO 8601 format with timezone specifier.
|
31
30
|
#
|
32
31
|
# @example EnforcedStyle: flexible (default)
|
33
32
|
# # `flexible` allows usage of `in_time_zone` instead of `zone`.
|
34
33
|
#
|
35
|
-
# # bad
|
36
|
-
# Time.now
|
37
|
-
# Time.parse('2015-03-02 19:05:37')
|
38
|
-
#
|
39
34
|
# # good
|
40
|
-
# Time.
|
41
|
-
# Time.zone.parse('2015-03-02 19:05:37')
|
35
|
+
# Time.at(timestamp).in_time_zone
|
42
36
|
#
|
43
|
-
#
|
44
|
-
# Time
|
37
|
+
# @example EnforcedStyle: strict
|
38
|
+
# # `strict` means that `Time` should be used with `zone`.
|
39
|
+
#
|
40
|
+
# # bad
|
45
41
|
# Time.at(timestamp).in_time_zone
|
46
|
-
class TimeZone <
|
42
|
+
class TimeZone < Base
|
47
43
|
include ConfigurableEnforcedStyle
|
44
|
+
extend AutoCorrector
|
48
45
|
|
49
|
-
MSG = 'Do not use `%<current>s` without zone. Use `%<prefer>s` '
|
50
|
-
'instead.'
|
46
|
+
MSG = 'Do not use `%<current>s` without zone. Use `%<prefer>s` instead.'
|
51
47
|
|
52
|
-
MSG_ACCEPTABLE = 'Do not use `%<current>s` without zone. '
|
53
|
-
'Use one of %<prefer>s instead.'
|
48
|
+
MSG_ACCEPTABLE = 'Do not use `%<current>s` without zone. Use one of %<prefer>s instead.'
|
54
49
|
|
55
|
-
MSG_LOCALTIME = 'Do not use `Time.localtime` without '
|
56
|
-
'offset or zone.'
|
57
|
-
|
58
|
-
TIMECLASSES = %i[Time DateTime].freeze
|
50
|
+
MSG_LOCALTIME = 'Do not use `Time.localtime` without offset or zone.'
|
59
51
|
|
60
52
|
GOOD_METHODS = %i[zone zone_default find_zone find_zone!].freeze
|
61
53
|
|
62
|
-
DANGEROUS_METHODS = %i[now local new parse at
|
54
|
+
DANGEROUS_METHODS = %i[now local new parse at].freeze
|
55
|
+
|
56
|
+
ACCEPTED_METHODS = %i[in_time_zone utc getlocal xmlschema iso8601 jisx0301 rfc3339 httpdate to_i to_f].freeze
|
63
57
|
|
64
|
-
|
65
|
-
jisx0301 rfc3339 httpdate to_i to_f].freeze
|
58
|
+
TIMEZONE_SPECIFIER = /([A-z]|[+-]\d{2}:?\d{2})\z/.freeze
|
66
59
|
|
67
60
|
def on_const(node)
|
68
61
|
mod, klass = *node
|
69
62
|
# we should only check core classes
|
70
|
-
# (`
|
63
|
+
# (`Time` or `::Time`)
|
71
64
|
return unless (mod.nil? || mod.cbase_type?) && method_send?(node)
|
72
65
|
|
73
|
-
check_time_node(klass, node.parent) if
|
66
|
+
check_time_node(klass, node.parent) if klass == :Time
|
74
67
|
end
|
75
68
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
69
|
+
private
|
70
|
+
|
71
|
+
def autocorrect(corrector, node)
|
72
|
+
# add `.zone`: `Time.at` => `Time.zone.at`
|
73
|
+
corrector.insert_after(node.children[0], '.zone')
|
74
|
+
|
75
|
+
case node.method_name
|
76
|
+
when :current
|
80
77
|
# replace `Time.zone.current` => `Time.zone.now`
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
# prefer `Time` over `DateTime` class
|
85
|
-
if strict?
|
86
|
-
corrector.replace(node.children.first.source_range, 'Time')
|
87
|
-
end
|
88
|
-
remove_redundant_in_time_zone(corrector, node)
|
78
|
+
corrector.replace(node.loc.selector, 'now')
|
79
|
+
when :new
|
80
|
+
autocorrect_time_new(node, corrector)
|
89
81
|
end
|
82
|
+
|
83
|
+
# prefer `Time` over `DateTime` class
|
84
|
+
corrector.replace(node.children.first, 'Time') if strict?
|
85
|
+
remove_redundant_in_time_zone(corrector, node)
|
90
86
|
end
|
91
87
|
|
92
|
-
|
88
|
+
def autocorrect_time_new(node, corrector)
|
89
|
+
if node.arguments?
|
90
|
+
corrector.replace(node.loc.selector, 'local')
|
91
|
+
else
|
92
|
+
corrector.replace(node.loc.selector, 'now')
|
93
|
+
end
|
94
|
+
end
|
93
95
|
|
94
96
|
# remove redundant `.in_time_zone` from `Time.zone.now.in_time_zone`
|
95
97
|
def remove_redundant_in_time_zone(corrector, node)
|
96
98
|
time_methods_called = extract_method_chain(node)
|
97
|
-
return unless time_methods_called.include?(:in_time_zone) ||
|
98
|
-
time_methods_called.include?(:zone)
|
99
|
+
return unless time_methods_called.include?(:in_time_zone) || time_methods_called.include?(:zone)
|
99
100
|
|
100
101
|
while node&.send_type?
|
101
102
|
if node.children.last == :in_time_zone
|
102
|
-
in_time_zone_with_dot =
|
103
|
-
node.loc.selector.adjust(begin_pos: -1)
|
103
|
+
in_time_zone_with_dot = node.loc.selector.adjust(begin_pos: -1)
|
104
104
|
corrector.remove(in_time_zone_with_dot)
|
105
105
|
end
|
106
106
|
node = node.parent
|
@@ -108,9 +108,10 @@ module RuboCop
|
|
108
108
|
end
|
109
109
|
|
110
110
|
def check_time_node(klass, node)
|
111
|
+
return if attach_timezone_specifier?(node.first_argument)
|
112
|
+
|
111
113
|
chain = extract_method_chain(node)
|
112
114
|
return if not_danger_chain?(chain)
|
113
|
-
|
114
115
|
return check_localtime(node) if need_check_localtime?(chain)
|
115
116
|
|
116
117
|
method_name = (chain & DANGEROUS_METHODS).join('.')
|
@@ -119,7 +120,13 @@ module RuboCop
|
|
119
120
|
|
120
121
|
message = build_message(klass, method_name, node)
|
121
122
|
|
122
|
-
add_offense(node
|
123
|
+
add_offense(node.loc.selector, message: message) do |corrector|
|
124
|
+
autocorrect(corrector, node)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def attach_timezone_specifier?(date)
|
129
|
+
date.respond_to?(:value) && TIMEZONE_SPECIFIER.match?(date.value.to_s)
|
123
130
|
end
|
124
131
|
|
125
132
|
def build_message(klass, method_name, node)
|
@@ -131,9 +138,7 @@ module RuboCop
|
|
131
138
|
)
|
132
139
|
else
|
133
140
|
safe_method_name = safe_method(method_name, node)
|
134
|
-
format(MSG,
|
135
|
-
current: "#{klass}.#{method_name}",
|
136
|
-
prefer: "Time.zone.#{safe_method_name}")
|
141
|
+
format(MSG, current: "#{klass}.#{method_name}", prefer: "Time.zone.#{safe_method_name}")
|
137
142
|
end
|
138
143
|
end
|
139
144
|
|
@@ -153,7 +158,7 @@ module RuboCop
|
|
153
158
|
if (receiver.is_a? RuboCop::AST::Node) && !receiver.cbase_type?
|
154
159
|
method_from_time_class?(receiver)
|
155
160
|
else
|
156
|
-
|
161
|
+
method_name == :Time
|
157
162
|
end
|
158
163
|
end
|
159
164
|
|
@@ -177,15 +182,16 @@ module RuboCop
|
|
177
182
|
selector_node = node
|
178
183
|
|
179
184
|
while node&.send_type?
|
180
|
-
break if node.
|
185
|
+
break if node.method?(:localtime)
|
181
186
|
|
182
187
|
node = node.parent
|
183
188
|
end
|
184
189
|
|
185
190
|
return if node.arguments?
|
186
191
|
|
187
|
-
add_offense(selector_node,
|
188
|
-
|
192
|
+
add_offense(selector_node.loc.selector, message: MSG_LOCALTIME) do |corrector|
|
193
|
+
autocorrect(corrector, selector_node)
|
194
|
+
end
|
189
195
|
end
|
190
196
|
|
191
197
|
def not_danger_chain?(chain)
|
@@ -213,10 +219,7 @@ module RuboCop
|
|
213
219
|
end
|
214
220
|
|
215
221
|
def acceptable_methods(klass, method_name, node)
|
216
|
-
acceptable = [
|
217
|
-
"`Time.zone.#{safe_method(method_name, node)}`",
|
218
|
-
"`#{klass}.current`"
|
219
|
-
]
|
222
|
+
acceptable = ["`Time.zone.#{safe_method(method_name, node)}`", "`#{klass}.current`"]
|
220
223
|
|
221
224
|
ACCEPTED_METHODS.each do |am|
|
222
225
|
acceptable << "`#{klass}.#{method_name}.#{am}`"
|
@@ -225,12 +228,25 @@ module RuboCop
|
|
225
228
|
acceptable
|
226
229
|
end
|
227
230
|
|
228
|
-
# Time.new can be called with a time zone offset
|
231
|
+
# Time.new, Time.at, and Time.now can be called with a time zone offset
|
229
232
|
# When it is, that should be considered safe
|
230
233
|
# Example:
|
231
234
|
# Time.new(1988, 3, 15, 3, 0, 0, "-05:00")
|
232
235
|
def offset_provided?(node)
|
233
|
-
node.
|
236
|
+
case node.method_name
|
237
|
+
when :new
|
238
|
+
node.arguments.size == 7 || offset_option_provided?(node)
|
239
|
+
when :at, :now
|
240
|
+
offset_option_provided?(node)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def offset_option_provided?(node)
|
245
|
+
options = node.last_argument
|
246
|
+
options&.hash_type? &&
|
247
|
+
options.each_pair.any? do |pair|
|
248
|
+
pair.key.sym_type? && pair.key.value == :in && !pair.value.nil_type?
|
249
|
+
end
|
234
250
|
end
|
235
251
|
end
|
236
252
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Checks for the use of `Time.zone=` method.
|
7
|
+
#
|
8
|
+
# The `zone` attribute persists for the rest of the Ruby runtime, potentially causing
|
9
|
+
# unexpected behavior at a later time.
|
10
|
+
# Using `Time.use_zone` ensures the code passed in the block is the only place Time.zone is affected.
|
11
|
+
# It eliminates the possibility of a `zone` sticking around longer than intended.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# # bad
|
15
|
+
# Time.zone = 'EST'
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# Time.use_zone('EST') do
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
class TimeZoneAssignment < Base
|
22
|
+
MSG = 'Use `Time.use_zone` with block instead of `Time.zone=`.'
|
23
|
+
RESTRICT_ON_SEND = %i[zone=].freeze
|
24
|
+
|
25
|
+
def_node_matcher :time_zone_assignment?, <<~PATTERN
|
26
|
+
(send (const {nil? cbase} :Time) :zone= ...)
|
27
|
+
PATTERN
|
28
|
+
|
29
|
+
def on_send(node)
|
30
|
+
return unless time_zone_assignment?(node)
|
31
|
+
|
32
|
+
add_offense(node)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Checks for consistent uses of `to_fs` or `to_formatted_s`,
|
7
|
+
# depending on the cop's configuration.
|
8
|
+
#
|
9
|
+
# @example EnforcedStyle: to_fs (default)
|
10
|
+
#
|
11
|
+
# # bad
|
12
|
+
# time.to_formatted_s(:db)
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# time.to_fs(:db)
|
16
|
+
#
|
17
|
+
# @example EnforcedStyle: to_formatted_s
|
18
|
+
#
|
19
|
+
# # bad
|
20
|
+
# time.to_fs(:db)
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# time.to_formatted_s(:db)
|
24
|
+
#
|
25
|
+
class ToFormattedS < Base
|
26
|
+
include ConfigurableEnforcedStyle
|
27
|
+
extend AutoCorrector
|
28
|
+
extend TargetRailsVersion
|
29
|
+
|
30
|
+
minimum_target_rails_version 7.0
|
31
|
+
|
32
|
+
MSG = 'Use `%<prefer>s` instead.'
|
33
|
+
RESTRICT_ON_SEND = %i[to_formatted_s to_fs].freeze
|
34
|
+
|
35
|
+
def on_send(node)
|
36
|
+
return if node.method?(style)
|
37
|
+
|
38
|
+
add_offense(node.loc.selector, message: format(MSG, prefer: style)) do |corrector|
|
39
|
+
corrector.replace(node.loc.selector, style)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
alias on_csend on_send
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Identifies passing any argument to `#to_s`.
|
7
|
+
#
|
8
|
+
# @safety
|
9
|
+
# This cop is marked as unsafe because it may detect `#to_s` calls
|
10
|
+
# that are not related to Active Support implementation.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
#
|
14
|
+
# # bad
|
15
|
+
# obj.to_s(:delimited)
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# obj.to_formatted_s(:delimited)
|
19
|
+
#
|
20
|
+
class ToSWithArgument < Base
|
21
|
+
extend AutoCorrector
|
22
|
+
extend TargetRailsVersion
|
23
|
+
|
24
|
+
# These types are defined by the following files in ActiveSupport:
|
25
|
+
# lib/active_support/core_ext/array/conversions.rb
|
26
|
+
# lib/active_support/core_ext/date/conversions.rb
|
27
|
+
# lib/active_support/core_ext/date_time/conversions.rb
|
28
|
+
# lib/active_support/core_ext/numeric/conversions.rb
|
29
|
+
# lib/active_support/core_ext/range/conversions.rb
|
30
|
+
# lib/active_support/core_ext/time/conversions.rb
|
31
|
+
# lib/active_support/time_with_zone.rb
|
32
|
+
EXTENDED_FORMAT_TYPES = Set.new(
|
33
|
+
%i[
|
34
|
+
currency
|
35
|
+
db
|
36
|
+
delimited
|
37
|
+
human
|
38
|
+
human_size
|
39
|
+
inspect
|
40
|
+
iso8601
|
41
|
+
long
|
42
|
+
long_ordinal
|
43
|
+
nsec
|
44
|
+
number
|
45
|
+
percentage
|
46
|
+
phone
|
47
|
+
rfc822
|
48
|
+
rounded
|
49
|
+
short
|
50
|
+
time
|
51
|
+
usec
|
52
|
+
]
|
53
|
+
)
|
54
|
+
|
55
|
+
MSG = 'Use `to_formatted_s` instead.'
|
56
|
+
|
57
|
+
RESTRICT_ON_SEND = %i[to_s].freeze
|
58
|
+
|
59
|
+
minimum_target_rails_version 7.0
|
60
|
+
|
61
|
+
def on_send(node)
|
62
|
+
return unless rails_extended_to_s?(node)
|
63
|
+
|
64
|
+
add_offense(node.loc.selector) do |corrector|
|
65
|
+
corrector.replace(node.loc.selector, 'to_formatted_s')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
alias on_csend on_send
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def rails_extended_to_s?(node)
|
73
|
+
node.first_argument&.sym_type? && EXTENDED_FORMAT_TYPES.include?(node.first_argument.value)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Identifies top-level `HashWithIndifferentAccess`.
|
7
|
+
# This has been soft-deprecated since Rails 5.1.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# HashWithIndifferentAccess.new(foo: 'bar')
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar')
|
15
|
+
#
|
16
|
+
class TopLevelHashWithIndifferentAccess < Base
|
17
|
+
extend AutoCorrector
|
18
|
+
extend TargetRailsVersion
|
19
|
+
|
20
|
+
minimum_target_rails_version 5.1
|
21
|
+
|
22
|
+
MSG = 'Avoid top-level `HashWithIndifferentAccess`.'
|
23
|
+
|
24
|
+
# @!method top_level_hash_with_indifferent_access?(node)
|
25
|
+
# @param [RuboCop::AST::ConstNode] node
|
26
|
+
# @return [Boolean]
|
27
|
+
def_node_matcher :top_level_hash_with_indifferent_access?, <<~PATTERN
|
28
|
+
(const {nil? cbase} :HashWithIndifferentAccess)
|
29
|
+
PATTERN
|
30
|
+
|
31
|
+
# @param [RuboCop::AST::ConstNode] node
|
32
|
+
def on_const(node)
|
33
|
+
return unless top_level_hash_with_indifferent_access?(node)
|
34
|
+
return if node.parent&.class_type? && node.parent.ancestors.any?(&:module_type?)
|
35
|
+
|
36
|
+
add_offense(node) do |corrector|
|
37
|
+
autocorrect(corrector, node)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def autocorrect(corrector, node)
|
44
|
+
corrector.insert_before(node.location.name, 'ActiveSupport::')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Checks for the use of exit statements (namely `return`,
|
7
|
+
# `break` and `throw`) in transactions. This is due to the eventual
|
8
|
+
# unexpected behavior when using ActiveRecord >= 7, where transactions
|
9
|
+
# exited using these statements are being rollbacked rather than
|
10
|
+
# committed (pre ActiveRecord 7 behavior).
|
11
|
+
#
|
12
|
+
# As alternatives, it would be more intuitive to explicitly raise an
|
13
|
+
# error when rollback is desired, and to use `next` when commit is
|
14
|
+
# desired.
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# # bad
|
18
|
+
# ApplicationRecord.transaction do
|
19
|
+
# return if user.active?
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # bad
|
23
|
+
# ApplicationRecord.transaction do
|
24
|
+
# break if user.active?
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# # bad
|
28
|
+
# ApplicationRecord.transaction do
|
29
|
+
# throw if user.active?
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# # bad, as `with_lock` implicitly opens a transaction too
|
33
|
+
# user.with_lock do
|
34
|
+
# throw if user.active?
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# # good
|
38
|
+
# ApplicationRecord.transaction do
|
39
|
+
# # Rollback
|
40
|
+
# raise "User is active" if user.active?
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# # good
|
44
|
+
# ApplicationRecord.transaction do
|
45
|
+
# # Commit
|
46
|
+
# next if user.active?
|
47
|
+
# end
|
48
|
+
class TransactionExitStatement < Base
|
49
|
+
MSG = <<~MSG.chomp
|
50
|
+
Exit statement `%<statement>s` is not allowed. Use `raise` (rollback) or `next` (commit).
|
51
|
+
MSG
|
52
|
+
|
53
|
+
RESTRICT_ON_SEND = %i[transaction with_lock].freeze
|
54
|
+
|
55
|
+
def_node_search :exit_statements, <<~PATTERN
|
56
|
+
({return | break | send nil? :throw} ...)
|
57
|
+
PATTERN
|
58
|
+
|
59
|
+
def_node_matcher :rescue_body_return_node?, <<~PATTERN
|
60
|
+
(:resbody ...
|
61
|
+
...
|
62
|
+
({return | break | send nil? :throw} ...)
|
63
|
+
...
|
64
|
+
)
|
65
|
+
PATTERN
|
66
|
+
|
67
|
+
def on_send(node)
|
68
|
+
return unless (parent = node.parent)
|
69
|
+
return unless parent.block_type? && parent.body
|
70
|
+
|
71
|
+
exit_statements(parent.body).each do |statement_node|
|
72
|
+
next if statement_node.break_type? && nested_block?(statement_node)
|
73
|
+
|
74
|
+
statement = statement(statement_node)
|
75
|
+
message = format(MSG, statement: statement)
|
76
|
+
|
77
|
+
add_offense(statement_node, message: message)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def statement(statement_node)
|
84
|
+
if statement_node.return_type?
|
85
|
+
'return'
|
86
|
+
elsif statement_node.break_type?
|
87
|
+
'break'
|
88
|
+
else
|
89
|
+
statement_node.method_name
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def nested_block?(statement_node)
|
94
|
+
!statement_node.ancestors.find(&:block_type?).method?(:transaction)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|