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.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -10
  3. data/config/default.yml +50 -491
  4. data/lib/rubocop.rb +5 -53
  5. data/lib/rubocop/ast/builder.rb +2 -0
  6. data/lib/rubocop/ast/node.rb +1 -1
  7. data/lib/rubocop/ast/node/float_node.rb +12 -0
  8. data/lib/rubocop/ast/node/int_node.rb +12 -0
  9. data/lib/rubocop/ast/node/mixin/numeric_node.rb +21 -0
  10. data/lib/rubocop/ast/node/resbody_node.rb +1 -6
  11. data/lib/rubocop/cached_data.rb +1 -1
  12. data/lib/rubocop/config.rb +35 -6
  13. data/lib/rubocop/config_loader.rb +2 -2
  14. data/lib/rubocop/config_loader_resolver.rb +0 -6
  15. data/lib/rubocop/cop/cop.rb +0 -4
  16. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +55 -0
  17. data/lib/rubocop/cop/layout/class_structure.rb +1 -1
  18. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +3 -1
  19. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +4 -0
  20. data/lib/rubocop/cop/layout/indent_first_argument.rb +6 -2
  21. data/lib/rubocop/cop/layout/indent_first_parameter.rb +7 -3
  22. data/lib/rubocop/cop/layout/indent_heredoc.rb +0 -1
  23. data/lib/rubocop/cop/layout/indentation_consistency.rb +13 -12
  24. data/lib/rubocop/cop/layout/indentation_width.rb +8 -4
  25. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +2 -0
  26. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  27. data/lib/rubocop/cop/mixin/hash_alignment.rb +4 -0
  28. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +20 -22
  29. data/lib/rubocop/cop/style/commented_keyword.rb +1 -1
  30. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -1
  31. data/lib/rubocop/cop/style/float_division.rb +94 -0
  32. data/lib/rubocop/cop/style/format_string.rb +7 -3
  33. data/lib/rubocop/cop/style/if_inside_else.rb +42 -0
  34. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -1
  35. data/lib/rubocop/cop/style/safe_navigation.rb +1 -1
  36. data/lib/rubocop/cop/style/ternary_parentheses.rb +12 -2
  37. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -0
  38. data/lib/rubocop/cop/style/word_array.rb +2 -2
  39. data/lib/rubocop/cop/style/zero_length_predicate.rb +1 -1
  40. data/lib/rubocop/node_pattern.rb +84 -5
  41. data/lib/rubocop/options.rb +0 -2
  42. data/lib/rubocop/processed_source.rb +5 -1
  43. data/lib/rubocop/rspec/cop_helper.rb +0 -1
  44. data/lib/rubocop/rspec/shared_contexts.rb +0 -17
  45. data/lib/rubocop/rspec/support.rb +0 -1
  46. data/lib/rubocop/runner.rb +6 -7
  47. data/lib/rubocop/version.rb +1 -1
  48. data/lib/rubocop/yaml_duplication_checker.rb +8 -2
  49. metadata +7 -69
  50. data/lib/rubocop/cop/mixin/target_rails_version.rb +0 -16
  51. data/lib/rubocop/cop/rails/action_filter.rb +0 -117
  52. data/lib/rubocop/cop/rails/active_record_aliases.rb +0 -48
  53. data/lib/rubocop/cop/rails/active_record_override.rb +0 -82
  54. data/lib/rubocop/cop/rails/active_support_aliases.rb +0 -69
  55. data/lib/rubocop/cop/rails/application_job.rb +0 -40
  56. data/lib/rubocop/cop/rails/application_record.rb +0 -40
  57. data/lib/rubocop/cop/rails/assert_not.rb +0 -44
  58. data/lib/rubocop/cop/rails/belongs_to.rb +0 -102
  59. data/lib/rubocop/cop/rails/blank.rb +0 -164
  60. data/lib/rubocop/cop/rails/bulk_change_table.rb +0 -289
  61. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +0 -91
  62. data/lib/rubocop/cop/rails/date.rb +0 -161
  63. data/lib/rubocop/cop/rails/delegate.rb +0 -132
  64. data/lib/rubocop/cop/rails/delegate_allow_blank.rb +0 -37
  65. data/lib/rubocop/cop/rails/dynamic_find_by.rb +0 -91
  66. data/lib/rubocop/cop/rails/enum_uniqueness.rb +0 -45
  67. data/lib/rubocop/cop/rails/environment_comparison.rb +0 -68
  68. data/lib/rubocop/cop/rails/exit.rb +0 -67
  69. data/lib/rubocop/cop/rails/file_path.rb +0 -108
  70. data/lib/rubocop/cop/rails/find_by.rb +0 -55
  71. data/lib/rubocop/cop/rails/find_each.rb +0 -51
  72. data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +0 -25
  73. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +0 -106
  74. data/lib/rubocop/cop/rails/http_positional_arguments.rb +0 -117
  75. data/lib/rubocop/cop/rails/http_status.rb +0 -179
  76. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +0 -94
  77. data/lib/rubocop/cop/rails/inverse_of.rb +0 -246
  78. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +0 -175
  79. data/lib/rubocop/cop/rails/link_to_blank.rb +0 -98
  80. data/lib/rubocop/cop/rails/not_null_column.rb +0 -67
  81. data/lib/rubocop/cop/rails/output.rb +0 -49
  82. data/lib/rubocop/cop/rails/output_safety.rb +0 -99
  83. data/lib/rubocop/cop/rails/pluralization_grammar.rb +0 -107
  84. data/lib/rubocop/cop/rails/presence.rb +0 -124
  85. data/lib/rubocop/cop/rails/present.rb +0 -153
  86. data/lib/rubocop/cop/rails/read_write_attribute.rb +0 -74
  87. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +0 -111
  88. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +0 -136
  89. data/lib/rubocop/cop/rails/reflection_class_name.rb +0 -37
  90. data/lib/rubocop/cop/rails/refute_methods.rb +0 -76
  91. data/lib/rubocop/cop/rails/relative_date_constant.rb +0 -93
  92. data/lib/rubocop/cop/rails/request_referer.rb +0 -56
  93. data/lib/rubocop/cop/rails/reversible_migration.rb +0 -286
  94. data/lib/rubocop/cop/rails/safe_navigation.rb +0 -87
  95. data/lib/rubocop/cop/rails/save_bang.rb +0 -316
  96. data/lib/rubocop/cop/rails/scope_args.rb +0 -29
  97. data/lib/rubocop/cop/rails/skips_model_validations.rb +0 -87
  98. data/lib/rubocop/cop/rails/time_zone.rb +0 -238
  99. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +0 -105
  100. data/lib/rubocop/cop/rails/unknown_env.rb +0 -63
  101. data/lib/rubocop/cop/rails/validation.rb +0 -109
  102. 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