rubocop-discourse 3.6.0 → 3.7.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 +4 -4
- data/.rubocop.yml +7 -0
- data/config/default.yml +28 -0
- data/lib/rubocop/cop/discourse/fabricator_shorthand.rb +52 -0
- data/lib/rubocop/cop/discourse/plugins/call_requires_plugin.rb +71 -0
- data/lib/rubocop/cop/discourse/plugins/namespace_constants.rb +35 -0
- data/lib/rubocop/cop/discourse/plugins/namespace_methods.rb +37 -0
- data/lib/rubocop/cop/discourse/plugins/no_monkey_patching.rb +92 -0
- data/lib/rubocop/cop/discourse/plugins/use_plugin_instance_on.rb +42 -0
- data/lib/rubocop/cop/discourse/plugins/use_require_relative.rb +32 -0
- data/lib/rubocop/cop/discourse_cops.rb +1 -1
- data/lib/rubocop-discourse.rb +2 -0
- data/rubocop-capybara.yml +5 -0
- data/rubocop-core.yml +1 -0
- data/rubocop-discourse.gemspec +4 -1
- data/rubocop-factory_bot.yml +11 -0
- data/rubocop-rspec.yml +0 -15
- data/spec/fixtures/controllers/bad_controller.rb +5 -0
- data/spec/fixtures/controllers/base_controller.rb +11 -0
- data/spec/fixtures/controllers/good_controller.rb +4 -0
- data/spec/fixtures/controllers/inherit_from_outside_controller.rb +5 -0
- data/spec/fixtures/controllers/namespaced_parent_controller.rb +5 -0
- data/spec/fixtures/controllers/no_requires_plugin_controller.rb +5 -0
- data/spec/fixtures/controllers/requires_plugin_controller.rb +6 -0
- data/spec/lib/rubocop/cop/fabricator_shorthand_spec.rb +34 -0
- data/spec/lib/rubocop/cop/plugins/call_requires_plugin_spec.rb +81 -0
- data/spec/lib/rubocop/cop/plugins/namespace_constants_spec.rb +42 -0
- data/spec/lib/rubocop/cop/plugins/namespace_methods_spec.rb +86 -0
- data/spec/lib/rubocop/cop/plugins/no_monkey_patching_spec.rb +93 -0
- data/spec/lib/rubocop/cop/plugins/use_plugin_instance_on_spec.rb +39 -0
- data/spec/lib/rubocop/cop/plugins/use_require_relative_spec.rb +26 -0
- data/stree-compat.yml +8 -0
- metadata +67 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 3d347009b4a77b64450a20ef3afc5ca102dd361c60e3fbde2e5fd0ea976469cd
         | 
| 4 | 
            +
              data.tar.gz: c98b24bd73f783f154320e337e44f6859e050c43ff9219cfa0be6f63a891fc13
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 98c56b4b42703f8fc1fe0b3f68344507e2001d7a71d5530c89b8f21a29b5276fc518542b445e9dd9a902cb30be38ce0d4df18218b2cf08bfb50465411d7c7d77
         | 
| 7 | 
            +
              data.tar.gz: f7f5f50325374fe530a26c20ed113b4cb827ed7541a8a9f6dbae1a81c0dca29be2cc7073c8f543b5200dab2e2238617984a7f4dbd87d838ff8a1d4cfa8da0db6
         | 
    
        data/.rubocop.yml
    CHANGED
    
    
    
        data/config/default.yml
    CHANGED
    
    | @@ -62,3 +62,31 @@ Discourse/NoMixingMultisiteAndStandardSpecs: | |
| 62 62 | 
             
              Patterns:
         | 
| 63 63 | 
             
                - _spec.rb
         | 
| 64 64 | 
             
                - '(?:^|/)spec/'
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            Discourse/Plugins/CallRequiresPlugin:
         | 
| 67 | 
            +
              Enabled: true
         | 
| 68 | 
            +
              Include:
         | 
| 69 | 
            +
                - 'app/controllers/**/*'
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            Discourse/Plugins/UsePluginInstanceOn:
         | 
| 72 | 
            +
              Enabled: true
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            Discourse/Plugins/NamespaceMethods:
         | 
| 75 | 
            +
              Enabled: true
         | 
| 76 | 
            +
              Exclude:
         | 
| 77 | 
            +
                - '**/spec/**/*'
         | 
| 78 | 
            +
                - '**/tasks/**/*.rake'
         | 
| 79 | 
            +
                - '**/db/fixtures/**/*'
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            Discourse/Plugins/NamespaceConstants:
         | 
| 82 | 
            +
              Enabled: true
         | 
| 83 | 
            +
              Exclude:
         | 
| 84 | 
            +
                - '**/spec/**/*'
         | 
| 85 | 
            +
                - '**/tasks/**/*.rake'
         | 
| 86 | 
            +
                - '**/db/fixtures/**/*'
         | 
| 87 | 
            +
             | 
| 88 | 
            +
            Discourse/Plugins/UseRequireRelative:
         | 
| 89 | 
            +
              Enabled: true
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            Discourse/Plugins/NoMonkeyPatching:
         | 
| 92 | 
            +
              Enabled: true
         | 
| @@ -0,0 +1,52 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RuboCop
         | 
| 4 | 
            +
              module Cop
         | 
| 5 | 
            +
                module Discourse
         | 
| 6 | 
            +
                  # When fabricating a record without custom attributes, we can use the
         | 
| 7 | 
            +
                  # fabricator shorthand as long as the identifier matches the fabricator
         | 
| 8 | 
            +
                  # name.
         | 
| 9 | 
            +
                  #
         | 
| 10 | 
            +
                  # @example
         | 
| 11 | 
            +
                  #
         | 
| 12 | 
            +
                  # # bad
         | 
| 13 | 
            +
                  # fab!(:user) { Fabricate(:user) }
         | 
| 14 | 
            +
                  #
         | 
| 15 | 
            +
                  # # good
         | 
| 16 | 
            +
                  # fab!(:user)
         | 
| 17 | 
            +
                  #
         | 
| 18 | 
            +
                  # When using custom attributes or the identifier doesn't match, the
         | 
| 19 | 
            +
                  # shorthand can't be used.
         | 
| 20 | 
            +
                  #
         | 
| 21 | 
            +
                  # @example
         | 
| 22 | 
            +
                  #
         | 
| 23 | 
            +
                  # # good
         | 
| 24 | 
            +
                  # fab!(:user) { Fabricate(:user, trust_level: TrustLevel[0]) }
         | 
| 25 | 
            +
                  #
         | 
| 26 | 
            +
                  # # good
         | 
| 27 | 
            +
                  # fab!(:another_user) { Fabricate(:user) }
         | 
| 28 | 
            +
                  class FabricatorShorthand < Base
         | 
| 29 | 
            +
                    def_node_matcher :offending_fabricator?, <<-MATCHER
         | 
| 30 | 
            +
                      (block
         | 
| 31 | 
            +
                        (send nil? :fab!
         | 
| 32 | 
            +
                          (sym $_identifier))
         | 
| 33 | 
            +
                        (args)
         | 
| 34 | 
            +
                        (send nil? :Fabricate
         | 
| 35 | 
            +
                          (sym $_identifier)))
         | 
| 36 | 
            +
                    MATCHER
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    def on_block(node)
         | 
| 39 | 
            +
                      offending_fabricator?(node) do |identifier|
         | 
| 40 | 
            +
                        add_offense(node, message: message(identifier))
         | 
| 41 | 
            +
                      end
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    private
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    def message(identifier)
         | 
| 47 | 
            +
                      "Use the fabricator shorthand: `fab!(:#{identifier})`"
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
            end
         | 
| @@ -0,0 +1,71 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RuboCop
         | 
| 4 | 
            +
              module Cop
         | 
| 5 | 
            +
                module Discourse
         | 
| 6 | 
            +
                  module Plugins
         | 
| 7 | 
            +
                    # Plugin controllers must call `requires_plugin` to prevent routes from
         | 
| 8 | 
            +
                    # being accessible when the plugin is disabled.
         | 
| 9 | 
            +
                    #
         | 
| 10 | 
            +
                    # @example
         | 
| 11 | 
            +
                    #   # bad
         | 
| 12 | 
            +
                    #   class MyController
         | 
| 13 | 
            +
                    #     def my_action
         | 
| 14 | 
            +
                    #     end
         | 
| 15 | 
            +
                    #   end
         | 
| 16 | 
            +
                    #
         | 
| 17 | 
            +
                    #   # good
         | 
| 18 | 
            +
                    #   class MyController
         | 
| 19 | 
            +
                    #     requires_plugin PLUGIN_NAME
         | 
| 20 | 
            +
                    #     def my_action
         | 
| 21 | 
            +
                    #     end
         | 
| 22 | 
            +
                    #   end
         | 
| 23 | 
            +
                    #
         | 
| 24 | 
            +
                    class CallRequiresPlugin < Base
         | 
| 25 | 
            +
                      MSG =
         | 
| 26 | 
            +
                        "Use `requires_plugin` in controllers to prevent routes from being accessible when plugin is disabled."
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                      def_node_matcher :requires_plugin_present?, <<~MATCHER
         | 
| 29 | 
            +
                        (class _ _
         | 
| 30 | 
            +
                          {
         | 
| 31 | 
            +
                            (begin <(send nil? :requires_plugin _) ...>)
         | 
| 32 | 
            +
                            <(send nil? :requires_plugin _) ...>
         | 
| 33 | 
            +
                          }
         | 
| 34 | 
            +
                        )
         | 
| 35 | 
            +
                      MATCHER
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                      def on_class(node)
         | 
| 38 | 
            +
                        return if requires_plugin_present?(node)
         | 
| 39 | 
            +
                        return if requires_plugin_present_in_parent_classes(node)
         | 
| 40 | 
            +
                        add_offense(node, message: MSG)
         | 
| 41 | 
            +
                      end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                      private
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                      def requires_plugin_present_in_parent_classes(node)
         | 
| 46 | 
            +
                        return unless processed_source.path
         | 
| 47 | 
            +
                        controller_path =
         | 
| 48 | 
            +
                          base_controller_path(node.parent_class&.const_name.to_s)
         | 
| 49 | 
            +
                        return unless controller_path
         | 
| 50 | 
            +
                        Commissioner
         | 
| 51 | 
            +
                          .new([self.class.new(config, @options)])
         | 
| 52 | 
            +
                          .investigate(parse(controller_path.read, controller_path.to_s))
         | 
| 53 | 
            +
                          .offenses
         | 
| 54 | 
            +
                          .empty?
         | 
| 55 | 
            +
                      end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                      def base_controller_path(base_class)
         | 
| 58 | 
            +
                        return if base_class.blank?
         | 
| 59 | 
            +
                        base_path = "#{base_class.underscore}.rb"
         | 
| 60 | 
            +
                        path = Pathname.new("#{processed_source.path}/../").cleanpath
         | 
| 61 | 
            +
                        until path.root?
         | 
| 62 | 
            +
                          controller_path = path.join(base_path)
         | 
| 63 | 
            +
                          return controller_path if controller_path.exist?
         | 
| 64 | 
            +
                          path = path.join("..").cleanpath
         | 
| 65 | 
            +
                        end
         | 
| 66 | 
            +
                      end
         | 
| 67 | 
            +
                    end
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
            end
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RuboCop
         | 
| 4 | 
            +
              module Cop
         | 
| 5 | 
            +
                module Discourse
         | 
| 6 | 
            +
                  module Plugins
         | 
| 7 | 
            +
                    # Constants must be defined inside the plugin namespace (module or class).
         | 
| 8 | 
            +
                    #
         | 
| 9 | 
            +
                    # @example
         | 
| 10 | 
            +
                    #   # bad
         | 
| 11 | 
            +
                    #   MY_CONSTANT = :value
         | 
| 12 | 
            +
                    #
         | 
| 13 | 
            +
                    #   # good
         | 
| 14 | 
            +
                    #   module MyPlugin
         | 
| 15 | 
            +
                    #     MY_CONSTANT = :value
         | 
| 16 | 
            +
                    #   end
         | 
| 17 | 
            +
                    #
         | 
| 18 | 
            +
                    class NamespaceConstants < Base
         | 
| 19 | 
            +
                      MSG = "Don’t define constants outside a class or a module."
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                      def on_casgn(node)
         | 
| 22 | 
            +
                        return if inside_namespace?(node)
         | 
| 23 | 
            +
                        add_offense(node, message: MSG)
         | 
| 24 | 
            +
                      end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                      private
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                      def inside_namespace?(node)
         | 
| 29 | 
            +
                        node.each_ancestor.detect { _1.class_type? || _1.module_type? }
         | 
| 30 | 
            +
                      end
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
| @@ -0,0 +1,37 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RuboCop
         | 
| 4 | 
            +
              module Cop
         | 
| 5 | 
            +
                module Discourse
         | 
| 6 | 
            +
                  module Plugins
         | 
| 7 | 
            +
                    # Methods must be defined inside the plugin namespace (module or class).
         | 
| 8 | 
            +
                    #
         | 
| 9 | 
            +
                    # @example
         | 
| 10 | 
            +
                    #   # bad
         | 
| 11 | 
            +
                    #   def my_method
         | 
| 12 | 
            +
                    #   end
         | 
| 13 | 
            +
                    #
         | 
| 14 | 
            +
                    #   # good
         | 
| 15 | 
            +
                    #   module MyPlugin
         | 
| 16 | 
            +
                    #     def my_method
         | 
| 17 | 
            +
                    #     end
         | 
| 18 | 
            +
                    #   end
         | 
| 19 | 
            +
                    #
         | 
| 20 | 
            +
                    class NamespaceMethods < Base
         | 
| 21 | 
            +
                      MSG = "Don’t define methods outside a class or a module."
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                      def on_def(node)
         | 
| 24 | 
            +
                        return if inside_namespace?(node)
         | 
| 25 | 
            +
                        add_offense(node, message: MSG)
         | 
| 26 | 
            +
                      end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                      private
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                      def inside_namespace?(node)
         | 
| 31 | 
            +
                        node.each_ancestor.detect { _1.class_type? || _1.module_type? }
         | 
| 32 | 
            +
                      end
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
            end
         | 
| @@ -0,0 +1,92 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RuboCop
         | 
| 4 | 
            +
              module Cop
         | 
| 5 | 
            +
                module Discourse
         | 
| 6 | 
            +
                  module Plugins
         | 
| 7 | 
            +
                    # Don’t monkey-patch classes directly in `plugin.rb`. Instead, define
         | 
| 8 | 
            +
                    # additional methods in a dedicated mixin (an ActiveSupport concern for
         | 
| 9 | 
            +
                    # example) and use `prepend` (this allows calling `super` from the mixin).
         | 
| 10 | 
            +
                    #
         | 
| 11 | 
            +
                    # If you’re just adding new methods to an existing serializer, then use
         | 
| 12 | 
            +
                    # `add_to_serializer` instead.
         | 
| 13 | 
            +
                    #
         | 
| 14 | 
            +
                    # @example generic monkey-patching
         | 
| 15 | 
            +
                    #   # bad
         | 
| 16 | 
            +
                    #   ::Topic.class_eval do
         | 
| 17 | 
            +
                    #     has_many :new_items
         | 
| 18 | 
            +
                    #
         | 
| 19 | 
            +
                    #     def new_method
         | 
| 20 | 
            +
                    #     end
         | 
| 21 | 
            +
                    #   end
         | 
| 22 | 
            +
                    #
         | 
| 23 | 
            +
                    #   # good
         | 
| 24 | 
            +
                    #   module MyPlugin::TopicExtension
         | 
| 25 | 
            +
                    #     extend ActiveSupport::Concern
         | 
| 26 | 
            +
                    #
         | 
| 27 | 
            +
                    #     prepended do
         | 
| 28 | 
            +
                    #       has_many :new_items
         | 
| 29 | 
            +
                    #     end
         | 
| 30 | 
            +
                    #
         | 
| 31 | 
            +
                    #     def new_method
         | 
| 32 | 
            +
                    #     end
         | 
| 33 | 
            +
                    #   end
         | 
| 34 | 
            +
                    #
         | 
| 35 | 
            +
                    #   reloadable_patch { ::Topic.prepend(MyPlugin::TopicExtension) }
         | 
| 36 | 
            +
                    #
         | 
| 37 | 
            +
                    # @example for serializers
         | 
| 38 | 
            +
                    #   # bad
         | 
| 39 | 
            +
                    #   UserSerializer.class_eval do
         | 
| 40 | 
            +
                    #     def new_method
         | 
| 41 | 
            +
                    #       do_processing
         | 
| 42 | 
            +
                    #     end
         | 
| 43 | 
            +
                    #   end
         | 
| 44 | 
            +
                    #
         | 
| 45 | 
            +
                    #   # good
         | 
| 46 | 
            +
                    #   add_to_serializer(:user, :new_method) { do_processing }
         | 
| 47 | 
            +
                    #
         | 
| 48 | 
            +
                    class NoMonkeyPatching < Base
         | 
| 49 | 
            +
                      MSG =
         | 
| 50 | 
            +
                        "Don’t reopen existing classes. Instead, create a mixin and use `prepend`."
         | 
| 51 | 
            +
                      MSG_CLASS_EVAL =
         | 
| 52 | 
            +
                        "Don’t call `class_eval`. Instead, create a mixin and use `prepend`."
         | 
| 53 | 
            +
                      MSG_CLASS_EVAL_SERIALIZERS =
         | 
| 54 | 
            +
                        "Don’t call `class_eval` on a serializer. If you’re adding new methods, use `add_to_serializer`. Otherwise, create a mixin and use `prepend`."
         | 
| 55 | 
            +
                      MSG_SERIALIZERS =
         | 
| 56 | 
            +
                        "Don’t reopen serializers. Instead, use `add_to_serializer`."
         | 
| 57 | 
            +
                      RESTRICT_ON_SEND = [:class_eval].freeze
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                      def_node_matcher :existing_class?, <<~MATCHER
         | 
| 60 | 
            +
                        (class (const (cbase) _) ...)
         | 
| 61 | 
            +
                      MATCHER
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                      def_node_matcher :serializer?, <<~MATCHER
         | 
| 64 | 
            +
                        ({class send} (const _ /Serializer$/) ...)
         | 
| 65 | 
            +
                      MATCHER
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                      def on_send(node)
         | 
| 68 | 
            +
                        if serializer?(node)
         | 
| 69 | 
            +
                          return add_offense(node, message: MSG_CLASS_EVAL_SERIALIZERS)
         | 
| 70 | 
            +
                        end
         | 
| 71 | 
            +
                        add_offense(node, message: MSG_CLASS_EVAL)
         | 
| 72 | 
            +
                      end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                      def on_class(node)
         | 
| 75 | 
            +
                        return unless in_plugin_rb_file?
         | 
| 76 | 
            +
                        return unless existing_class?(node)
         | 
| 77 | 
            +
                        if serializer?(node)
         | 
| 78 | 
            +
                          return add_offense(node, message: MSG_SERIALIZERS)
         | 
| 79 | 
            +
                        end
         | 
| 80 | 
            +
                        add_offense(node, message: MSG)
         | 
| 81 | 
            +
                      end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                      private
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                      def in_plugin_rb_file?
         | 
| 86 | 
            +
                        processed_source.path.split("/").last == "plugin.rb"
         | 
| 87 | 
            +
                      end
         | 
| 88 | 
            +
                    end
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
            end
         | 
| @@ -0,0 +1,42 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RuboCop
         | 
| 4 | 
            +
              module Cop
         | 
| 5 | 
            +
                module Discourse
         | 
| 6 | 
            +
                  module Plugins
         | 
| 7 | 
            +
                    # Using `DiscourseEvent.on` leaves the handler enabled when the plugin is disabled.
         | 
| 8 | 
            +
                    #
         | 
| 9 | 
            +
                    # @example
         | 
| 10 | 
            +
                    #   # bad
         | 
| 11 | 
            +
                    #   DiscourseEvent.on(:event) { do_something }
         | 
| 12 | 
            +
                    #
         | 
| 13 | 
            +
                    #   # good
         | 
| 14 | 
            +
                    #   on(:event) { do_something }
         | 
| 15 | 
            +
                    #
         | 
| 16 | 
            +
                    class UsePluginInstanceOn < Base
         | 
| 17 | 
            +
                      MSG =
         | 
| 18 | 
            +
                        "Use `on` instead of `DiscourseEvent.on` as the latter will listen to events even if the plugin is disabled."
         | 
| 19 | 
            +
                      NOT_OUTSIDE_PLUGIN_RB =
         | 
| 20 | 
            +
                        "Don’t call `DiscourseEvent.on` outside `plugin.rb`."
         | 
| 21 | 
            +
                      RESTRICT_ON_SEND = [:on].freeze
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                      def_node_matcher :discourse_event_on?, <<~MATCHER
         | 
| 24 | 
            +
                        (send (const nil? :DiscourseEvent) :on _)
         | 
| 25 | 
            +
                      MATCHER
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                      def on_send(node)
         | 
| 28 | 
            +
                        return unless discourse_event_on?(node)
         | 
| 29 | 
            +
                        return add_offense(node, message: MSG) if in_plugin_rb_file?
         | 
| 30 | 
            +
                        add_offense(node, message: NOT_OUTSIDE_PLUGIN_RB)
         | 
| 31 | 
            +
                      end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                      private
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                      def in_plugin_rb_file?
         | 
| 36 | 
            +
                        processed_source.path.split("/").last == "plugin.rb"
         | 
| 37 | 
            +
                      end
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
            end
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RuboCop
         | 
| 4 | 
            +
              module Cop
         | 
| 5 | 
            +
                module Discourse
         | 
| 6 | 
            +
                  module Plugins
         | 
| 7 | 
            +
                    # Use `require_relative` to load dependencies.
         | 
| 8 | 
            +
                    #
         | 
| 9 | 
            +
                    # @example
         | 
| 10 | 
            +
                    #   # bad
         | 
| 11 | 
            +
                    #   load File.expand_path("../lib/my_file.rb", __FILE__)
         | 
| 12 | 
            +
                    #
         | 
| 13 | 
            +
                    #   # good
         | 
| 14 | 
            +
                    #   require_relative "lib/my_file"
         | 
| 15 | 
            +
                    #
         | 
| 16 | 
            +
                    class UseRequireRelative < Base
         | 
| 17 | 
            +
                      MSG = "Use `require_relative` instead of `load`."
         | 
| 18 | 
            +
                      RESTRICT_ON_SEND = [:load].freeze
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                      def_node_matcher :load_called?, <<~MATCHER
         | 
| 21 | 
            +
                        (send nil? :load _)
         | 
| 22 | 
            +
                      MATCHER
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                      def on_send(node)
         | 
| 25 | 
            +
                        return unless load_called?(node)
         | 
| 26 | 
            +
                        add_offense(node, message: MSG)
         | 
| 27 | 
            +
                      end
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
            end
         | 
    
        data/lib/rubocop-discourse.rb
    CHANGED
    
    
    
        data/rubocop-core.yml
    CHANGED
    
    
    
        data/rubocop-discourse.gemspec
    CHANGED
    
    | @@ -2,7 +2,7 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            Gem::Specification.new do |s|
         | 
| 4 4 | 
             
              s.name = "rubocop-discourse"
         | 
| 5 | 
            -
              s.version = "3. | 
| 5 | 
            +
              s.version = "3.7.0"
         | 
| 6 6 | 
             
              s.summary = "Custom rubocop cops used by Discourse"
         | 
| 7 7 | 
             
              s.authors = ["Discourse Team"]
         | 
| 8 8 | 
             
              s.license = "MIT"
         | 
| @@ -11,8 +11,11 @@ Gem::Specification.new do |s| | |
| 11 11 | 
             
              s.files = `git ls-files`.split($/)
         | 
| 12 12 | 
             
              s.require_paths = ["lib"]
         | 
| 13 13 |  | 
| 14 | 
            +
              s.add_runtime_dependency "activesupport", ">= 6.1"
         | 
| 14 15 | 
             
              s.add_runtime_dependency "rubocop", ">= 1.59.0"
         | 
| 15 16 | 
             
              s.add_runtime_dependency "rubocop-rspec", ">= 2.25.0"
         | 
| 17 | 
            +
              s.add_runtime_dependency "rubocop-factory_bot", ">= 2.0.0"
         | 
| 18 | 
            +
              s.add_runtime_dependency "rubocop-capybara", ">= 2.0.0"
         | 
| 16 19 |  | 
| 17 20 | 
             
              s.add_development_dependency "rake", "~> 13.1.0"
         | 
| 18 21 | 
             
              s.add_development_dependency "rspec", "~> 3.12.0"
         | 
    
        data/rubocop-rspec.yml
    CHANGED
    
    | @@ -223,23 +223,8 @@ RSpec/VoidExpect: | |
| 223 223 | 
             
            RSpec/Yield:
         | 
| 224 224 | 
             
              Enabled: true
         | 
| 225 225 |  | 
| 226 | 
            -
            Capybara/CurrentPathExpectation:
         | 
| 227 | 
            -
              Enabled: true
         | 
| 228 | 
            -
             | 
| 229 226 | 
             
            RSpec/Capybara/FeatureMethods:
         | 
| 230 227 | 
             
              Enabled: true
         | 
| 231 228 |  | 
| 232 | 
            -
            Capybara/VisibilityMatcher:
         | 
| 233 | 
            -
              Enabled: true
         | 
| 234 | 
            -
             | 
| 235 | 
            -
            FactoryBot/AttributeDefinedStatically:
         | 
| 236 | 
            -
              Enabled: true
         | 
| 237 | 
            -
             | 
| 238 | 
            -
            FactoryBot/CreateList:
         | 
| 239 | 
            -
              Enabled: true
         | 
| 240 | 
            -
             | 
| 241 | 
            -
            FactoryBot/FactoryClassName:
         | 
| 242 | 
            -
              Enabled: true
         | 
| 243 | 
            -
             | 
| 244 229 | 
             
            RSpec/Rails/HttpStatus:
         | 
| 245 230 | 
             
              Enabled: true
         | 
| @@ -0,0 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class BadController < NoRequiresPluginController
         | 
| 4 | 
            +
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Discourse/Plugins/CallRequiresPlugin: Use `requires_plugin` in controllers to prevent routes from being accessible when plugin is disabled.
         | 
| 5 | 
            +
            end
         | 
| @@ -0,0 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class InheritFromOutsideController < ApplicationController
         | 
| 4 | 
            +
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Discourse/Plugins/CallRequiresPlugin: Use `requires_plugin` in controllers to prevent routes from being accessible when plugin is disabled.
         | 
| 5 | 
            +
            end
         | 
| @@ -0,0 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class InheritFromOutsideController < MyPlugin::ApplicationController
         | 
| 4 | 
            +
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Discourse/Plugins/CallRequiresPlugin: Use `requires_plugin` in controllers to prevent routes from being accessible when plugin is disabled.
         | 
| 5 | 
            +
            end
         | 
| @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "spec_helper"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            describe RuboCop::Cop::Discourse::FabricatorShorthand, :config do
         | 
| 6 | 
            +
              subject(:cop) { described_class.new(config) }
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              let(:config) { RuboCop::Config.new }
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              it "registers an offense when not using the fabricator shorthand" do
         | 
| 11 | 
            +
                expect_offense(<<~RUBY)
         | 
| 12 | 
            +
                  RSpec.describe "Foo" do
         | 
| 13 | 
            +
                    fab!(:foo) { Fabricate(:foo) }
         | 
| 14 | 
            +
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Discourse/FabricatorShorthand: Use the fabricator shorthand: `fab!(:foo)`
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                RUBY
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              it "does not register an offense when the fabricator has attributes" do
         | 
| 20 | 
            +
                expect_no_offenses(<<~RUBY)
         | 
| 21 | 
            +
                  RSpec.describe "Foo" do
         | 
| 22 | 
            +
                    fab!(:foo) { Fabricate(:foo, bar: 1) }
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                RUBY
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              it "does not register an offense when the identifier doesn't match" do
         | 
| 28 | 
            +
                expect_no_offenses(<<~RUBY)
         | 
| 29 | 
            +
                  RSpec.describe "Foo" do
         | 
| 30 | 
            +
                    fab!(:bar) { Fabricate(:foo) }
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                RUBY
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
            end
         | 
| @@ -0,0 +1,81 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "spec_helper"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            RSpec.describe RuboCop::Cop::Discourse::Plugins::CallRequiresPlugin, :config do
         | 
| 6 | 
            +
              subject(:cop) { described_class.new(config) }
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              let(:config) { RuboCop::Config.new }
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              context "when `requires_plugin` is missing" do
         | 
| 11 | 
            +
                it "registers an offense" do
         | 
| 12 | 
            +
                  expect_offense(<<~RUBY)
         | 
| 13 | 
            +
                    class MyController < ApplicationController
         | 
| 14 | 
            +
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Discourse/Plugins/CallRequiresPlugin: Use `requires_plugin` in controllers to prevent routes from being accessible when plugin is disabled.
         | 
| 15 | 
            +
                      requires_login
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
                  RUBY
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              context "when `requires_plugin` is not missing" do
         | 
| 22 | 
            +
                it "does not register an offense" do
         | 
| 23 | 
            +
                  expect_no_offenses(<<~RUBY)
         | 
| 24 | 
            +
                    class MyController
         | 
| 25 | 
            +
                      requires_plugin MyPlugin::PLUGIN_NAME
         | 
| 26 | 
            +
                      requires_login
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
                  RUBY
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              context "when inheriting" do
         | 
| 33 | 
            +
                let(:controllers_path) do
         | 
| 34 | 
            +
                  Pathname.new("#{__dir__}/../../../../fixtures/controllers").cleanpath
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                before do
         | 
| 38 | 
            +
                  # As we’re providing real files, we need to get rid of the default config
         | 
| 39 | 
            +
                  # restricting the cop to `app/controllers/*`
         | 
| 40 | 
            +
                  configuration.for_cop(cop).delete("Include")
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                context "when `requires_plugin` is called in a parent controller" do
         | 
| 44 | 
            +
                  let(:good_controller) { controllers_path.join("good_controller.rb") }
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  it "does not register an offense" do
         | 
| 47 | 
            +
                    expect_no_offenses(good_controller.read, good_controller.to_s)
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                context "when `requires_plugin` is not called in a parent controller" do
         | 
| 52 | 
            +
                  let(:bad_controller) { controllers_path.join("bad_controller.rb") }
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  it "registers an offense" do
         | 
| 55 | 
            +
                    expect_offense(bad_controller.read, bad_controller.to_s)
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                context "when parent controller can’t be located" do
         | 
| 60 | 
            +
                  context "when parent controller is namespaced" do
         | 
| 61 | 
            +
                    let(:controller) do
         | 
| 62 | 
            +
                      controllers_path.join("namespaced_parent_controller.rb")
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    it "registers an offense" do
         | 
| 66 | 
            +
                      expect_offense(controller.read, controller.to_s)
         | 
| 67 | 
            +
                    end
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  context "when parent controller is not namespaced" do
         | 
| 71 | 
            +
                    let(:controller) do
         | 
| 72 | 
            +
                      controllers_path.join("inherit_from_outside_controller.rb")
         | 
| 73 | 
            +
                    end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                    it "registers an offense" do
         | 
| 76 | 
            +
                      expect_offense(controller.read, controller.to_s)
         | 
| 77 | 
            +
                    end
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
              end
         | 
| 81 | 
            +
            end
         | 
| @@ -0,0 +1,42 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "spec_helper"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            RSpec.describe RuboCop::Cop::Discourse::Plugins::NamespaceConstants, :config do
         | 
| 6 | 
            +
              subject(:cop) { described_class.new(config) }
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              let(:config) { RuboCop::Config.new }
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              context "when defining a constant outside any namespace" do
         | 
| 11 | 
            +
                it "registers an offense" do
         | 
| 12 | 
            +
                  expect_offense(<<~RUBY)
         | 
| 13 | 
            +
                    MY_CONSTANT = "my_value"
         | 
| 14 | 
            +
                    ^^^^^^^^^^^^^^^^^^^^^^^^ Discourse/Plugins/NamespaceConstants: Don’t define constants outside a class or a module.
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    class MyClass
         | 
| 17 | 
            +
                      MY_CONSTANT = "my_value"
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
                  RUBY
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              context "when defining a constant inside a class" do
         | 
| 24 | 
            +
                it "does not register an offense" do
         | 
| 25 | 
            +
                  expect_no_offenses(<<~RUBY)
         | 
| 26 | 
            +
                    class MyClass
         | 
| 27 | 
            +
                      MY_CONSTANT = "my_value"
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                  RUBY
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              context "when defining a constant inside a module" do
         | 
| 34 | 
            +
                it "does not register an offense" do
         | 
| 35 | 
            +
                  expect_no_offenses(<<~RUBY)
         | 
| 36 | 
            +
                    module MyModule
         | 
| 37 | 
            +
                      MY_CONSTANT = "my_value"
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
                  RUBY
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
            end
         | 
| @@ -0,0 +1,86 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "spec_helper"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            RSpec.describe RuboCop::Cop::Discourse::Plugins::NamespaceMethods, :config do
         | 
| 6 | 
            +
              subject(:cop) { described_class.new(config) }
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              let(:config) { RuboCop::Config.new }
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              context "when defining a method outside any namespace" do
         | 
| 11 | 
            +
                it "registers an offense" do
         | 
| 12 | 
            +
                  expect_offense(<<~RUBY)
         | 
| 13 | 
            +
                    def my_method
         | 
| 14 | 
            +
                    ^^^^^^^^^^^^^ Discourse/Plugins/NamespaceMethods: Don’t define methods outside a class or a module.
         | 
| 15 | 
            +
                      "my_value"
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    class MyClass
         | 
| 19 | 
            +
                      def my_method
         | 
| 20 | 
            +
                        "my_method"
         | 
| 21 | 
            +
                      end
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                  RUBY
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              context "when defining a method inside a class" do
         | 
| 28 | 
            +
                context "when defining an instance method" do
         | 
| 29 | 
            +
                  it "does not register an offense" do
         | 
| 30 | 
            +
                    expect_no_offenses(<<~RUBY)
         | 
| 31 | 
            +
                      class MyClass
         | 
| 32 | 
            +
                        def my_method
         | 
| 33 | 
            +
                          "my_value"
         | 
| 34 | 
            +
                        end
         | 
| 35 | 
            +
                      end
         | 
| 36 | 
            +
                    RUBY
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                context "when defining a class method" do
         | 
| 41 | 
            +
                  it "does not register an offense" do
         | 
| 42 | 
            +
                    expect_no_offenses(<<~RUBY)
         | 
| 43 | 
            +
                      class MyClass
         | 
| 44 | 
            +
                        class << self
         | 
| 45 | 
            +
                          def my_method
         | 
| 46 | 
            +
                            "my_value"
         | 
| 47 | 
            +
                          end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                          def another_method
         | 
| 50 | 
            +
                            "plop"
         | 
| 51 | 
            +
                          end
         | 
| 52 | 
            +
                        end
         | 
| 53 | 
            +
                      end
         | 
| 54 | 
            +
                    RUBY
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              context "when defining a method inside a module" do
         | 
| 60 | 
            +
                context "when defining an instance method" do
         | 
| 61 | 
            +
                  it "does not register an offense" do
         | 
| 62 | 
            +
                    expect_no_offenses(<<~RUBY)
         | 
| 63 | 
            +
                      module MyModule
         | 
| 64 | 
            +
                        def my_method
         | 
| 65 | 
            +
                          "my_value"
         | 
| 66 | 
            +
                        end
         | 
| 67 | 
            +
                      end
         | 
| 68 | 
            +
                    RUBY
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                context "when defining a class method" do
         | 
| 73 | 
            +
                  it "does not register an offense" do
         | 
| 74 | 
            +
                    expect_no_offenses(<<~RUBY)
         | 
| 75 | 
            +
                      module MyModule
         | 
| 76 | 
            +
                        class << self
         | 
| 77 | 
            +
                          def my_method
         | 
| 78 | 
            +
                            "my_value"
         | 
| 79 | 
            +
                          end
         | 
| 80 | 
            +
                        end
         | 
| 81 | 
            +
                      end
         | 
| 82 | 
            +
                    RUBY
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
            end
         | 
| @@ -0,0 +1,93 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "spec_helper"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            RSpec.describe RuboCop::Cop::Discourse::Plugins::NoMonkeyPatching, :config do
         | 
| 6 | 
            +
              subject(:cop) { described_class.new(config) }
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              let(:config) { RuboCop::Config.new }
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              context "when outside `plugin.rb`" do
         | 
| 11 | 
            +
                it "does not register an offense" do
         | 
| 12 | 
            +
                  expect_no_offenses(<<~RUBY, "my_class.rb")
         | 
| 13 | 
            +
                    class ::MyClass
         | 
| 14 | 
            +
                      def my_method
         | 
| 15 | 
            +
                        "my_value"
         | 
| 16 | 
            +
                      end
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    class AnotherClass
         | 
| 20 | 
            +
                      def my_method
         | 
| 21 | 
            +
                        "my_value"
         | 
| 22 | 
            +
                      end
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                  RUBY
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              context "when inside `plugin.rb`" do
         | 
| 29 | 
            +
                context "when opening an existing class" do
         | 
| 30 | 
            +
                  it "registers an offense" do
         | 
| 31 | 
            +
                    expect_offense(<<~RUBY, "plugin.rb")
         | 
| 32 | 
            +
                      after_initialize do
         | 
| 33 | 
            +
                        module MyPlugin
         | 
| 34 | 
            +
                          class Engine < Rails::Engine
         | 
| 35 | 
            +
                            isolate_namespace MyPlugin
         | 
| 36 | 
            +
                          end
         | 
| 37 | 
            +
                        end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                        class ::Topic
         | 
| 40 | 
            +
                        ^^^^^^^^^^^^^ Discourse/Plugins/NoMonkeyPatching: Don’t reopen existing classes. [...]
         | 
| 41 | 
            +
                          def self.new_method
         | 
| 42 | 
            +
                            :new_value
         | 
| 43 | 
            +
                          end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                          def my_new_method
         | 
| 46 | 
            +
                            "my_new_value"
         | 
| 47 | 
            +
                          end
         | 
| 48 | 
            +
                        end
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
                    RUBY
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                context "when opening an existing serializer" do
         | 
| 55 | 
            +
                  it "registers an offense" do
         | 
| 56 | 
            +
                    expect_offense(<<~RUBY, "plugin.rb")
         | 
| 57 | 
            +
                      class ::TopicSerializer
         | 
| 58 | 
            +
                      ^^^^^^^^^^^^^^^^^^^^^^^ Discourse/Plugins/NoMonkeyPatching: Don’t reopen serializers. [...]
         | 
| 59 | 
            +
                        def new_attribute
         | 
| 60 | 
            +
                          "my_attribute"
         | 
| 61 | 
            +
                        end
         | 
| 62 | 
            +
                      end
         | 
| 63 | 
            +
                    RUBY
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                context "when calling `.class_eval` on a class" do
         | 
| 68 | 
            +
                  it "registers an offense" do
         | 
| 69 | 
            +
                    expect_offense(<<~RUBY)
         | 
| 70 | 
            +
                      User.class_eval do
         | 
| 71 | 
            +
                      ^^^^^^^^^^^^^^^ Discourse/Plugins/NoMonkeyPatching: Don’t call `class_eval`. [...]
         | 
| 72 | 
            +
                        def a_new_method
         | 
| 73 | 
            +
                          :new_value
         | 
| 74 | 
            +
                        end
         | 
| 75 | 
            +
                      end
         | 
| 76 | 
            +
                    RUBY
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                context "when calling `.class_eval` on a serializer" do
         | 
| 81 | 
            +
                  it "registers an offense" do
         | 
| 82 | 
            +
                    expect_offense(<<~RUBY)
         | 
| 83 | 
            +
                      UserSerializer.class_eval do
         | 
| 84 | 
            +
                      ^^^^^^^^^^^^^^^^^^^^^^^^^ Discourse/Plugins/NoMonkeyPatching: Don’t call `class_eval` on a serializer. [...]
         | 
| 85 | 
            +
                        def a_new_method
         | 
| 86 | 
            +
                          :new_value
         | 
| 87 | 
            +
                        end
         | 
| 88 | 
            +
                      end
         | 
| 89 | 
            +
                    RUBY
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
              end
         | 
| 93 | 
            +
            end
         | 
| @@ -0,0 +1,39 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "spec_helper"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            RSpec.describe RuboCop::Cop::Discourse::Plugins::UsePluginInstanceOn, :config do
         | 
| 6 | 
            +
              subject(:cop) { described_class.new(config) }
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              let(:config) { RuboCop::Config.new }
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              context "when outside `plugin.rb`" do
         | 
| 11 | 
            +
                context "when `DiscourseEvent.on` is called" do
         | 
| 12 | 
            +
                  it "registers an offense" do
         | 
| 13 | 
            +
                    expect_offense(<<~RUBY, "another_file.rb")
         | 
| 14 | 
            +
                      DiscourseEvent.on(:topic_status_updated) { do_something }
         | 
| 15 | 
            +
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Discourse/Plugins/UsePluginInstanceOn: Don’t call `DiscourseEvent.on` outside `plugin.rb`.
         | 
| 16 | 
            +
                    RUBY
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              context "when inside `plugin.rb`" do
         | 
| 22 | 
            +
                context "when `DiscourseEvent.on` is called" do
         | 
| 23 | 
            +
                  it "registers an offense" do
         | 
| 24 | 
            +
                    expect_offense(<<~RUBY, "plugin.rb")
         | 
| 25 | 
            +
                      DiscourseEvent.on(:topic_status_updated) { do_something }
         | 
| 26 | 
            +
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Discourse/Plugins/UsePluginInstanceOn: Use `on` instead of `DiscourseEvent.on` [...]
         | 
| 27 | 
            +
                    RUBY
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                context "when `on` is called" do
         | 
| 32 | 
            +
                  it "does not register an offense" do
         | 
| 33 | 
            +
                    expect_no_offenses(<<~RUBY, "plugin.rb")
         | 
| 34 | 
            +
                      on(:topic_status_updated) { do_something }
         | 
| 35 | 
            +
                    RUBY
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
            end
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "spec_helper"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            RSpec.describe RuboCop::Cop::Discourse::Plugins::UseRequireRelative, :config do
         | 
| 6 | 
            +
              subject(:cop) { described_class.new(config) }
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              let(:config) { RuboCop::Config.new }
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              context "when using `load`" do
         | 
| 11 | 
            +
                it "registers an offense" do
         | 
| 12 | 
            +
                  expect_offense(<<~RUBY)
         | 
| 13 | 
            +
                    load File.expand_path("../app/jobs/onceoff/voting_ensure_consistency.rb", __FILE__)
         | 
| 14 | 
            +
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Discourse/Plugins/UseRequireRelative: Use `require_relative` instead of `load`.
         | 
| 15 | 
            +
                  RUBY
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              context "when using `require_relative`" do
         | 
| 20 | 
            +
                it "does not register an offense" do
         | 
| 21 | 
            +
                  expect_no_offenses(<<~RUBY)
         | 
| 22 | 
            +
                    require_relative "app/controllers/encrypt_controller.rb"
         | 
| 23 | 
            +
                  RUBY
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
            end
         | 
    
        data/stree-compat.yml
    CHANGED
    
    | @@ -3,10 +3,13 @@ require: | |
| 3 3 |  | 
| 4 4 | 
             
            inherit_from:
         | 
| 5 5 | 
             
              - ./rubocop-core.yml
         | 
| 6 | 
            +
              - ./rubocop-capybara.yml
         | 
| 7 | 
            +
              - ./rubocop-factory_bot.yml
         | 
| 6 8 | 
             
              - ./rubocop-rspec.yml
         | 
| 7 9 |  | 
| 8 10 | 
             
            AllCops:
         | 
| 9 11 | 
             
              TargetRubyVersion: 3.2
         | 
| 12 | 
            +
              SuggestExtensions: false
         | 
| 10 13 | 
             
              DisabledByDefault: true
         | 
| 11 14 | 
             
              Exclude:
         | 
| 12 15 | 
             
                - 'db/schema.rb'
         | 
| @@ -19,3 +22,8 @@ AllCops: | |
| 19 22 |  | 
| 20 23 | 
             
            Discourse:
         | 
| 21 24 | 
             
              Enabled: true
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            Discourse/FabricatorShorthand:
         | 
| 27 | 
            +
              Enabled: true
         | 
| 28 | 
            +
              Include:
         | 
| 29 | 
            +
                - 'spec/**/*_spec.rb'
         | 
    
        metadata
    CHANGED
    
    | @@ -1,15 +1,29 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: rubocop-discourse
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 3. | 
| 4 | 
            +
              version: 3.7.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Discourse Team
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2024-02-28 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: activesupport
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - ">="
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '6.1'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - ">="
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '6.1'
         | 
| 13 27 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 28 | 
             
              name: rubocop
         | 
| 15 29 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -38,6 +52,34 @@ dependencies: | |
| 38 52 | 
             
                - - ">="
         | 
| 39 53 | 
             
                  - !ruby/object:Gem::Version
         | 
| 40 54 | 
             
                    version: 2.25.0
         | 
| 55 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 56 | 
            +
              name: rubocop-factory_bot
         | 
| 57 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 58 | 
            +
                requirements:
         | 
| 59 | 
            +
                - - ">="
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: 2.0.0
         | 
| 62 | 
            +
              type: :runtime
         | 
| 63 | 
            +
              prerelease: false
         | 
| 64 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 65 | 
            +
                requirements:
         | 
| 66 | 
            +
                - - ">="
         | 
| 67 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 68 | 
            +
                    version: 2.0.0
         | 
| 69 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 70 | 
            +
              name: rubocop-capybara
         | 
| 71 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 72 | 
            +
                requirements:
         | 
| 73 | 
            +
                - - ">="
         | 
| 74 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 75 | 
            +
                    version: 2.0.0
         | 
| 76 | 
            +
              type: :runtime
         | 
| 77 | 
            +
              prerelease: false
         | 
| 78 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 79 | 
            +
                requirements:
         | 
| 80 | 
            +
                - - ">="
         | 
| 81 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 82 | 
            +
                    version: 2.0.0
         | 
| 41 83 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 42 84 | 
             
              name: rake
         | 
| 43 85 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -82,6 +124,7 @@ files: | |
| 82 124 | 
             
            - config/default.yml
         | 
| 83 125 | 
             
            - default.yml
         | 
| 84 126 | 
             
            - lib/rubocop-discourse.rb
         | 
| 127 | 
            +
            - lib/rubocop/cop/discourse/fabricator_shorthand.rb
         | 
| 85 128 | 
             
            - lib/rubocop/cop/discourse/no_add_reference_active_record_migrations.rb
         | 
| 86 129 | 
             
            - lib/rubocop/cop/discourse/no_chdir.rb
         | 
| 87 130 | 
             
            - lib/rubocop/cop/discourse/no_direct_multisite_manipulation.rb
         | 
| @@ -93,19 +136,41 @@ files: | |
| 93 136 | 
             
            - lib/rubocop/cop/discourse/no_time_new_without_args.rb
         | 
| 94 137 | 
             
            - lib/rubocop/cop/discourse/no_uri_escape_encode.rb
         | 
| 95 138 | 
             
            - lib/rubocop/cop/discourse/only_top_level_multisite_specs.rb
         | 
| 139 | 
            +
            - lib/rubocop/cop/discourse/plugins/call_requires_plugin.rb
         | 
| 140 | 
            +
            - lib/rubocop/cop/discourse/plugins/namespace_constants.rb
         | 
| 141 | 
            +
            - lib/rubocop/cop/discourse/plugins/namespace_methods.rb
         | 
| 142 | 
            +
            - lib/rubocop/cop/discourse/plugins/no_monkey_patching.rb
         | 
| 143 | 
            +
            - lib/rubocop/cop/discourse/plugins/use_plugin_instance_on.rb
         | 
| 144 | 
            +
            - lib/rubocop/cop/discourse/plugins/use_require_relative.rb
         | 
| 96 145 | 
             
            - lib/rubocop/cop/discourse/time_eq_matcher.rb
         | 
| 97 146 | 
             
            - lib/rubocop/cop/discourse_cops.rb
         | 
| 98 147 | 
             
            - lib/rubocop/discourse.rb
         | 
| 99 148 | 
             
            - lib/rubocop/discourse/inject.rb
         | 
| 149 | 
            +
            - rubocop-capybara.yml
         | 
| 100 150 | 
             
            - rubocop-core.yml
         | 
| 101 151 | 
             
            - rubocop-discourse.gemspec
         | 
| 152 | 
            +
            - rubocop-factory_bot.yml
         | 
| 102 153 | 
             
            - rubocop-layout.yml
         | 
| 103 154 | 
             
            - rubocop-rspec.yml
         | 
| 155 | 
            +
            - spec/fixtures/controllers/bad_controller.rb
         | 
| 156 | 
            +
            - spec/fixtures/controllers/base_controller.rb
         | 
| 157 | 
            +
            - spec/fixtures/controllers/good_controller.rb
         | 
| 158 | 
            +
            - spec/fixtures/controllers/inherit_from_outside_controller.rb
         | 
| 159 | 
            +
            - spec/fixtures/controllers/namespaced_parent_controller.rb
         | 
| 160 | 
            +
            - spec/fixtures/controllers/no_requires_plugin_controller.rb
         | 
| 161 | 
            +
            - spec/fixtures/controllers/requires_plugin_controller.rb
         | 
| 162 | 
            +
            - spec/lib/rubocop/cop/fabricator_shorthand_spec.rb
         | 
| 104 163 | 
             
            - spec/lib/rubocop/cop/no_add_reference_active_record_migrations_spec.rb
         | 
| 105 164 | 
             
            - spec/lib/rubocop/cop/no_mixing_multisite_and_standard_specs_spec.rb
         | 
| 106 165 | 
             
            - spec/lib/rubocop/cop/no_mocking_jobs_enqueue_spec.rb
         | 
| 107 166 | 
             
            - spec/lib/rubocop/cop/no_reset_column_information_migrations_spec.rb
         | 
| 108 167 | 
             
            - spec/lib/rubocop/cop/only_top_level_multisite_specs_spec.rb
         | 
| 168 | 
            +
            - spec/lib/rubocop/cop/plugins/call_requires_plugin_spec.rb
         | 
| 169 | 
            +
            - spec/lib/rubocop/cop/plugins/namespace_constants_spec.rb
         | 
| 170 | 
            +
            - spec/lib/rubocop/cop/plugins/namespace_methods_spec.rb
         | 
| 171 | 
            +
            - spec/lib/rubocop/cop/plugins/no_monkey_patching_spec.rb
         | 
| 172 | 
            +
            - spec/lib/rubocop/cop/plugins/use_plugin_instance_on_spec.rb
         | 
| 173 | 
            +
            - spec/lib/rubocop/cop/plugins/use_require_relative_spec.rb
         | 
| 109 174 | 
             
            - spec/lib/rubocop/cop/time_eq_matcher_spec.rb
         | 
| 110 175 | 
             
            - spec/spec_helper.rb
         | 
| 111 176 | 
             
            - stree-compat.yml
         |