rubocop-callbacksystems 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +78 -0
- data/config/default.yml +590 -0
- data/docs/cops.md +111 -0
- data/lib/rubocop/callbacksystems/autocorrection/batch.rb +135 -0
- data/lib/rubocop/callbacksystems/autocorrection/comment_rescue.rb +112 -0
- data/lib/rubocop/callbacksystems/autocorrection/rehearsal.rb +65 -0
- data/lib/rubocop/callbacksystems/autocorrection/rewrite_permit.rb +14 -0
- data/lib/rubocop/callbacksystems/class_structure/builder_assignment.rb +48 -0
- data/lib/rubocop/callbacksystems/class_structure/class_body.rb +50 -0
- data/lib/rubocop/callbacksystems/class_structure/divergence.rb +31 -0
- data/lib/rubocop/callbacksystems/class_structure/node_visibility.rb +82 -0
- data/lib/rubocop/callbacksystems/class_structure/reference_order.rb +36 -0
- data/lib/rubocop/callbacksystems/class_structure/statement_rank.rb +84 -0
- data/lib/rubocop/callbacksystems/class_structure/statement_run.rb +42 -0
- data/lib/rubocop/callbacksystems/class_structure/visibility.rb +84 -0
- data/lib/rubocop/callbacksystems/cops_document.rb +78 -0
- data/lib/rubocop/callbacksystems/execution/deferred_local_variables.rb +138 -0
- data/lib/rubocop/callbacksystems/execution/discarded_expression.rb +45 -0
- data/lib/rubocop/callbacksystems/execution/eager_evaluation_path.rb +73 -0
- data/lib/rubocop/callbacksystems/execution/immediate.rb +71 -0
- data/lib/rubocop/callbacksystems/execution/local_variable_occurrences.rb +112 -0
- data/lib/rubocop/callbacksystems/hashes/key_transform_detection.rb +108 -0
- data/lib/rubocop/callbacksystems/hashes/keyword_options.rb +37 -0
- data/lib/rubocop/callbacksystems/helpers/bodies.rb +150 -0
- data/lib/rubocop/callbacksystems/helpers/builders.rb +103 -0
- data/lib/rubocop/callbacksystems/helpers/comments.rb +56 -0
- data/lib/rubocop/callbacksystems/helpers/corrections.rb +15 -0
- data/lib/rubocop/callbacksystems/helpers/methods.rb +46 -0
- data/lib/rubocop/callbacksystems/helpers/names.rb +65 -0
- data/lib/rubocop/callbacksystems/helpers/node_types.rb +154 -0
- data/lib/rubocop/callbacksystems/helpers/rails.rb +52 -0
- data/lib/rubocop/callbacksystems/helpers/ranges.rb +107 -0
- data/lib/rubocop/callbacksystems/helpers/recursion.rb +29 -0
- data/lib/rubocop/callbacksystems/helpers/variables.rb +44 -0
- data/lib/rubocop/callbacksystems/helpers/visibility.rb +121 -0
- data/lib/rubocop/callbacksystems/helpers.rb +43 -0
- data/lib/rubocop/callbacksystems/methods/calls.rb +38 -0
- data/lib/rubocop/callbacksystems/methods/definitions.rb +218 -0
- data/lib/rubocop/callbacksystems/methods/delegate_macro.rb +144 -0
- data/lib/rubocop/callbacksystems/methods/delegation_target.rb +55 -0
- data/lib/rubocop/callbacksystems/methods/domain.rb +77 -0
- data/lib/rubocop/callbacksystems/methods/implicit_invocations.rb +17 -0
- data/lib/rubocop/callbacksystems/methods/macro_references.rb +238 -0
- data/lib/rubocop/callbacksystems/methods/reference_evidence.rb +74 -0
- data/lib/rubocop/callbacksystems/methods/siblings.rb +24 -0
- data/lib/rubocop/callbacksystems/methods/statement_definitions.rb +68 -0
- data/lib/rubocop/callbacksystems/offense.rb +13 -0
- data/lib/rubocop/callbacksystems/plugin.rb +27 -0
- data/lib/rubocop/callbacksystems/project_index/cache.rb +16 -0
- data/lib/rubocop/callbacksystems/project_index/confinement_cache.rb +40 -0
- data/lib/rubocop/callbacksystems/project_index/constant_references.rb +74 -0
- data/lib/rubocop/callbacksystems/project_index/corrections.rb +35 -0
- data/lib/rubocop/callbacksystems/project_index/coverage.rb +56 -0
- data/lib/rubocop/callbacksystems/project_index/diagnostics.rb +60 -0
- data/lib/rubocop/callbacksystems/project_index/freshness.rb +104 -0
- data/lib/rubocop/callbacksystems/project_index/loader.rb +9 -0
- data/lib/rubocop/callbacksystems/project_index/location.rb +29 -0
- data/lib/rubocop/callbacksystems/project_index/nested_class_confinement.rb +297 -0
- data/lib/rubocop/callbacksystems/project_index/nested_class_method_dispatch.rb +50 -0
- data/lib/rubocop/callbacksystems/project_index/reading.rb +129 -0
- data/lib/rubocop/callbacksystems/project_index/source_file.rb +59 -0
- data/lib/rubocop/callbacksystems/project_index/source_file_match.rb +57 -0
- data/lib/rubocop/callbacksystems/project_index/support.rb +46 -0
- data/lib/rubocop/callbacksystems/source/comment_blocks.rb +43 -0
- data/lib/rubocop/callbacksystems/source/comments.rb +141 -0
- data/lib/rubocop/callbacksystems/source/file_ast.rb +15 -0
- data/lib/rubocop/callbacksystems/source/files_checksum.rb +43 -0
- data/lib/rubocop/callbacksystems/source/single_line_call.rb +181 -0
- data/lib/rubocop/callbacksystems/source/statement_with_comments.rb +131 -0
- data/lib/rubocop/callbacksystems/testing/cop_helpers.rb +42 -0
- data/lib/rubocop/callbacksystems/testing/fixture_call.rb +30 -0
- data/lib/rubocop/callbacksystems/testing/fixture_names.rb +62 -0
- data/lib/rubocop/callbacksystems/testing/path_mapping.rb +104 -0
- data/lib/rubocop/cop/callbacksystems/application_controller_method_definition.rb +44 -0
- data/lib/rubocop/cop/callbacksystems/assert_with_expected_argument.rb +60 -0
- data/lib/rubocop/cop/callbacksystems/base.rb +134 -0
- data/lib/rubocop/cop/callbacksystems/body_order.rb +478 -0
- data/lib/rubocop/cop/callbacksystems/builder_method_parentheses.rb +74 -0
- data/lib/rubocop/cop/callbacksystems/collapse_multiline_expression.rb +140 -0
- data/lib/rubocop/cop/callbacksystems/comment_block_length.rb +137 -0
- data/lib/rubocop/cop/callbacksystems/complex_conditional.rb +75 -0
- data/lib/rubocop/cop/callbacksystems/controller_test_response_assertion.rb +105 -0
- data/lib/rubocop/cop/callbacksystems/controller_test_response_first_assertion.rb +123 -0
- data/lib/rubocop/cop/callbacksystems/data_clump.rb +339 -0
- data/lib/rubocop/cop/callbacksystems/declarative_method_naming.rb +350 -0
- data/lib/rubocop/cop/callbacksystems/delegate_requires_private_option.rb +81 -0
- data/lib/rubocop/cop/callbacksystems/eager_loading_scope_naming.rb +386 -0
- data/lib/rubocop/cop/callbacksystems/early_return.rb +215 -0
- data/lib/rubocop/cop/callbacksystems/empty_line_before_method.rb +168 -0
- data/lib/rubocop/cop/callbacksystems/empty_line_between_sections.rb +169 -0
- data/lib/rubocop/cop/callbacksystems/empty_private_section.rb +107 -0
- data/lib/rubocop/cop/callbacksystems/expanded_empty_class_or_module.rb +71 -0
- data/lib/rubocop/cop/callbacksystems/inline_memoized_computation.rb +110 -0
- data/lib/rubocop/cop/callbacksystems/job_enqueue_methods.rb +93 -0
- data/lib/rubocop/cop/callbacksystems/max_safe_navigation_depth.rb +118 -0
- data/lib/rubocop/cop/callbacksystems/method_invocation_order.rb +261 -0
- data/lib/rubocop/cop/callbacksystems/nested_class_order.rb +255 -0
- data/lib/rubocop/cop/callbacksystems/no_abbreviations.rb +197 -0
- data/lib/rubocop/cop/callbacksystems/no_anemic_delegation.rb +101 -0
- data/lib/rubocop/cop/callbacksystems/no_anemic_record.rb +309 -0
- data/lib/rubocop/cop/callbacksystems/no_assert_select.rb +31 -0
- data/lib/rubocop/cop/callbacksystems/no_bang_method_without_counterpart.rb +451 -0
- data/lib/rubocop/cop/callbacksystems/no_call_method.rb +73 -0
- data/lib/rubocop/cop/callbacksystems/no_comments_in_test_class_body.rb +159 -0
- data/lib/rubocop/cop/callbacksystems/no_consecutive_appends.rb +261 -0
- data/lib/rubocop/cop/callbacksystems/no_custom_route_actions.rb +124 -0
- data/lib/rubocop/cop/callbacksystems/no_direct_eager_loading.rb +90 -0
- data/lib/rubocop/cop/callbacksystems/no_each_with_early_return.rb +58 -0
- data/lib/rubocop/cop/callbacksystems/no_explicit_public_modifier.rb +81 -0
- data/lib/rubocop/cop/callbacksystems/no_hash_with_default_block.rb +68 -0
- data/lib/rubocop/cop/callbacksystems/no_local_variable_return.rb +173 -0
- data/lib/rubocop/cop/callbacksystems/no_manual_accumulation.rb +188 -0
- data/lib/rubocop/cop/callbacksystems/no_mixed_memoization.rb +145 -0
- data/lib/rubocop/cop/callbacksystems/no_nested_modules_in_classes.rb +78 -0
- data/lib/rubocop/cop/callbacksystems/no_nil_at_end_of_method.rb +74 -0
- data/lib/rubocop/cop/callbacksystems/no_redundant_validation_name.rb +37 -0
- data/lib/rubocop/cop/callbacksystems/no_redundant_wrapper_method.rb +161 -0
- data/lib/rubocop/cop/callbacksystems/no_section_divider_comments.rb +105 -0
- data/lib/rubocop/cop/callbacksystems/no_send_in_tests.rb +75 -0
- data/lib/rubocop/cop/callbacksystems/no_service_objects.rb +64 -0
- data/lib/rubocop/cop/callbacksystems/no_single_letter_names.rb +47 -0
- data/lib/rubocop/cop/callbacksystems/no_singleton_class_in_modules.rb +43 -0
- data/lib/rubocop/cop/callbacksystems/no_tap_collection_building.rb +173 -0
- data/lib/rubocop/cop/callbacksystems/no_turbo_stream_in_controllers.rb +52 -0
- data/lib/rubocop/cop/callbacksystems/no_typographic_clutter.rb +159 -0
- data/lib/rubocop/cop/callbacksystems/no_writes_in_get_actions.rb +149 -0
- data/lib/rubocop/cop/callbacksystems/ordered_mixin_arguments.rb +78 -0
- data/lib/rubocop/cop/callbacksystems/plural_controller_names.rb +103 -0
- data/lib/rubocop/cop/callbacksystems/prefer_attr_reader_over_instance_variable.rb +86 -0
- data/lib/rubocop/cop/callbacksystems/prefer_backslash_continuation.rb +478 -0
- data/lib/rubocop/cop/callbacksystems/prefer_class_for_state.rb +532 -0
- data/lib/rubocop/cop/callbacksystems/prefer_class_new_for_empty_nested_class.rb +247 -0
- data/lib/rubocop/cop/callbacksystems/prefer_class_over_builder_block.rb +124 -0
- data/lib/rubocop/cop/callbacksystems/prefer_default_argument.rb +225 -0
- data/lib/rubocop/cop/callbacksystems/prefer_delegate.rb +242 -0
- data/lib/rubocop/cop/callbacksystems/prefer_delegate_over_instance_variable_assignment.rb +175 -0
- data/lib/rubocop/cop/callbacksystems/prefer_excluding.rb +204 -0
- data/lib/rubocop/cop/callbacksystems/prefer_extract_associated.rb +54 -0
- data/lib/rubocop/cop/callbacksystems/prefer_it_block_parameter.rb +156 -0
- data/lib/rubocop/cop/callbacksystems/prefer_many.rb +57 -0
- data/lib/rubocop/cop/callbacksystems/prefer_method_over_lambda.rb +211 -0
- data/lib/rubocop/cop/callbacksystems/prefer_method_over_variable.rb +172 -0
- data/lib/rubocop/cop/callbacksystems/prefer_module_for_static_class.rb +286 -0
- data/lib/rubocop/cop/callbacksystems/prefer_ordinal_array_access.rb +136 -0
- data/lib/rubocop/cop/callbacksystems/prefer_positive_wrap.rb +265 -0
- data/lib/rubocop/cop/callbacksystems/prefer_squish.rb +47 -0
- data/lib/rubocop/cop/callbacksystems/prefer_stringify_keys.rb +24 -0
- data/lib/rubocop/cop/callbacksystems/prefer_symbolize_keys.rb +24 -0
- data/lib/rubocop/cop/callbacksystems/prefer_turbo_page_refresh_broadcasts.rb +40 -0
- data/lib/rubocop/cop/callbacksystems/private_constants_in_private_section.rb +160 -0
- data/lib/rubocop/cop/callbacksystems/private_delegate_placement.rb +342 -0
- data/lib/rubocop/cop/callbacksystems/private_methods_in_private_section.rb +70 -0
- data/lib/rubocop/cop/callbacksystems/private_nested_class_method_visibility.rb +269 -0
- data/lib/rubocop/cop/callbacksystems/private_nested_classes.rb +94 -0
- data/lib/rubocop/cop/callbacksystems/public_methods_must_have_tests.rb +190 -0
- data/lib/rubocop/cop/callbacksystems/redundant_concern_extend.rb +157 -0
- data/lib/rubocop/cop/callbacksystems/redundant_constant_freeze.rb +57 -0
- data/lib/rubocop/cop/callbacksystems/redundant_constant_namespace.rb +134 -0
- data/lib/rubocop/cop/callbacksystems/reflow_comments.rb +133 -0
- data/lib/rubocop/cop/callbacksystems/repeated_fixture_in_tests.rb +106 -0
- data/lib/rubocop/cop/callbacksystems/restricted_controller_actions.rb +85 -0
- data/lib/rubocop/cop/callbacksystems/routes_module_scope.rb +336 -0
- data/lib/rubocop/cop/callbacksystems/single_line_setup_block.rb +107 -0
- data/lib/rubocop/cop/callbacksystems/single_singleton_class.rb +49 -0
- data/lib/rubocop/cop/callbacksystems/single_top_level_class_or_module.rb +160 -0
- data/lib/rubocop/cop/callbacksystems/single_use_fixture_variable.rb +211 -0
- data/lib/rubocop/cop/callbacksystems/single_use_setup_variable.rb +684 -0
- data/lib/rubocop/cop/callbacksystems/singular_scope_names.rb +47 -0
- data/lib/rubocop/cop/callbacksystems/space_inside_percent_literal.rb +73 -0
- data/lib/rubocop/cop/callbacksystems/system_test_browser_driver.rb +30 -0
- data/lib/rubocop/cop/callbacksystems/test_method_definition.rb +86 -0
- data/lib/rubocop/cop/callbacksystems/test_method_order.rb +319 -0
- data/lib/rubocop/cop/callbacksystems/too_many_instance_variables.rb +78 -0
- data/lib/rubocop/cop/callbacksystems/too_many_local_variables.rb +77 -0
- data/lib/rubocop/cop/callbacksystems/too_many_validation_guards.rb +84 -0
- data/lib/rubocop/cop/callbacksystems/unnecessary_local_variable.rb +176 -0
- data/lib/rubocop/cop/callbacksystems/unused_private_method_in_nested_class.rb +203 -0
- data/lib/rubocop-callbacksystems.rb +21 -0
- data/rubocop.yml +685 -0
- metadata +337 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Detects `str.strip.gsub(/\s+/, " ")` patterns that should use `squish`. The
|
|
2
|
+
# method names the whole transformation, where the chain spells out a regexp
|
|
3
|
+
# the reader has to decode.
|
|
4
|
+
#
|
|
5
|
+
# @example
|
|
6
|
+
# # bad
|
|
7
|
+
# text.strip.gsub(/\s+/, " ")
|
|
8
|
+
# name.strip.gsub(/[[:space:]]+/, " ")
|
|
9
|
+
#
|
|
10
|
+
# # good
|
|
11
|
+
# text.squish
|
|
12
|
+
# name.squish
|
|
13
|
+
#
|
|
14
|
+
class RuboCop::Cop::Callbacksystems::PreferSquish < RuboCop::Cop::Callbacksystems::Base
|
|
15
|
+
extend RuboCop::Cop::AutoCorrector
|
|
16
|
+
|
|
17
|
+
MESSAGE = 'Use `squish` instead of `strip.gsub(/\s+/, " ")`.'
|
|
18
|
+
WHITESPACE_PATTERNS = %w[ \s+ [[:space:]]+ ]
|
|
19
|
+
|
|
20
|
+
# @!method strip_gsub_pattern(node)
|
|
21
|
+
def_node_matcher :strip_gsub_pattern, <<~PATTERN
|
|
22
|
+
(call $(call _ :strip) :gsub (regexp (str $_pattern) (regopt)) (str " "))
|
|
23
|
+
PATTERN
|
|
24
|
+
|
|
25
|
+
def on_send(node)
|
|
26
|
+
strip_gsub_pattern(node) do |strip_call, pattern|
|
|
27
|
+
next if WHITESPACE_PATTERNS.exclude?(pattern)
|
|
28
|
+
|
|
29
|
+
add_squish_offense(node, strip_call:)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
alias on_csend on_send
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
def add_squish_offense(node, strip_call:)
|
|
37
|
+
if source_comments.any_within?(node)
|
|
38
|
+
add_offense(node, message: MESSAGE)
|
|
39
|
+
else
|
|
40
|
+
add_offense(node, message: MESSAGE) { it.replace(node, replacement_for(strip_call)) }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def replacement_for(call)
|
|
45
|
+
"#{call_prefix_of(call)}squish"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Detects `hash.transform_keys(&:to_s)` patterns that should use `stringify_keys`.
|
|
2
|
+
# The Active Support method names what the keys become, where the transform
|
|
3
|
+
# makes the reader work it out from the block.
|
|
4
|
+
#
|
|
5
|
+
# @example
|
|
6
|
+
# # bad
|
|
7
|
+
# hash.transform_keys(&:to_s)
|
|
8
|
+
# hash.transform_keys { |k| k.to_s }
|
|
9
|
+
#
|
|
10
|
+
# # good
|
|
11
|
+
# hash.stringify_keys
|
|
12
|
+
#
|
|
13
|
+
class RuboCop::Cop::Callbacksystems::PreferStringifyKeys < RuboCop::Cop::Callbacksystems::Base
|
|
14
|
+
include RuboCop::Callbacksystems::Hashes::KeyTransformDetection
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
def source_method
|
|
18
|
+
:to_s
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def preferred_method
|
|
22
|
+
:stringify_keys
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Detects `hash.transform_keys(&:to_sym)` patterns that should use `symbolize_keys`.
|
|
2
|
+
# The Active Support method names what the keys become, where the transform
|
|
3
|
+
# makes the reader work it out from the block.
|
|
4
|
+
#
|
|
5
|
+
# @example
|
|
6
|
+
# # bad
|
|
7
|
+
# hash.transform_keys(&:to_sym)
|
|
8
|
+
# hash.transform_keys { |k| k.to_sym }
|
|
9
|
+
#
|
|
10
|
+
# # good
|
|
11
|
+
# hash.symbolize_keys
|
|
12
|
+
#
|
|
13
|
+
class RuboCop::Cop::Callbacksystems::PreferSymbolizeKeys < RuboCop::Cop::Callbacksystems::Base
|
|
14
|
+
include RuboCop::Callbacksystems::Hashes::KeyTransformDetection
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
def source_method
|
|
18
|
+
:to_sym
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def preferred_method
|
|
22
|
+
:symbolize_keys
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Enforces Turbo page refreshes over targeted stream broadcasts.
|
|
2
|
+
#
|
|
3
|
+
# Targeted broadcasts (append, prepend, replace, remove, and friends) and the
|
|
4
|
+
# macros that generate them tie the server to specific partials and DOM targets.
|
|
5
|
+
# Page refresh broadcasts let the client morph the current page instead, which
|
|
6
|
+
# keeps the broadcasting code declarative and the views as the single source of
|
|
7
|
+
# truth.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# # bad
|
|
11
|
+
# broadcasts_to :board
|
|
12
|
+
# message.broadcast_append_to :messages
|
|
13
|
+
# broadcast_replace_later_to room
|
|
14
|
+
#
|
|
15
|
+
# # good - macro
|
|
16
|
+
# broadcasts_refreshes
|
|
17
|
+
# broadcasts_refreshes_to :board
|
|
18
|
+
#
|
|
19
|
+
# # good - granular, manual refresh
|
|
20
|
+
# broadcast_refresh_to :board
|
|
21
|
+
# broadcast_refresh_later_to room
|
|
22
|
+
#
|
|
23
|
+
class RuboCop::Cop::Callbacksystems::PreferTurboPageRefreshBroadcasts < RuboCop::Cop::Callbacksystems::Base
|
|
24
|
+
MESSAGE = "Prefer Turbo page refreshes over `%<name>s`."
|
|
25
|
+
|
|
26
|
+
def on_send(node)
|
|
27
|
+
add_offense(node, message: format(MESSAGE, name: node.method_name)) if targeted_broadcast?(node)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
alias on_csend on_send
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
MACROS = %i[ broadcasts broadcasts_to ]
|
|
34
|
+
TARGETED_BROADCAST =
|
|
35
|
+
/\Abroadcast_(append|prepend|replace|update|remove|before|after|action|render)(_later)?(_to)?\z/
|
|
36
|
+
|
|
37
|
+
def targeted_broadcast?(node)
|
|
38
|
+
(bare_send?(node) && MACROS.include?(node.method_name)) || TARGETED_BROADCAST.match?(node.method_name)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# A constant that only serves the implementation belongs in the private
|
|
2
|
+
# section, where every other implementation detail already lives. Marking it
|
|
3
|
+
# with `private_constant` from the top of the class says the same thing twice
|
|
4
|
+
# and leaves the declaration among the public ones.
|
|
5
|
+
#
|
|
6
|
+
# The marker earns its place when class-level code above the private section
|
|
7
|
+
# reads the constant, since the declaration cannot move below the code that
|
|
8
|
+
# reads it. Class-level code inside the section moves down with it.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# # bad - only method bodies read it
|
|
12
|
+
# class Backend
|
|
13
|
+
# Route = Data.define(:host, :matcher)
|
|
14
|
+
# private_constant :Route
|
|
15
|
+
#
|
|
16
|
+
# def route_for(host)
|
|
17
|
+
# Route.new(host, nil)
|
|
18
|
+
# end
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# # good - declared where the implementation lives
|
|
22
|
+
# class Backend
|
|
23
|
+
# def route_for(host)
|
|
24
|
+
# Route.new(host, nil)
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# private
|
|
28
|
+
# Route = Data.define(:host, :matcher)
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# # bad - the reader is private too, so both move
|
|
32
|
+
# class Backend
|
|
33
|
+
# MEMBERS = [ :host, :matcher ]
|
|
34
|
+
# private_constant :MEMBERS
|
|
35
|
+
#
|
|
36
|
+
# private
|
|
37
|
+
# Route = Data.define(*MEMBERS)
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
# # good
|
|
41
|
+
# class Backend
|
|
42
|
+
# private
|
|
43
|
+
# MEMBERS = [ :host, :matcher ]
|
|
44
|
+
# Route = Data.define(*MEMBERS)
|
|
45
|
+
# end
|
|
46
|
+
#
|
|
47
|
+
# # good - class-level code above reads it, so it has to stay there
|
|
48
|
+
# class Backend
|
|
49
|
+
# FORMATS = [ :json ]
|
|
50
|
+
# private_constant :FORMATS
|
|
51
|
+
#
|
|
52
|
+
# validates :format, inclusion: { in: FORMATS }
|
|
53
|
+
# end
|
|
54
|
+
#
|
|
55
|
+
class RuboCop::Cop::Callbacksystems::PrivateConstantsInPrivateSection < RuboCop::Cop::Callbacksystems::Base
|
|
56
|
+
def on_send(node)
|
|
57
|
+
report_each PrivateConstantMarker.new(node)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
alias on_csend on_send
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
DEFERRED_MACROS = %i[ test setup teardown ]
|
|
64
|
+
|
|
65
|
+
class PrivateConstantMarker
|
|
66
|
+
include RuboCop::Callbacksystems::Helpers
|
|
67
|
+
|
|
68
|
+
MESSAGE = "Declare `%<name>s` in the private section instead of marking it with `private_constant`."
|
|
69
|
+
REDUNDANT_MESSAGE = "Remove `private_constant` from `%<name>s`, which is already declared in the private section."
|
|
70
|
+
|
|
71
|
+
def initialize(node)
|
|
72
|
+
@node = node
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def each_offense
|
|
76
|
+
movable_arguments.each { yield RuboCop::Callbacksystems::Offense.new(it, message_for(it)) }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
attr_reader :node
|
|
81
|
+
|
|
82
|
+
def movable_arguments
|
|
83
|
+
enclosing_body ? names_marked_by(node, macro: :private_constant).reject { pinned?(it) } : []
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def enclosing_body
|
|
87
|
+
enclosing_class_or_module_of(node)&.body
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def pinned?(argument)
|
|
91
|
+
pinning_names.include?(argument.value.to_s)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def pinning_names
|
|
95
|
+
@pinning_names ||= constant_reads.select(&:pins_declaration?).map(&:name)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def constant_reads
|
|
99
|
+
constant_nodes.map { ConstantRead.new(it, enclosing_body) }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def constant_nodes
|
|
103
|
+
nodes_in(enclosing_body, :const)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def message_for(argument)
|
|
107
|
+
format(template_for(argument.value.to_sym), name: argument.value)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def template_for(name)
|
|
111
|
+
declared_privately?(declaration_of(name)) ? REDUNDANT_MESSAGE : MESSAGE
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def declared_privately?(declaration)
|
|
115
|
+
declaration && in_private_section?(declaration, enclosing_body)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def declaration_of(name)
|
|
119
|
+
statements_in(enclosing_body).find { declared_name_of(it) == name }
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# A read that would break if the declaration moved, made by class-level code above the private section.
|
|
124
|
+
class ConstantRead
|
|
125
|
+
include RuboCop::Callbacksystems::Helpers
|
|
126
|
+
|
|
127
|
+
def initialize(node, body)
|
|
128
|
+
@node = node
|
|
129
|
+
@body = body
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def pins_declaration?
|
|
133
|
+
own_constant? && class_level? && !in_private_section?(node, body)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def name
|
|
137
|
+
node.short_name.to_s
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
private
|
|
141
|
+
attr_reader :node, :body
|
|
142
|
+
|
|
143
|
+
# `Other::Namespace::NAME` reads a constant of its own that happens to end in the same name.
|
|
144
|
+
def own_constant?
|
|
145
|
+
node.namespace.nil?
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def class_level?
|
|
149
|
+
!runs_later? && !definition_identifier?(node)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def runs_later?
|
|
153
|
+
enclosing_method_of(node).present? || inside_deferred_block?
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def inside_deferred_block?
|
|
157
|
+
node.each_ancestor(:any_block).any? { DEFERRED_MACROS.include?(it.method_name) }
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# `delegate ... private: true` declares private methods, so it belongs in the
|
|
2
|
+
# private section next to the other private declarations. Left above, it hides
|
|
3
|
+
# private methods among the public API; left below the section's methods, it
|
|
4
|
+
# hides a declaration among behavior.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# # bad
|
|
8
|
+
# class Report
|
|
9
|
+
# delegate :total, to: :order, private: true
|
|
10
|
+
#
|
|
11
|
+
# def to_s
|
|
12
|
+
# total.to_s
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# private
|
|
16
|
+
# attr_reader :order
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# # bad - below the methods of its section
|
|
20
|
+
# class Report
|
|
21
|
+
# private
|
|
22
|
+
# def formatted_total
|
|
23
|
+
# total.to_s
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# delegate :total, to: :order, private: true
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# # good
|
|
30
|
+
# class Report
|
|
31
|
+
# def to_s
|
|
32
|
+
# total.to_s
|
|
33
|
+
# end
|
|
34
|
+
#
|
|
35
|
+
# private
|
|
36
|
+
# attr_reader :order
|
|
37
|
+
# delegate :total, to: :order, private: true
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
class RuboCop::Cop::Callbacksystems::PrivateDelegatePlacement < RuboCop::Cop::Callbacksystems::Base
|
|
41
|
+
extend RuboCop::Cop::AutoCorrector
|
|
42
|
+
|
|
43
|
+
def on_send(node)
|
|
44
|
+
report Delegate.new(node, processed_source)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
alias on_csend on_send
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
class Delegate
|
|
51
|
+
include RuboCop::Callbacksystems::Helpers
|
|
52
|
+
|
|
53
|
+
MESSAGE = "Move this `delegate` into the private section; it declares private methods."
|
|
54
|
+
STRAYED_MESSAGE = "Move this `delegate` up beside the other private declarations."
|
|
55
|
+
|
|
56
|
+
def initialize(node, processed_source)
|
|
57
|
+
@node = node
|
|
58
|
+
@processed_source = processed_source
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def offense
|
|
62
|
+
RuboCop::Callbacksystems::Offense.new(node, message, correcting: correctable?) { correct(it) } if misplaced?
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
attr_reader :node, :processed_source
|
|
67
|
+
|
|
68
|
+
def misplaced?
|
|
69
|
+
private_delegate? && statements.include?(node) && !placed_with_declarations?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def private_delegate?
|
|
73
|
+
macro.macro? && macro.private?
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def macro
|
|
77
|
+
@macro ||= RuboCop::Callbacksystems::Methods::DelegateMacro.new(node)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def statements
|
|
81
|
+
@statements ||= statements_in(enclosing_body)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def enclosing_body
|
|
85
|
+
@enclosing_body ||= enclosing_definition_of(node)&.body
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def placed_with_declarations?
|
|
89
|
+
declarations_after_private_modifier.include?(node)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def declarations_after_private_modifier
|
|
93
|
+
statements_after_private_modifier.take_while { declaration?(it) }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def statements_after_private_modifier
|
|
97
|
+
private_modifier ? statements.drop(statements.index(private_modifier) + 1) : []
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def private_modifier
|
|
101
|
+
@private_modifier ||= private_modifier_in(enclosing_body)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def declaration?(statement)
|
|
105
|
+
statement.casgn_type? || declaration_macro?(statement)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def message
|
|
109
|
+
outside_private_section? ? MESSAGE : STRAYED_MESSAGE
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def outside_private_section?
|
|
113
|
+
visibility_at(node, enclosing_body) != :private
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def correctable?
|
|
117
|
+
movement_safe? && relocation_safe?
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def movement_safe?
|
|
121
|
+
!blocked_by_constant? && !blocked_by_method_definition? && !blocked_by_comment? &&
|
|
122
|
+
!blocked_by_executable_statement? && tooling_scope_kept?
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Moving up over the assignment of a constant the delegate reads would break the class at load time.
|
|
126
|
+
def blocked_by_constant?
|
|
127
|
+
referenced_constant_names.intersect?(constant_names_crossed_moving_up)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def referenced_constant_names
|
|
131
|
+
node.arguments.flat_map { it.each_node(:const).map(&:short_name) }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def constant_names_crossed_moving_up
|
|
135
|
+
if anchor_index && anchor_index < node_index
|
|
136
|
+
statements[(anchor_index + 1)...node_index].select(&:casgn_type?).map(&:name)
|
|
137
|
+
else
|
|
138
|
+
[]
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def anchor_index
|
|
143
|
+
statements.index(anchor)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def anchor
|
|
147
|
+
declarations_after_private_modifier.last || private_modifier || statements.excluding(node).last
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def node_index
|
|
151
|
+
statements.index(node)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Swapping two declarations of the same method would change which implementation the class keeps.
|
|
155
|
+
def blocked_by_method_definition?
|
|
156
|
+
names = macro.defined_method_names&.to_set
|
|
157
|
+
crossed_names = method_names_defined_by(crossed_statements)
|
|
158
|
+
|
|
159
|
+
names.nil? || crossed_names.nil? || names.intersect?(crossed_names)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def method_names_defined_by(found)
|
|
163
|
+
names = found.map { RuboCop::Callbacksystems::Methods::StatementDefinitions.new(it).names }
|
|
164
|
+
|
|
165
|
+
names.flat_map(&:to_a).to_set if names.none?(&:nil?)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def crossed_statements
|
|
169
|
+
if anchor_index
|
|
170
|
+
if node_index < anchor_index
|
|
171
|
+
statements[(node_index + 1)..anchor_index]
|
|
172
|
+
else
|
|
173
|
+
statements[(anchor_index + 1)...node_index]
|
|
174
|
+
end
|
|
175
|
+
else
|
|
176
|
+
[]
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def blocked_by_comment?
|
|
181
|
+
movement_blocks.each_cons(2).any? do |above, below|
|
|
182
|
+
above.range.end.join(below.range.begin).source.strip.present?
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def movement_blocks
|
|
187
|
+
movement_statements.map { RuboCop::Callbacksystems::Source::StatementWithComments.new(it, processed_source) }
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def movement_statements
|
|
191
|
+
if anchor_index
|
|
192
|
+
first, last = [ node_index, anchor_index ].minmax
|
|
193
|
+
statements[first..last]
|
|
194
|
+
else
|
|
195
|
+
[ node ]
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Method bodies do not run while the class loads. Unknown statements do, so their relative order is kept.
|
|
200
|
+
def blocked_by_executable_statement?
|
|
201
|
+
crossed_statements.any? { !movable_across?(it) }
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def movable_across?(statement)
|
|
205
|
+
statement.type?(:def, :defs) || visibility_modifier_of(statement) || declaration_macro?(statement)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def tooling_scope_kept?
|
|
209
|
+
movement_blocks.none?(&:contains_tooling_comment?)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def relocation_safe?
|
|
213
|
+
relocation_ranges_proven? && relocated_source.possible?
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def relocation_ranges_proven?
|
|
217
|
+
statement_owns_line?(node) && (!anchor || statement_owns_line?(anchor))
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def statement_owns_line?(statement)
|
|
221
|
+
StatementLine.new(statement).owned?
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def relocated_source
|
|
225
|
+
@relocated_source ||= RelocatedSource.new(block, to: section_indentation)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def block
|
|
229
|
+
@block ||= RuboCop::Callbacksystems::Source::StatementWithComments.new(node, processed_source)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def section_indentation
|
|
233
|
+
statements_after_private_modifier.first&.then { indentation_of(it) } ||
|
|
234
|
+
"#{body_indentation}#{indentation_step}"
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def body_indentation
|
|
238
|
+
indentation_of(statements.first)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def indentation_step
|
|
242
|
+
" " * (statements.first.source_range.column - enclosing_definition_of(node).source_range.column)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def correct(corrector)
|
|
246
|
+
if anchor
|
|
247
|
+
corrector.insert_after(anchor_block.range, relocated_delegate)
|
|
248
|
+
corrector.remove(statement_removal_range_of(block.range))
|
|
249
|
+
else
|
|
250
|
+
corrector.replace(block.range, private_section)
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def anchor_block
|
|
255
|
+
@anchor_block ||= RuboCop::Callbacksystems::Source::StatementWithComments.new(anchor, processed_source)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def relocated_delegate
|
|
259
|
+
private_modifier ? indented_delegate : "\n\n#{body_indentation}private#{indented_delegate}"
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def indented_delegate
|
|
263
|
+
"\n#{relocated_source.source}"
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def private_section
|
|
267
|
+
"#{body_indentation}private#{indented_delegate}"
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Whether a statement range can be moved without consuming code that shares either edge line.
|
|
272
|
+
class StatementLine
|
|
273
|
+
def initialize(node)
|
|
274
|
+
@node = node
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def owned?
|
|
278
|
+
code_before.strip.empty? && (code_after.strip.empty? || code_after.lstrip.start_with?("#"))
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
private
|
|
282
|
+
attr_reader :node
|
|
283
|
+
|
|
284
|
+
def code_before
|
|
285
|
+
node.source_range.source_line[0...node.source_range.column]
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def code_after
|
|
289
|
+
node.source_range.end.source_line[node.source_range.end.column..]
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Source shifted by one indentation delta, with relative whitespace and heredoc contents left intact.
|
|
294
|
+
class RelocatedSource
|
|
295
|
+
def initialize(statement, to:)
|
|
296
|
+
@statement = statement
|
|
297
|
+
@target_indentation = to
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def possible?
|
|
301
|
+
indentation_removable? && heredocs_preserved?
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def source
|
|
305
|
+
lines.map { reindented(it) }.join
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
private
|
|
309
|
+
attr_reader :statement, :target_indentation
|
|
310
|
+
|
|
311
|
+
def indentation_removable?
|
|
312
|
+
delta >= 0 || lines.all? { it.strip.empty? || it.start_with?(removed_indentation) }
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def delta
|
|
316
|
+
target_indentation.length - statement.node.source_range.column
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def lines
|
|
320
|
+
@lines ||= statement.source.lines
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def removed_indentation
|
|
324
|
+
" " * -delta
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def heredocs_preserved?
|
|
328
|
+
delta.zero? || heredocs.all? { it.source.start_with?("<<~") }
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def heredocs
|
|
332
|
+
statement.node.each_node(:str, :dstr, :xstr).select { it.loc?(:heredoc_body) }
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def reindented(line)
|
|
336
|
+
if line.strip.empty? then line
|
|
337
|
+
elsif delta.positive? then "#{" " * delta}#{line}"
|
|
338
|
+
else line.delete_prefix(removed_indentation)
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# A method you define goes in the private section, not above it with a
|
|
2
|
+
# `private :name` afterwards. The section already says what the marker repeats.
|
|
3
|
+
#
|
|
4
|
+
# The marker earns its place when the method is not defined here, since a
|
|
5
|
+
# section cannot reach a method that arrives from a superclass or a macro.
|
|
6
|
+
# `Data.define` generating readers you would rather hide is the usual case.
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# # bad
|
|
10
|
+
# class Report
|
|
11
|
+
# def total
|
|
12
|
+
# end
|
|
13
|
+
# private :total
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# # good
|
|
17
|
+
# class Report
|
|
18
|
+
# private
|
|
19
|
+
# def total
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# # good - the reader comes from the superclass, so nothing here can hold it
|
|
24
|
+
# class Request < Data.define(:lock_held)
|
|
25
|
+
# alias lock_held? lock_held
|
|
26
|
+
#
|
|
27
|
+
# private :lock_held
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
class RuboCop::Cop::Callbacksystems::PrivateMethodsInPrivateSection < RuboCop::Cop::Callbacksystems::Base
|
|
31
|
+
def on_send(node)
|
|
32
|
+
report_each PrivateCall.new(node)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
alias on_csend on_send
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
class PrivateCall
|
|
39
|
+
include RuboCop::Callbacksystems::Helpers
|
|
40
|
+
|
|
41
|
+
MESSAGE = "Declare `%<name>s` in the private section instead of marking it with `private :%<name>s`."
|
|
42
|
+
|
|
43
|
+
def initialize(node)
|
|
44
|
+
@node = node
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def each_offense
|
|
48
|
+
movable_arguments.each { yield RuboCop::Callbacksystems::Offense.new(it, format(MESSAGE, name: it.value)) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
attr_reader :node
|
|
53
|
+
|
|
54
|
+
def movable_arguments
|
|
55
|
+
names_marked_by(node, macro: :private).select { defined_here?(it) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def defined_here?(argument)
|
|
59
|
+
method_names.include?(argument.value.to_sym)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def method_names
|
|
63
|
+
@method_names ||= direct_definitions_in(enclosing_body, :def).map(&:method_name)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def enclosing_body
|
|
67
|
+
enclosing_definition_of(node)&.body
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|