rubocop-rails 2.9.1 → 2.11.2
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/LICENSE.txt +1 -1
- data/README.md +4 -4
- data/config/default.yml +111 -5
- data/config/obsoletion.yml +7 -0
- data/lib/rubocop/cop/mixin/active_record_helper.rb +11 -0
- data/lib/rubocop/cop/rails/add_column_index.rb +64 -0
- data/lib/rubocop/cop/rails/attribute_default_block_value.rb +1 -1
- data/lib/rubocop/cop/rails/belongs_to.rb +1 -1
- data/lib/rubocop/cop/rails/blank.rb +4 -0
- data/lib/rubocop/cop/rails/bulk_change_table.rb +5 -1
- data/lib/rubocop/cop/rails/content_tag.rb +18 -3
- data/lib/rubocop/cop/rails/date.rb +17 -6
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +4 -2
- data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +78 -0
- data/lib/rubocop/cop/rails/environment_comparison.rb +1 -2
- data/lib/rubocop/cop/rails/environment_variable_access.rb +67 -0
- data/lib/rubocop/cop/rails/expanded_date_range.rb +86 -0
- data/lib/rubocop/cop/rails/file_path.rb +2 -4
- data/lib/rubocop/cop/rails/find_by.rb +31 -12
- data/lib/rubocop/cop/rails/find_each.rb +2 -0
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +34 -4
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +8 -1
- data/lib/rubocop/cop/rails/http_status.rb +12 -3
- data/lib/rubocop/cop/rails/i18n_locale_assignment.rb +37 -0
- data/lib/rubocop/cop/rails/inverse_of.rb +1 -2
- data/lib/rubocop/cop/rails/link_to_blank.rb +5 -1
- data/lib/rubocop/cop/rails/reflection_class_name.rb +14 -1
- data/lib/rubocop/cop/rails/relative_date_constant.rb +18 -19
- data/lib/rubocop/cop/rails/require_dependency.rb +38 -0
- data/lib/rubocop/cop/rails/reversible_migration.rb +1 -1
- data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +75 -0
- data/lib/rubocop/cop/rails/safe_navigation.rb +20 -2
- data/lib/rubocop/cop/rails/time_zone.rb +22 -22
- data/lib/rubocop/cop/rails/time_zone_assignment.rb +37 -0
- data/lib/rubocop/cop/rails/unknown_env.rb +1 -1
- data/lib/rubocop/cop/rails/unused_ignored_columns.rb +69 -0
- data/lib/rubocop/cop/rails/where_exists.rb +11 -0
- data/lib/rubocop/cop/rails/where_not.rb +5 -1
- data/lib/rubocop/cop/rails_cops.rb +9 -0
- data/lib/rubocop/rails.rb +2 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +2 -4
- data/lib/rubocop/rails/version.rb +1 -1
- metadata +21 -11
@@ -18,6 +18,7 @@ module RuboCop
|
|
18
18
|
# get :new, params: { user_id: 1 }
|
19
19
|
# get :new, **options
|
20
20
|
class HttpPositionalArguments < Base
|
21
|
+
include RangeHelp
|
21
22
|
extend AutoCorrector
|
22
23
|
extend TargetRailsVersion
|
23
24
|
|
@@ -44,7 +45,7 @@ module RuboCop
|
|
44
45
|
|
45
46
|
message = format(MSG, verb: node.method_name)
|
46
47
|
|
47
|
-
add_offense(node
|
48
|
+
add_offense(highlight_range(node), message: message) do |corrector|
|
48
49
|
# given a pre Rails 5 method: get :new, {user_id: @user.id}, {}
|
49
50
|
#
|
50
51
|
# @return lambda of auto correct procedure
|
@@ -80,6 +81,12 @@ module RuboCop
|
|
80
81
|
node.sym_type? && node.value == :format
|
81
82
|
end
|
82
83
|
|
84
|
+
def highlight_range(node)
|
85
|
+
_http_path, *data = *node.arguments
|
86
|
+
|
87
|
+
range_between(data.first.source_range.begin_pos, data.last.source_range.end_pos)
|
88
|
+
end
|
89
|
+
|
83
90
|
def convert_hash_data(data, type)
|
84
91
|
return '' if data.hash_type? && data.empty?
|
85
92
|
|
@@ -11,12 +11,14 @@ module RuboCop
|
|
11
11
|
# render json: { foo: 'bar' }, status: 200
|
12
12
|
# render plain: 'foo/bar', status: 304
|
13
13
|
# redirect_to root_url, status: 301
|
14
|
+
# head 200
|
14
15
|
#
|
15
16
|
# # good
|
16
17
|
# render :foo, status: :ok
|
17
18
|
# render json: { foo: 'bar' }, status: :ok
|
18
19
|
# render plain: 'foo/bar', status: :not_modified
|
19
20
|
# redirect_to root_url, status: :moved_permanently
|
21
|
+
# head :ok
|
20
22
|
#
|
21
23
|
# @example EnforcedStyle: numeric
|
22
24
|
# # bad
|
@@ -24,23 +26,26 @@ module RuboCop
|
|
24
26
|
# render json: { foo: 'bar' }, status: :not_found
|
25
27
|
# render plain: 'foo/bar', status: :not_modified
|
26
28
|
# redirect_to root_url, status: :moved_permanently
|
29
|
+
# head :ok
|
27
30
|
#
|
28
31
|
# # good
|
29
32
|
# render :foo, status: 200
|
30
33
|
# render json: { foo: 'bar' }, status: 404
|
31
34
|
# render plain: 'foo/bar', status: 304
|
32
35
|
# redirect_to root_url, status: 301
|
36
|
+
# head 200
|
33
37
|
#
|
34
38
|
class HttpStatus < Base
|
35
39
|
include ConfigurableEnforcedStyle
|
36
40
|
extend AutoCorrector
|
37
41
|
|
38
|
-
RESTRICT_ON_SEND = %i[render redirect_to].freeze
|
42
|
+
RESTRICT_ON_SEND = %i[render redirect_to head].freeze
|
39
43
|
|
40
44
|
def_node_matcher :http_status, <<~PATTERN
|
41
45
|
{
|
42
46
|
(send nil? {:render :redirect_to} _ $hash)
|
43
47
|
(send nil? {:render :redirect_to} $hash)
|
48
|
+
(send nil? :head ${int sym} ...)
|
44
49
|
}
|
45
50
|
PATTERN
|
46
51
|
|
@@ -49,8 +54,12 @@ module RuboCop
|
|
49
54
|
PATTERN
|
50
55
|
|
51
56
|
def on_send(node)
|
52
|
-
http_status(node) do |
|
53
|
-
status =
|
57
|
+
http_status(node) do |hash_node_or_status_code|
|
58
|
+
status = if hash_node_or_status_code.hash_type?
|
59
|
+
status_code(hash_node_or_status_code)
|
60
|
+
else
|
61
|
+
hash_node_or_status_code
|
62
|
+
end
|
54
63
|
return unless status
|
55
64
|
|
56
65
|
checker = checker_class.new(status)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks for the use of `I18n.locale=` method.
|
7
|
+
#
|
8
|
+
# The `locale` attribute persists for the rest of the Ruby runtime, potentially causing
|
9
|
+
# unexpected behavior at a later time.
|
10
|
+
# Using `I18n.with_locale` ensures the code passed in the block is the only place `I18n.locale` is affected.
|
11
|
+
# It eliminates the possibility of a `locale` sticking around longer than intended.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# # bad
|
15
|
+
# I18n.locale = :fr
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# I18n.with_locale(:fr) do
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
class I18nLocaleAssignment < Base
|
22
|
+
MSG = 'Use `I18n.with_locale` with block instead of `I18n.locale=`.'
|
23
|
+
RESTRICT_ON_SEND = %i[locale=].freeze
|
24
|
+
|
25
|
+
def_node_matcher :i18n_locale_assignment?, <<~PATTERN
|
26
|
+
(send (const {nil? cbase} :I18n) :locale= ...)
|
27
|
+
PATTERN
|
28
|
+
|
29
|
+
def on_send(node)
|
30
|
+
return unless i18n_locale_assignment?(node)
|
31
|
+
|
32
|
+
add_offense(node)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -130,8 +130,7 @@ module RuboCop
|
|
130
130
|
# @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Setting+Inverses
|
131
131
|
class InverseOf < Base
|
132
132
|
SPECIFY_MSG = 'Specify an `:inverse_of` option.'
|
133
|
-
NIL_MSG = 'You specified `inverse_of: nil`, you probably meant to '
|
134
|
-
'use `inverse_of: false`.'
|
133
|
+
NIL_MSG = 'You specified `inverse_of: nil`, you probably meant to use `inverse_of: false`.'
|
135
134
|
RESTRICT_ON_SEND = %i[has_many has_one belongs_to].freeze
|
136
135
|
|
137
136
|
def_node_matcher :association_recv_arguments, <<~PATTERN
|
@@ -79,7 +79,11 @@ module RuboCop
|
|
79
79
|
opening_quote = offence_node.children.last.source[0]
|
80
80
|
closing_quote = opening_quote == ':' ? '' : opening_quote
|
81
81
|
new_rel_exp = ", rel: #{opening_quote}noopener#{closing_quote}"
|
82
|
-
range = send_node.
|
82
|
+
range = if (last_argument = send_node.last_argument).hash_type?
|
83
|
+
last_argument.pairs.last.source_range
|
84
|
+
else
|
85
|
+
last_argument.source_range
|
86
|
+
end
|
83
87
|
|
84
88
|
corrector.insert_after(range, new_rel_exp)
|
85
89
|
end
|
@@ -5,6 +5,8 @@ module RuboCop
|
|
5
5
|
module Rails
|
6
6
|
# This cop checks if the value of the option `class_name`, in
|
7
7
|
# the definition of a reflection is a string.
|
8
|
+
# It is marked as unsafe because it cannot be determined whether
|
9
|
+
# constant or method return value specified to `class_name` is a string.
|
8
10
|
#
|
9
11
|
# @example
|
10
12
|
# # bad
|
@@ -16,6 +18,7 @@ module RuboCop
|
|
16
18
|
class ReflectionClassName < Base
|
17
19
|
MSG = 'Use a string value for `class_name`.'
|
18
20
|
RESTRICT_ON_SEND = %i[has_many has_one belongs_to].freeze
|
21
|
+
ALLOWED_REFLECTION_CLASS_TYPES = %i[dstr str sym].freeze
|
19
22
|
|
20
23
|
def_node_matcher :association_with_reflection, <<~PATTERN
|
21
24
|
(send nil? {:has_many :has_one :belongs_to} _ _ ?
|
@@ -24,7 +27,7 @@ module RuboCop
|
|
24
27
|
PATTERN
|
25
28
|
|
26
29
|
def_node_matcher :reflection_class_name, <<~PATTERN
|
27
|
-
(pair (sym :class_name)
|
30
|
+
(pair (sym :class_name) #reflection_class_value?)
|
28
31
|
PATTERN
|
29
32
|
|
30
33
|
def on_send(node)
|
@@ -32,6 +35,16 @@ module RuboCop
|
|
32
35
|
add_offense(reflection_class_name.loc.expression)
|
33
36
|
end
|
34
37
|
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def reflection_class_value?(class_value)
|
42
|
+
if class_value.send_type?
|
43
|
+
!class_value.method?(:to_s) || class_value.receiver&.const_type?
|
44
|
+
else
|
45
|
+
!ALLOWED_REFLECTION_CLASS_TYPES.include?(class_value.type)
|
46
|
+
end
|
47
|
+
end
|
35
48
|
end
|
36
49
|
end
|
37
50
|
end
|
@@ -31,11 +31,12 @@ module RuboCop
|
|
31
31
|
include RangeHelp
|
32
32
|
extend AutoCorrector
|
33
33
|
|
34
|
-
MSG = 'Do not assign
|
34
|
+
MSG = 'Do not assign `%<method_name>s` to constants as it ' \
|
35
35
|
'will be evaluated only once.'
|
36
|
+
RELATIVE_DATE_METHODS = %i[since from_now after ago until before yesterday tomorrow].to_set.freeze
|
36
37
|
|
37
38
|
def on_casgn(node)
|
38
|
-
|
39
|
+
nested_relative_date(node) do |method_name|
|
39
40
|
add_offense(node, message: message(method_name)) do |corrector|
|
40
41
|
autocorrect(corrector, node)
|
41
42
|
end
|
@@ -50,7 +51,7 @@ module RuboCop
|
|
50
51
|
lhs.children.zip(rhs.children).each do |(name, value)|
|
51
52
|
next unless name.casgn_type?
|
52
53
|
|
53
|
-
|
54
|
+
nested_relative_date(value) do |method_name|
|
54
55
|
add_offense(offense_range(name, value), message: message(method_name)) do |corrector|
|
55
56
|
autocorrect(corrector, node)
|
56
57
|
end
|
@@ -59,7 +60,7 @@ module RuboCop
|
|
59
60
|
end
|
60
61
|
|
61
62
|
def on_or_asgn(node)
|
62
|
-
relative_date_or_assignment
|
63
|
+
relative_date_or_assignment(node) do |method_name|
|
63
64
|
add_offense(node, message: format(MSG, method_name: method_name))
|
64
65
|
end
|
65
66
|
end
|
@@ -88,24 +89,22 @@ module RuboCop
|
|
88
89
|
range_between(name.loc.expression.begin_pos, value.loc.expression.end_pos)
|
89
90
|
end
|
90
91
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
(
|
96
|
-
|
97
|
-
|
92
|
+
def nested_relative_date(node, &callback)
|
93
|
+
return if node.block_type?
|
94
|
+
|
95
|
+
node.each_child_node do |child|
|
96
|
+
nested_relative_date(child, &callback)
|
97
|
+
end
|
98
|
+
|
99
|
+
relative_date(node, &callback)
|
100
|
+
end
|
98
101
|
|
99
|
-
def_node_matcher :relative_date_or_assignment
|
100
|
-
(:or_asgn (casgn _ _) (send _ $
|
102
|
+
def_node_matcher :relative_date_or_assignment, <<~PATTERN
|
103
|
+
(:or_asgn (casgn _ _) (send _ $RELATIVE_DATE_METHODS))
|
101
104
|
PATTERN
|
102
105
|
|
103
|
-
def_node_matcher :relative_date
|
104
|
-
|
105
|
-
({erange irange} _ (send _ ${:since :from_now :after :ago :until :before}))
|
106
|
-
({erange irange} (send _ ${:since :from_now :after :ago :until :before}) _)
|
107
|
-
(send _ ${:since :from_now :after :ago :until :before})
|
108
|
-
}
|
106
|
+
def_node_matcher :relative_date, <<~PATTERN
|
107
|
+
(send _ $RELATIVE_DATE_METHODS)
|
109
108
|
PATTERN
|
110
109
|
end
|
111
110
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks for the usage of `require_dependency`.
|
7
|
+
#
|
8
|
+
# `require_dependency` is an obsolete method for Rails applications running in Zeitwerk mode.
|
9
|
+
# In Zeitwerk mode, the semantics should match Ruby's and no need to be defensive with load order,
|
10
|
+
# just refer to classes and modules normally.
|
11
|
+
# If the constant name is dynamic, camelize if needed, and constantize.
|
12
|
+
#
|
13
|
+
# Applications running in Zeitwerk mode should not use `require_dependency`.
|
14
|
+
#
|
15
|
+
# NOTE: This cop is disabled by default. Please enable it if you are using Zeitwerk mode.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# # bad
|
19
|
+
# require_dependency 'some_lib'
|
20
|
+
class RequireDependency < Base
|
21
|
+
extend TargetRailsVersion
|
22
|
+
|
23
|
+
minimum_target_rails_version 6.0
|
24
|
+
|
25
|
+
MSG = 'Do not use `require_dependency` with Zeitwerk mode.'
|
26
|
+
RESTRICT_ON_SEND = %i[require_dependency].freeze
|
27
|
+
|
28
|
+
def_node_matcher :require_dependency_call?, <<~PATTERN
|
29
|
+
(send {nil? (const _ :Kernel)} :require_dependency _)
|
30
|
+
PATTERN
|
31
|
+
|
32
|
+
def on_send(node)
|
33
|
+
require_dependency_call?(node) { add_offense(node) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -237,7 +237,7 @@ module RuboCop
|
|
237
237
|
|
238
238
|
def check_drop_table_node(node)
|
239
239
|
drop_table_call(node) do
|
240
|
-
unless node.parent.block_type?
|
240
|
+
unless node.parent.block_type? || node.last_argument.block_pass_type?
|
241
241
|
add_offense(
|
242
242
|
node,
|
243
243
|
message: format(MSG, action: 'drop_table(without block)')
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks whether the migration implements
|
7
|
+
# either a `change` method or both an `up` and a `down`
|
8
|
+
# method.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# # bad
|
12
|
+
# class SomeMigration < ActiveRecord::Migration[6.0]
|
13
|
+
# def up
|
14
|
+
# # up migration
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# # <----- missing down method
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# class SomeMigration < ActiveRecord::Migration[6.0]
|
21
|
+
# # <----- missing up method
|
22
|
+
#
|
23
|
+
# def down
|
24
|
+
# # down migration
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# # good
|
29
|
+
# class SomeMigration < ActiveRecord::Migration[6.0]
|
30
|
+
# def change
|
31
|
+
# # reversible migration
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # good
|
36
|
+
# class SomeMigration < ActiveRecord::Migration[6.0]
|
37
|
+
# def up
|
38
|
+
# # up migration
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# def down
|
42
|
+
# # down migration
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
class ReversibleMigrationMethodDefinition < Base
|
46
|
+
MSG = 'Migrations must contain either a `change` method, or ' \
|
47
|
+
'both an `up` and a `down` method.'
|
48
|
+
|
49
|
+
def_node_matcher :migration_class?, <<~PATTERN
|
50
|
+
(class
|
51
|
+
(const nil? _)
|
52
|
+
(send
|
53
|
+
(const (const {nil? cbase} :ActiveRecord) :Migration)
|
54
|
+
:[]
|
55
|
+
(float _))
|
56
|
+
_)
|
57
|
+
PATTERN
|
58
|
+
|
59
|
+
def_node_matcher :change_method?, <<~PATTERN
|
60
|
+
[ #migration_class? `(def :change (args) _) ]
|
61
|
+
PATTERN
|
62
|
+
|
63
|
+
def_node_matcher :up_and_down_methods?, <<~PATTERN
|
64
|
+
[ #migration_class? `(def :up (args) _) `(def :down (args) _) ]
|
65
|
+
PATTERN
|
66
|
+
|
67
|
+
def on_class(node)
|
68
|
+
return if change_method?(node) || up_and_down_methods?(node)
|
69
|
+
|
70
|
+
add_offense(node)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -44,9 +44,22 @@ module RuboCop
|
|
44
44
|
RESTRICT_ON_SEND = %i[try try!].freeze
|
45
45
|
|
46
46
|
def_node_matcher :try_call, <<~PATTERN
|
47
|
-
(send
|
47
|
+
(send _ ${:try :try!} $_ ...)
|
48
48
|
PATTERN
|
49
49
|
|
50
|
+
# Monkey patching for `Style/RedundantSelf` of RuboCop core.
|
51
|
+
# rubocop:disable Style/ClassAndModuleChildren
|
52
|
+
class Style::RedundantSelf
|
53
|
+
def self.autocorrect_incompatible_with
|
54
|
+
[Rails::SafeNavigation]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
# rubocop:enable Style/ClassAndModuleChildren
|
58
|
+
|
59
|
+
def self.autocorrect_incompatible_with
|
60
|
+
[Style::RedundantSelf]
|
61
|
+
end
|
62
|
+
|
50
63
|
def on_send(node)
|
51
64
|
try_call(node) do |try_method, dispatch|
|
52
65
|
return if try_method == :try && !cop_config['ConvertTry']
|
@@ -64,7 +77,12 @@ module RuboCop
|
|
64
77
|
method_node, *params = *node.arguments
|
65
78
|
method = method_node.source[1..-1]
|
66
79
|
|
67
|
-
range =
|
80
|
+
range = if node.receiver
|
81
|
+
range_between(node.loc.dot.begin_pos, node.loc.expression.end_pos)
|
82
|
+
else
|
83
|
+
corrector.insert_before(node, 'self')
|
84
|
+
node
|
85
|
+
end
|
68
86
|
|
69
87
|
corrector.replace(range, replacement(method, params))
|
70
88
|
end
|
@@ -8,40 +8,33 @@ module RuboCop
|
|
8
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
|
16
|
-
#
|
17
|
-
# @example EnforcedStyle: strict
|
18
|
-
# # `strict` means that `Time` should be used with `zone`.
|
15
|
+
# to use `Time#in_time_zone`.
|
19
16
|
#
|
17
|
+
# @example
|
20
18
|
# # bad
|
21
19
|
# Time.now
|
22
|
-
# Time.parse('2015-03-
|
23
|
-
#
|
24
|
-
# # bad
|
25
|
-
# Time.current
|
26
|
-
# Time.at(timestamp).in_time_zone
|
20
|
+
# Time.parse('2015-03-02T19:05:37')
|
27
21
|
#
|
28
22
|
# # good
|
23
|
+
# Time.current
|
29
24
|
# Time.zone.now
|
30
|
-
# Time.zone.parse('2015-03-
|
25
|
+
# Time.zone.parse('2015-03-02T19:05:37')
|
26
|
+
# Time.zone.parse('2015-03-02T19:05:37Z') # Respect ISO 8601 format with timezone specifier.
|
31
27
|
#
|
32
|
-
# @example EnforcedStyle:
|
33
|
-
# # `
|
28
|
+
# @example EnforcedStyle: strict
|
29
|
+
# # `strict` means that `Time` should be used with `zone`.
|
34
30
|
#
|
35
31
|
# # bad
|
36
|
-
# Time.
|
37
|
-
# Time.parse('2015-03-02 19:05:37')
|
32
|
+
# Time.at(timestamp).in_time_zone
|
38
33
|
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
# Time.zone.parse('2015-03-02 19:05:37')
|
34
|
+
# @example EnforcedStyle: flexible (default)
|
35
|
+
# # `flexible` allows usage of `in_time_zone` instead of `zone`.
|
42
36
|
#
|
43
37
|
# # good
|
44
|
-
# Time.current
|
45
38
|
# Time.at(timestamp).in_time_zone
|
46
39
|
class TimeZone < Base
|
47
40
|
include ConfigurableEnforcedStyle
|
@@ -58,11 +51,13 @@ module RuboCop
|
|
58
51
|
|
59
52
|
GOOD_METHODS = %i[zone zone_default find_zone find_zone!].freeze
|
60
53
|
|
61
|
-
DANGEROUS_METHODS = %i[now local new parse at
|
54
|
+
DANGEROUS_METHODS = %i[now local new parse at].freeze
|
62
55
|
|
63
56
|
ACCEPTED_METHODS = %i[in_time_zone utc getlocal xmlschema iso8601
|
64
57
|
jisx0301 rfc3339 httpdate to_i to_f].freeze
|
65
58
|
|
59
|
+
TIMEZONE_SPECIFIER = /[A-z]/.freeze
|
60
|
+
|
66
61
|
def on_const(node)
|
67
62
|
mod, klass = *node
|
68
63
|
# we should only check core classes
|
@@ -116,9 +111,10 @@ module RuboCop
|
|
116
111
|
end
|
117
112
|
|
118
113
|
def check_time_node(klass, node)
|
114
|
+
return if attach_timezone_specifier?(node.first_argument)
|
115
|
+
|
119
116
|
chain = extract_method_chain(node)
|
120
117
|
return if not_danger_chain?(chain)
|
121
|
-
|
122
118
|
return check_localtime(node) if need_check_localtime?(chain)
|
123
119
|
|
124
120
|
method_name = (chain & DANGEROUS_METHODS).join('.')
|
@@ -132,6 +128,10 @@ module RuboCop
|
|
132
128
|
end
|
133
129
|
end
|
134
130
|
|
131
|
+
def attach_timezone_specifier?(date)
|
132
|
+
date.respond_to?(:value) && TIMEZONE_SPECIFIER.match?(date.value.to_s[-1])
|
133
|
+
end
|
134
|
+
|
135
135
|
def build_message(klass, method_name, node)
|
136
136
|
if flexible?
|
137
137
|
format(
|