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,161 @@
|
|
|
1
|
+
# Detects private methods that only call another method or read a constant
|
|
2
|
+
# without adding value. Such a wrapper is one more name for a reader to
|
|
3
|
+
# resolve on the way to the thing it stands for, and it hides the call site
|
|
4
|
+
# from whoever searches for the method it wraps. Public methods and predicates
|
|
5
|
+
# are excluded as they often provide semantic value.
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# # bad - private wrapper just calls another method
|
|
9
|
+
# private
|
|
10
|
+
# def find_all
|
|
11
|
+
# find(@node)
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# # bad - private wrapper carries the name of the constant it reads
|
|
15
|
+
# MESSAGE = "Remove this."
|
|
16
|
+
#
|
|
17
|
+
# private
|
|
18
|
+
# def message
|
|
19
|
+
# MESSAGE
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# # good - read the constant where it is needed
|
|
23
|
+
# MESSAGE = "Remove this."
|
|
24
|
+
#
|
|
25
|
+
# private
|
|
26
|
+
# def offense
|
|
27
|
+
# Offense.new(node, MESSAGE)
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# # good - the method names a concept, and a subclass answers with its own constant
|
|
31
|
+
# private
|
|
32
|
+
# def marker_tag
|
|
33
|
+
# DEFAULT_MARKER_TAG
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# # good - nothing in the class calls it, so a superclass or a subclass does
|
|
37
|
+
# private
|
|
38
|
+
# def path_after_saving
|
|
39
|
+
# root_path
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# # good - read from several places, so it is the one spelling of that value
|
|
43
|
+
# private
|
|
44
|
+
# def scope
|
|
45
|
+
# account.widgets
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# # good - public method (may be API or provide semantic meaning)
|
|
49
|
+
# def authenticated?
|
|
50
|
+
# resume_session
|
|
51
|
+
# end
|
|
52
|
+
#
|
|
53
|
+
# # good - predicate provides semantic alias
|
|
54
|
+
# def fulfillable?
|
|
55
|
+
# paid?
|
|
56
|
+
# end
|
|
57
|
+
#
|
|
58
|
+
# # good - method does something meaningful
|
|
59
|
+
# def find_all
|
|
60
|
+
# find(@node).compact
|
|
61
|
+
# end
|
|
62
|
+
#
|
|
63
|
+
class RuboCop::Cop::Callbacksystems::NoRedundantWrapperMethod < RuboCop::Cop::Callbacksystems::Base
|
|
64
|
+
def on_new_investigation
|
|
65
|
+
@method_calls = RuboCop::Callbacksystems::Methods::Calls.new(processed_source.ast)
|
|
66
|
+
@macro_referenced_methods = RuboCop::Callbacksystems::Methods::MacroReferences::ContainerIndex.new
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def on_def(node)
|
|
70
|
+
if private_non_predicate?(node)
|
|
71
|
+
report WrapperMethod.new(node, method_calls:, macro_referenced_methods:)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
alias on_defs on_def
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
attr_reader :macro_referenced_methods, :method_calls
|
|
79
|
+
|
|
80
|
+
class WrapperMethod
|
|
81
|
+
include RuboCop::Callbacksystems::Helpers
|
|
82
|
+
|
|
83
|
+
MESSAGE = "Method `%<wrapper>s` only wraps `%<target>s` without adding value. Consider removing the indirection."
|
|
84
|
+
|
|
85
|
+
def initialize(node, method_calls:, macro_referenced_methods:)
|
|
86
|
+
@node = node
|
|
87
|
+
@method_calls = method_calls
|
|
88
|
+
@macro_referenced_methods = macro_referenced_methods
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def offense
|
|
92
|
+
RuboCop::Callbacksystems::Offense.new(node, message) if redundant? && inlineable?
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
attr_reader :node, :method_calls, :macro_referenced_methods
|
|
97
|
+
delegate :body, to: :node, private: true
|
|
98
|
+
|
|
99
|
+
def redundant?
|
|
100
|
+
wraps_a_call? || reads_a_constant?
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def wraps_a_call?
|
|
104
|
+
body&.send_type? && simple_method_call? && arguments_are_pass_through?
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def simple_method_call?
|
|
108
|
+
body.receiver.nil? || body.receiver.self_type?
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def arguments_are_pass_through?
|
|
112
|
+
body.arguments.all? { passes_through?(it) }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def passes_through?(argument)
|
|
116
|
+
argument.ivar_type? || parameter_names_of(node).include?(spelling_of(argument))
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def spelling_of(argument)
|
|
120
|
+
argument.lvar_type? ? argument.name.to_s : argument.source
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def reads_a_constant?
|
|
124
|
+
body&.const_type? && node.arguments.empty? && named_after_the_constant?
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# A method naming the constant something else states a concept, usually a hook a subclass answers with its own.
|
|
128
|
+
def named_after_the_constant?
|
|
129
|
+
node.method_name.to_s.upcase == body.short_name.to_s
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def inlineable?
|
|
133
|
+
called_in_its_class? && !sole_spelling_of_a_value? && !macro_referenced?
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# A private method nothing in its class calls is the hook a superclass or a subclass answers.
|
|
137
|
+
def called_in_its_class?
|
|
138
|
+
calls.any?
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def calls
|
|
142
|
+
@calls ||= method_calls.named(node.method_name, from: node)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def sole_spelling_of_a_value?
|
|
146
|
+
node.arguments.empty? && calls.many?
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def macro_referenced?
|
|
150
|
+
macro_referenced_methods.include?(node.method_name, beside: node)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def message
|
|
154
|
+
format(MESSAGE, wrapper: node.method_name, target: target)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def target
|
|
158
|
+
body.const_type? ? body.source : body.method_name
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# ASCII-art section dividers in comments add no information: a row of dashes,
|
|
2
|
+
# equals, or box-drawing glyphs is noise to the toolchain and signals padding
|
|
3
|
+
# over substance. A pure divider should be removed; a divider wrapping a title
|
|
4
|
+
# (`=== Setup ===`) should keep only its inner text as a normal comment.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# # bad - a pure divider, here closing the statement it follows
|
|
8
|
+
# configure_defaults # ==========
|
|
9
|
+
#
|
|
10
|
+
# # bad - a title wrapped in dividers
|
|
11
|
+
# # ===== Setup =====
|
|
12
|
+
#
|
|
13
|
+
# # good - a plain comment
|
|
14
|
+
# # Setup
|
|
15
|
+
#
|
|
16
|
+
class RuboCop::Cop::Callbacksystems::NoSectionDividerComments < RuboCop::Cop::Callbacksystems::Base
|
|
17
|
+
extend RuboCop::Cop::AutoCorrector
|
|
18
|
+
|
|
19
|
+
def on_new_investigation
|
|
20
|
+
processed_source.comments.each { report DividerComment.new(it), rewrites_comments: true }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
class DividerComment
|
|
25
|
+
include RuboCop::Callbacksystems::Helpers
|
|
26
|
+
|
|
27
|
+
PURE_MESSAGE = "Section-divider comments add no information. Remove."
|
|
28
|
+
WRAPPED_MESSAGE = "Strip the divider decorations; keep the inner text as a normal comment."
|
|
29
|
+
# `-=*_~#`, the dashes, and the Box Drawing and Block Elements blocks, by ordinal so this file stays ASCII.
|
|
30
|
+
DIVIDER_POINTS = Set[0x2D, 0x3D, 0x2A, 0x5F, 0x7E, 0x23, 0x2013, 0x2014]
|
|
31
|
+
DIVIDER_RANGE = (0x2500..0x259F)
|
|
32
|
+
|
|
33
|
+
def initialize(comment)
|
|
34
|
+
@comment = comment
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def offense
|
|
38
|
+
RuboCop::Callbacksystems::Offense.new(comment, message) { correct(it) } if divider_comment?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
attr_reader :comment
|
|
43
|
+
|
|
44
|
+
def divider_comment?
|
|
45
|
+
!tooling_comment?(comment) && (pure? || wrapped?)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def pure?
|
|
49
|
+
present? && only_divider_characters?(trimmed) && divider_run?(trimmed)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def present?
|
|
53
|
+
!trimmed.empty?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def trimmed
|
|
57
|
+
@trimmed ||= comment.text.delete_prefix("#").strip
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def only_divider_characters?(text)
|
|
61
|
+
text.each_char.all? { divider_or_space?(it) }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def divider_or_space?(character)
|
|
65
|
+
divider?(character) || character.match?(/\s/)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def divider?(character)
|
|
69
|
+
DIVIDER_POINTS.include?(character.ord) || DIVIDER_RANGE.cover?(character.ord)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def divider_run?(text)
|
|
73
|
+
text.each_char.chunk_while { |left, right| divider?(left) && divider?(right) }
|
|
74
|
+
.any? { it.size >= 2 && divider?(it.first) }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def wrapped?
|
|
78
|
+
present? && !inner_text.nil?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def inner_text
|
|
82
|
+
@inner_text ||= strip_run(trimmed, side: :leading)&.then { strip_run(it, side: :trailing) }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def strip_run(text, side:)
|
|
86
|
+
characters = side == :leading ? text.each_char : text.reverse.each_char
|
|
87
|
+
run = characters.take_while { divider?(it) }
|
|
88
|
+
if run.size >= 2
|
|
89
|
+
side == :leading ? text[run.size..].lstrip : text[0...(text.size - run.size)].rstrip
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def message
|
|
94
|
+
pure? ? PURE_MESSAGE : WRAPPED_MESSAGE
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def correct(corrector)
|
|
98
|
+
if pure?
|
|
99
|
+
corrector.remove(comment_removal_range_for(comment))
|
|
100
|
+
else
|
|
101
|
+
corrector.replace(comment, "# #{inner_text}")
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Prohibits using `send` or `__send__` inside test blocks.
|
|
2
|
+
# Private methods are private for a reason; tests should verify
|
|
3
|
+
# behavior and outcomes, not call private methods directly.
|
|
4
|
+
#
|
|
5
|
+
# @example
|
|
6
|
+
# # bad
|
|
7
|
+
# test "processes payment" do
|
|
8
|
+
# result = order.send(:process_payment)
|
|
9
|
+
# assert result
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# # bad
|
|
13
|
+
# test "processes payment" do
|
|
14
|
+
# order.__send__(:process_payment)
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# # good - test the public interface
|
|
18
|
+
# test "processes payment" do
|
|
19
|
+
# order.checkout
|
|
20
|
+
# assert order.paid?
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
class RuboCop::Cop::Callbacksystems::NoSendInTests < RuboCop::Cop::Callbacksystems::Base
|
|
24
|
+
include RuboCop::Callbacksystems::Testing::CopHelpers
|
|
25
|
+
|
|
26
|
+
def on_block(node)
|
|
27
|
+
report_each RestrictedCalls.new(node) if test_block?(node)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
alias on_numblock on_block
|
|
31
|
+
alias on_itblock on_block
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
class RestrictedCalls
|
|
35
|
+
include RuboCop::Callbacksystems::Helpers
|
|
36
|
+
|
|
37
|
+
MESSAGE = "Don't use `%<method>s` to test private methods. Test behavior through the public interface instead."
|
|
38
|
+
RESTRICTED_METHODS = %i[ send __send__ ]
|
|
39
|
+
|
|
40
|
+
def initialize(test_block)
|
|
41
|
+
@test_block = test_block
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def each_offense
|
|
45
|
+
restricted_calls.each { yield RuboCop::Callbacksystems::Offense.new(it, message_for(it)) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
attr_reader :test_block
|
|
50
|
+
|
|
51
|
+
def restricted_calls
|
|
52
|
+
immediate_execution.select { restricted?(it) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def immediate_execution
|
|
56
|
+
RuboCop::Callbacksystems::Execution::Immediate.new(test_block.body, deferred_blocks: deferred_blocks)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def deferred_blocks
|
|
60
|
+
nodes_in(test_block.body, :any_block).select { method_definition_block?(it) || nested_test_block?(it) }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def nested_test_block?(node)
|
|
64
|
+
any_block_type?(node) && bare_send?(call_of(node)) && call_of(node).method?(:test)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def restricted?(send_node)
|
|
68
|
+
send_node.call_type? && RESTRICTED_METHODS.include?(send_node.method_name) && !send_node.receiver.nil?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def message_for(send_node)
|
|
72
|
+
format(MESSAGE, method: send_node.method_name)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Prohibits files in service object-style directories under `app/`. A service
|
|
2
|
+
# is a verb with no noun, so the behavior it holds drifts away from the model
|
|
3
|
+
# that owns the data it acts on, and the model is left anemic. The behavior
|
|
4
|
+
# belongs to that model, or to a plain object in `app/models` named after the
|
|
5
|
+
# concept it stands for.
|
|
6
|
+
#
|
|
7
|
+
# Only applies to Rails applications (paths under `app/`). Gems or libraries
|
|
8
|
+
# that use directories like `commands/` or `queries/` outside of `app/` are
|
|
9
|
+
# untouched.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# # bad - file in app/services/
|
|
13
|
+
# # app/services/order_processor.rb
|
|
14
|
+
#
|
|
15
|
+
# # bad - file in app/decorators/
|
|
16
|
+
# # app/decorators/user_decorator.rb
|
|
17
|
+
#
|
|
18
|
+
# # bad - file in app/interactors/
|
|
19
|
+
# # app/interactors/create_order.rb
|
|
20
|
+
#
|
|
21
|
+
# # bad - file in app/presenters/
|
|
22
|
+
# # app/presenters/user_presenter.rb
|
|
23
|
+
#
|
|
24
|
+
# # bad - file in app/forms/
|
|
25
|
+
# # app/forms/registration_form.rb
|
|
26
|
+
#
|
|
27
|
+
# # good - use models
|
|
28
|
+
# # app/models/order.rb
|
|
29
|
+
#
|
|
30
|
+
# # good - non-Rails gem with commands in lib/
|
|
31
|
+
# # lib/my_gem/commands/run.rb
|
|
32
|
+
#
|
|
33
|
+
class RuboCop::Cop::Callbacksystems::NoServiceObjects < RuboCop::Cop::Callbacksystems::Base
|
|
34
|
+
MESSAGE = "Files in `app/%<directory>s/` are not allowed. Use models or POROs in `app/models/` instead."
|
|
35
|
+
|
|
36
|
+
def on_new_investigation
|
|
37
|
+
forbidden_directory&.then { add_offense(offense_range, message: format(MESSAGE, directory: it)) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
FORBIDDEN_DIRECTORIES = %w[
|
|
42
|
+
services
|
|
43
|
+
decorators
|
|
44
|
+
interactors
|
|
45
|
+
presenters
|
|
46
|
+
forms
|
|
47
|
+
operations
|
|
48
|
+
commands
|
|
49
|
+
queries
|
|
50
|
+
use_cases
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
def forbidden_directory
|
|
54
|
+
FORBIDDEN_DIRECTORIES.find { in_directory?(it) }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def in_directory?(directory)
|
|
58
|
+
processed_source.file_path.match?(%r{(^|/)app/#{Regexp.escape(directory)}/})
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def offense_range
|
|
62
|
+
processed_source.ast || processed_source.buffer.source_range
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Detects single-letter names for variables, block arguments, and method
|
|
2
|
+
# parameters. A single letter says nothing about what it holds, so a reader
|
|
3
|
+
# has to go back to where it was assigned to learn what `i` or `e` stands for,
|
|
4
|
+
# where a descriptive name answers on the spot.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# # bad
|
|
8
|
+
# x = 5
|
|
9
|
+
# items.each { |i| process(i) }
|
|
10
|
+
# def foo(n); end
|
|
11
|
+
# items.map { |e| e.name }
|
|
12
|
+
# hash.each { |k, v| puts k }
|
|
13
|
+
#
|
|
14
|
+
# # good
|
|
15
|
+
# count = 5
|
|
16
|
+
# items.each { |item| process(item) }
|
|
17
|
+
# def foo(number); end
|
|
18
|
+
# items.map { |element| element.name }
|
|
19
|
+
# hash.each { |key, value| puts key }
|
|
20
|
+
#
|
|
21
|
+
# # ok - underscore convention for unused
|
|
22
|
+
# items.each { |_| process }
|
|
23
|
+
# [1,2].each_with_index { |_, index| puts index }
|
|
24
|
+
#
|
|
25
|
+
class RuboCop::Cop::Callbacksystems::NoSingleLetterNames < RuboCop::Cop::Callbacksystems::Base
|
|
26
|
+
MESSAGE = "Avoid single-letter name `%<name>s`. Use a descriptive name instead."
|
|
27
|
+
|
|
28
|
+
def on_lvasgn(node)
|
|
29
|
+
name = variable_name_of(node)
|
|
30
|
+
add_offense(node, message: format(MESSAGE, name:)) if single_letter?(name)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
alias on_arg on_lvasgn
|
|
34
|
+
alias on_optarg on_lvasgn
|
|
35
|
+
alias on_restarg on_lvasgn
|
|
36
|
+
alias on_kwarg on_lvasgn
|
|
37
|
+
alias on_kwoptarg on_lvasgn
|
|
38
|
+
alias on_kwrestarg on_lvasgn
|
|
39
|
+
alias on_blockarg on_lvasgn
|
|
40
|
+
alias on_match_var on_lvasgn
|
|
41
|
+
alias on_shadowarg on_lvasgn
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
def single_letter?(name)
|
|
45
|
+
name && name.length == 1 && !name.start_with?("_")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Forbids using `class << self` inside modules.
|
|
2
|
+
# Use `extend self` instead to define module-level methods.
|
|
3
|
+
#
|
|
4
|
+
# `class << self` in modules is an imperative way to define methods
|
|
5
|
+
# when a more declarative `extend self` approach exists.
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# # bad - using class << self in module
|
|
9
|
+
# module Calculator
|
|
10
|
+
# class << self
|
|
11
|
+
# def add(a, b)
|
|
12
|
+
# a + b
|
|
13
|
+
# end
|
|
14
|
+
# end
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# # good - using extend self
|
|
18
|
+
# module Calculator
|
|
19
|
+
# extend self
|
|
20
|
+
#
|
|
21
|
+
# def add(a, b)
|
|
22
|
+
# a + b
|
|
23
|
+
# end
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
class RuboCop::Cop::Callbacksystems::NoSingletonClassInModules < RuboCop::Cop::Callbacksystems::Base
|
|
27
|
+
MESSAGE = "Use `extend self` instead of `class << self` in modules."
|
|
28
|
+
|
|
29
|
+
def on_sclass(node)
|
|
30
|
+
add_offense(node, message: MESSAGE) if singleton_section?(node) && lexical_owner_of(node)&.module_type?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
def lexical_owner_of(node)
|
|
35
|
+
node.each_ancestor.find { lexical_boundary?(it) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# A block may be evaluated with another receiver (`included`, `class_eval`, and friends), so its `self` cannot be
|
|
39
|
+
# attributed to the module around the block from syntax alone.
|
|
40
|
+
def lexical_boundary?(node)
|
|
41
|
+
node.type?(:class, :module, :sclass, :def, :defs) || any_block_type?(node)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Detects using tap to build collections imperatively. A `tap` on an empty
|
|
2
|
+
# collection is a loop that fills it by hand, so a reader has to run the block
|
|
3
|
+
# to learn what comes out, where `map`, `select`, `index_by` or `group_by`
|
|
4
|
+
# names the result in the call itself.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# # bad - building array with tap
|
|
8
|
+
# [].tap { |arr| items.each { |i| arr << i.name } }
|
|
9
|
+
#
|
|
10
|
+
# # good - use map
|
|
11
|
+
# items.map { it.name }
|
|
12
|
+
#
|
|
13
|
+
# # bad - building hash with tap
|
|
14
|
+
# {}.tap { |h| items.each { |i| h[i.id] = i } }
|
|
15
|
+
#
|
|
16
|
+
# # good - use index_by or to_h
|
|
17
|
+
# items.index_by { it.id }
|
|
18
|
+
#
|
|
19
|
+
# # bad - building Set with tap
|
|
20
|
+
# Set.new.tap { |s| items.each { |i| s << i.id } }
|
|
21
|
+
#
|
|
22
|
+
# # good - use Set.new with block or to_set
|
|
23
|
+
# items.map(&:id).to_set
|
|
24
|
+
#
|
|
25
|
+
# # bad - building Hash with default value and tap
|
|
26
|
+
# Hash.new { |h, k| h[k] = [] }.tap { |h| items.each { |i| h[i.type] << i } }
|
|
27
|
+
#
|
|
28
|
+
# # good - use group_by
|
|
29
|
+
# items.group_by(&:type)
|
|
30
|
+
#
|
|
31
|
+
# # ok - tap for logging (not collection building)
|
|
32
|
+
# user.tap { |u| logger.info(u.id) }
|
|
33
|
+
#
|
|
34
|
+
# # ok - tap for configuring object
|
|
35
|
+
# User.new.tap { |u| u.name = "John" }
|
|
36
|
+
#
|
|
37
|
+
class RuboCop::Cop::Callbacksystems::NoTapCollectionBuilding < RuboCop::Cop::Callbacksystems::Base
|
|
38
|
+
def on_block(node)
|
|
39
|
+
report TapBlock.new(node)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
alias on_numblock on_block
|
|
43
|
+
alias on_itblock on_block
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
class TapBlock
|
|
47
|
+
include RuboCop::Callbacksystems::Helpers
|
|
48
|
+
|
|
49
|
+
MESSAGE = "Don't use `tap` to build collections. Use `map`, `select`, `index_by`, `group_by`, or `to_h` instead."
|
|
50
|
+
MUTATION_METHODS = %i[ << push append []= store merge! ]
|
|
51
|
+
|
|
52
|
+
def initialize(node)
|
|
53
|
+
@node = node
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def offense
|
|
57
|
+
RuboCop::Callbacksystems::Offense.new(node, MESSAGE) if tap_on_empty_collection? && mutation_in_block?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
attr_reader :node
|
|
62
|
+
|
|
63
|
+
def tap_on_empty_collection?
|
|
64
|
+
node.method?(:tap) && CollectionReceiver.new(node.receiver).empty_collection?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def mutation_in_block?
|
|
68
|
+
RuboCop::Callbacksystems::Execution::Immediate.new(node.body).any? do |candidate|
|
|
69
|
+
candidate.call_type? && mutates_collection?(candidate)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def mutates_collection?(call)
|
|
74
|
+
MUTATION_METHODS.include?(call.method_name) && yielded_collection?(call.receiver)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def yielded_collection?(receiver)
|
|
78
|
+
directly_yielded_collection?(receiver) || stored_default_collection?(receiver)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def directly_yielded_collection?(receiver)
|
|
82
|
+
receiver&.lvar_type? && node.argument_list.any? { it.name == receiver.name } &&
|
|
83
|
+
!name_rebound_between?(receiver.name, node: receiver, boundary: node)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def stored_default_collection?(receiver)
|
|
87
|
+
receiver&.call_type? && receiver.method?(:[]) && directly_yielded_collection?(receiver.receiver) &&
|
|
88
|
+
StoredHashDefault.new(node.receiver).collection?
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
class StoredHashDefault
|
|
93
|
+
def initialize(block)
|
|
94
|
+
@block = block
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def collection?
|
|
98
|
+
default_block? && stores_default? &&
|
|
99
|
+
CollectionReceiver.new(default_value).empty_collection?
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
attr_reader :block
|
|
104
|
+
|
|
105
|
+
def default_block?
|
|
106
|
+
block&.any_block_type? && argument_names.size == 2 && block.argument_list.all?(&:arg_type?)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def argument_names
|
|
110
|
+
@argument_names ||= block.argument_list.map(&:name)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def stores_default?
|
|
114
|
+
hash_argument_assigned? && key_argument_used?
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def hash_argument_assigned?
|
|
118
|
+
assignment&.call_type? && assignment.method?(:[]=) && assignment.receiver&.lvar_type? &&
|
|
119
|
+
assignment.receiver.name == argument_names.first
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def assignment
|
|
123
|
+
block.body
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def key_argument_used?
|
|
127
|
+
key&.lvar_type? && key.name == argument_names.second
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def key
|
|
131
|
+
assignment.first_argument
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def default_value
|
|
135
|
+
assignment.last_argument
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
class CollectionReceiver
|
|
140
|
+
include RuboCop::Callbacksystems::Helpers
|
|
141
|
+
|
|
142
|
+
COLLECTION_CLASSES = %i[ Set Hash Array ]
|
|
143
|
+
|
|
144
|
+
def initialize(receiver)
|
|
145
|
+
@receiver = receiver
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def empty_collection?
|
|
149
|
+
receiver && (empty_collection_literal?(receiver) || collection_class_constructor?)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
private
|
|
153
|
+
attr_reader :receiver
|
|
154
|
+
|
|
155
|
+
def collection_class_constructor?
|
|
156
|
+
constructor.send_type? && collection_constructor? &&
|
|
157
|
+
constructor_without_entries?
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def constructor
|
|
161
|
+
@constructor ||= call_of(receiver)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def collection_constructor?
|
|
165
|
+
constructor.method?(:new) && core_constant?(constructor.receiver) &&
|
|
166
|
+
COLLECTION_CLASSES.include?(constructor.receiver.short_name)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def constructor_without_entries?
|
|
170
|
+
constructor.arguments.empty? || constructor.receiver.short_name == :Hash
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|