summaryrefslogtreecommitdiff
path: root/rubocop/code_reuse_helpers.rb
diff options
context:
space:
mode:
authorYorick Peterse <yorickpeterse@gmail.com>2018-08-27 17:30:20 +0200
committerYorick Peterse <yorickpeterse@gmail.com>2018-09-11 15:00:18 +0200
commit91c40973dc620430b173ea2df968587d2a708dff (patch)
treeeb871f8b742d8e165138d699be5e884a2c612b24 /rubocop/code_reuse_helpers.rb
parentc56f2b96159afaf6f1e0831d0e7a756a40568cab (diff)
downloadgitlab-ce-91c40973dc620430b173ea2df968587d2a708dff.tar.gz
Added RuboCop cops to enforce code reuse rules
These Cops enforces the code reuse rules as defined in merge request https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21254.
Diffstat (limited to 'rubocop/code_reuse_helpers.rb')
-rw-r--r--rubocop/code_reuse_helpers.rb156
1 files changed, 156 insertions, 0 deletions
diff --git a/rubocop/code_reuse_helpers.rb b/rubocop/code_reuse_helpers.rb
new file mode 100644
index 00000000000..0929a55d901
--- /dev/null
+++ b/rubocop/code_reuse_helpers.rb
@@ -0,0 +1,156 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module CodeReuseHelpers
+ # Returns true for a `(send const ...)` node.
+ def send_to_constant?(node)
+ node.type == :send && node.children&.first&.type == :const
+ end
+
+ # Returns `true` if the name of the receiving constant ends with a given
+ # `String`.
+ def send_receiver_name_ends_with?(node, suffix)
+ return false unless send_to_constant?(node)
+
+ receiver_name = name_of_receiver(node)
+
+ receiver_name != suffix &&
+ receiver_name.end_with?(suffix)
+ end
+
+ # Returns the file path (as a `String`) for an AST node.
+ def file_path_for_node(node)
+ node.location.expression.source_buffer.name
+ end
+
+ # Returns the name of a constant node.
+ #
+ # Given the AST node `(const nil :Foo)`, this method will return `:Foo`.
+ def name_of_constant(node)
+ node.children[1]
+ end
+
+ # Returns true if the given node resides in app/finders or ee/app/finders.
+ def in_finder?(node)
+ in_directory?(node, 'finders')
+ end
+
+ # Returns true if the given node resides in app/models or ee/app/models.
+ def in_model?(node)
+ in_directory?(node, 'models')
+ end
+
+ # Returns true if the given node resides in app/services or ee/app/services.
+ def in_service_class?(node)
+ in_directory?(node, 'services')
+ end
+
+ # Returns true if the given node resides in app/presenters or
+ # ee/app/presenters.
+ def in_presenter?(node)
+ in_directory?(node, 'presenters')
+ end
+
+ # Returns true if the given node resides in app/serializers or
+ # ee/app/serializers.
+ def in_serializer?(node)
+ in_directory?(node, 'serializers')
+ end
+
+ # Returns true if the given node resides in app/workers or ee/app/workers.
+ def in_worker?(node)
+ in_directory?(node, 'workers')
+ end
+
+ # Returns true if the given node resides in app/controllers or
+ # ee/app/controllers.
+ def in_controller?(node)
+ in_directory?(node, 'controllers')
+ end
+
+ # Returns true if the given node resides in lib/api or ee/lib/api.
+ def in_api?(node)
+ file_path_for_node(node).start_with?(
+ File.join(ce_lib_directory, 'api'),
+ File.join(ee_lib_directory, 'api')
+ )
+ end
+
+ # Returns `true` if the given AST node resides in the given directory,
+ # relative to app and/or ee/app.
+ def in_directory?(node, directory)
+ file_path_for_node(node).start_with?(
+ File.join(ce_app_directory, directory),
+ File.join(ee_app_directory, directory)
+ )
+ end
+
+ # Returns the receiver name of a send node.
+ #
+ # For the AST node `(send (const nil :Foo) ...)` this would return
+ # `'Foo'`.
+ def name_of_receiver(node)
+ name_of_constant(node.children.first).to_s
+ end
+
+ # Yields every defined class method in the given AST node.
+ def each_class_method(node)
+ return to_enum(__method__, node) unless block_given?
+
+ # class << self
+ # def foo
+ # end
+ # end
+ node.each_descendant(:sclass) do |sclass|
+ sclass.each_descendant(:def) do |def_node|
+ yield def_node
+ end
+ end
+
+ # def self.foo
+ # end
+ node.each_descendant(:defs) do |defs_node|
+ yield defs_node
+ end
+ end
+
+ # Yields every send node found in the given AST node.
+ def each_send_node(node, &block)
+ node.each_descendant(:send, &block)
+ end
+
+ # Registers a RuboCop offense for a `(send)` node with a receiver that ends
+ # with a given suffix.
+ #
+ # node - The AST node to check.
+ # suffix - The suffix of the receiver name, such as "Finder".
+ # message - The message to use for the offense.
+ def disallow_send_to(node, suffix, message)
+ each_send_node(node) do |send_node|
+ next unless send_receiver_name_ends_with?(send_node, suffix)
+
+ add_offense(send_node, location: :expression, message: message)
+ end
+ end
+
+ def ce_app_directory
+ File.join(rails_root, 'app')
+ end
+
+ def ee_app_directory
+ File.join(rails_root, 'ee', 'app')
+ end
+
+ def ce_lib_directory
+ File.join(rails_root, 'lib')
+ end
+
+ def ee_lib_directory
+ File.join(rails_root, 'ee', 'lib')
+ end
+
+ def rails_root
+ File.expand_path('..', __dir__)
+ end
+ end
+end