Commit 9ae92b8c authored by Lin Jen-Shin's avatar Lin Jen-Shin

Add cop to make sure we don't use ivar in a module

parent 4cadf22e
# rubocop:disable Cop/ModuleWithInstanceVariables
module BoardsResponses module BoardsResponses
def authorize_read_list def authorize_read_list
authorize_action_for!(board.parent, :read_list) authorize_action_for!(board.parent, :read_list)
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module CreatesCommit module CreatesCommit
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
module CycleAnalyticsParams module CycleAnalyticsParams
extend ActiveSupport::Concern extend ActiveSupport::Concern
# rubocop:disable Cop/ModuleWithInstanceVariables
def options(params) def options(params)
@options ||= { from: start_date(params), current_user: current_user } @options ||= { from: start_date(params), current_user: current_user }
end end
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module IssuableActions module IssuableActions
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module IssuableCollections module IssuableCollections
extend ActiveSupport::Concern extend ActiveSupport::Concern
include SortingHelper include SortingHelper
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module IssuesAction module IssuesAction
extend ActiveSupport::Concern extend ActiveSupport::Concern
include IssuableCollections include IssuableCollections
......
...@@ -90,6 +90,7 @@ module LfsRequest ...@@ -90,6 +90,7 @@ module LfsRequest
has_authentication_ability?(:build_download_code) && can?(user, :build_download_code, project) has_authentication_ability?(:build_download_code) && can?(user, :build_download_code, project)
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def storage_project def storage_project
@storage_project ||= begin @storage_project ||= begin
result = project result = project
...@@ -103,6 +104,7 @@ module LfsRequest ...@@ -103,6 +104,7 @@ module LfsRequest
end end
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def objects def objects
@objects ||= (params[:objects] || []).to_a @objects ||= (params[:objects] || []).to_a
end end
......
...@@ -76,6 +76,7 @@ module MembershipActions ...@@ -76,6 +76,7 @@ module MembershipActions
end end
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def source_type def source_type
@source_type ||= membershipable.class.to_s.humanize(capitalize: false) @source_type ||= membershipable.class.to_s.humanize(capitalize: false)
end end
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module MergeRequestsAction module MergeRequestsAction
extend ActiveSupport::Concern extend ActiveSupport::Concern
include IssuableCollections include IssuableCollections
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module MilestoneActions module MilestoneActions
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module NotesActions module NotesActions
include RendersNotes include RendersNotes
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
...@@ -13,6 +13,7 @@ module OauthApplications ...@@ -13,6 +13,7 @@ module OauthApplications
end end
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def load_scopes def load_scopes
@scopes = Doorkeeper.configuration.scopes @scopes = Doorkeeper.configuration.scopes
end end
......
module RendersCommits module RendersCommits
# rubocop:disable Cop/ModuleWithInstanceVariables
def prepare_commits_for_rendering(commits) def prepare_commits_for_rendering(commits)
Banzai::CommitRenderer.render(commits, @project, current_user) Banzai::CommitRenderer.render(commits, @project, current_user)
......
module RendersNotes module RendersNotes
# rubocop:disable Cop/ModuleWithInstanceVariables
def prepare_notes_for_rendering(notes, noteable = nil) def prepare_notes_for_rendering(notes, noteable = nil)
preload_noteable_for_regular_notes(notes) preload_noteable_for_regular_notes(notes)
preload_max_access_for_authors(notes, @project) preload_max_access_for_authors(notes, @project)
......
...@@ -17,6 +17,7 @@ module RequiresWhitelistedMonitoringClient ...@@ -17,6 +17,7 @@ module RequiresWhitelistedMonitoringClient
ip_whitelist.any? { |e| e.include?(Gitlab::RequestContext.client_ip) } ip_whitelist.any? { |e| e.include?(Gitlab::RequestContext.client_ip) }
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def ip_whitelist def ip_whitelist
@ip_whitelist ||= Settings.monitoring.ip_whitelist.map(&IPAddr.method(:new)) @ip_whitelist ||= Settings.monitoring.ip_whitelist.map(&IPAddr.method(:new))
end end
......
...@@ -65,6 +65,7 @@ module ServiceParams ...@@ -65,6 +65,7 @@ module ServiceParams
# Parameters to ignore if no value is specified # Parameters to ignore if no value is specified
FILTER_BLANK_PARAMS = [:password].freeze FILTER_BLANK_PARAMS = [:password].freeze
# rubocop:disable Cop/ModuleWithInstanceVariables
def service_params def service_params
dynamic_params = @service.event_channel_names + @service.event_names dynamic_params = @service.event_channel_names + @service.event_names
service_params = params.permit(:id, service: ALLOWED_PARAMS_CE + dynamic_params) service_params = params.permit(:id, service: ALLOWED_PARAMS_CE + dynamic_params)
......
...@@ -4,6 +4,7 @@ module SnippetsActions ...@@ -4,6 +4,7 @@ module SnippetsActions
def edit def edit
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def raw def raw
disposition = params[:inline] == 'false' ? 'attachment' : 'inline' disposition = params[:inline] == 'false' ? 'attachment' : 'inline'
......
...@@ -17,6 +17,7 @@ module SpammableActions ...@@ -17,6 +17,7 @@ module SpammableActions
private private
# rubocop:disable Cop/ModuleWithInstanceVariables
def ensure_spam_config_loaded! def ensure_spam_config_loaded!
return @spam_config_loaded if defined?(@spam_config_loaded) return @spam_config_loaded if defined?(@spam_config_loaded)
......
...@@ -11,6 +11,7 @@ module ToggleSubscriptionAction ...@@ -11,6 +11,7 @@ module ToggleSubscriptionAction
private private
# rubocop:disable Cop/ModuleWithInstanceVariables
def subscribable_project def subscribable_project
@project || raise(NotImplementedError) @project || raise(NotImplementedError)
end end
......
...@@ -17,6 +17,7 @@ module IgnorableColumn ...@@ -17,6 +17,7 @@ module IgnorableColumn
super.reject { |column| ignored_columns.include?(column.name) } super.reject { |column| ignored_columns.include?(column.name) }
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def ignored_columns def ignored_columns
@ignored_columns ||= Set.new @ignored_columns ||= Set.new
end end
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
# #
# Used by Issue, Note, MergeRequest, and Commit. # Used by Issue, Note, MergeRequest, and Commit.
# #
# # rubocop:disable Cop/ModuleWithInstanceVariables
module Mentionable module Mentionable
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
...@@ -94,6 +94,7 @@ module Milestoneish ...@@ -94,6 +94,7 @@ module Milestoneish
end end
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def memoize_per_user(user, method_name) def memoize_per_user(user, method_name)
@memoized ||= {} @memoized ||= {}
@memoized[method_name] ||= {} @memoized[method_name] ||= {}
......
...@@ -12,6 +12,7 @@ module Noteable ...@@ -12,6 +12,7 @@ module Noteable
# #
# noteable.class # => MergeRequest # noteable.class # => MergeRequest
# noteable.human_class_name # => "merge request" # noteable.human_class_name # => "merge request"
# rubocop:disable Cop/ModuleWithInstanceVariables
def human_class_name def human_class_name
@human_class_name ||= base_class_name.titleize.downcase @human_class_name ||= base_class_name.titleize.downcase
end end
...@@ -34,6 +35,7 @@ module Noteable ...@@ -34,6 +35,7 @@ module Noteable
delegate :find_discussion, to: :discussion_notes delegate :find_discussion, to: :discussion_notes
# rubocop:disable Cop/ModuleWithInstanceVariables
def discussions def discussions
@discussions ||= discussion_notes @discussions ||= discussion_notes
.inc_relations_for_view .inc_relations_for_view
...@@ -46,6 +48,7 @@ module Noteable ...@@ -46,6 +48,7 @@ module Noteable
notes.inc_relations_for_view.grouped_diff_discussions(*args) notes.inc_relations_for_view.grouped_diff_discussions(*args)
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def resolvable_discussions def resolvable_discussions
@resolvable_discussions ||= @resolvable_discussions ||=
if defined?(@discussions) if defined?(@discussions)
...@@ -67,6 +70,7 @@ module Noteable ...@@ -67,6 +70,7 @@ module Noteable
discussions_resolvable? && !discussions_resolved? discussions_resolvable? && !discussions_resolved?
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def discussions_to_be_resolved def discussions_to_be_resolved
@discussions_to_be_resolved ||= resolvable_discussions.select(&:to_be_resolved?) @discussions_to_be_resolved ||= resolvable_discussions.select(&:to_be_resolved?)
end end
......
...@@ -55,6 +55,7 @@ module Participable ...@@ -55,6 +55,7 @@ module Participable
# This method processes attributes of objects in breadth-first order. # This method processes attributes of objects in breadth-first order.
# #
# Returns an Array of User instances. # Returns an Array of User instances.
# rubocop:disable Cop/ModuleWithInstanceVariables
def participants(current_user = nil) def participants(current_user = nil)
@participants ||= Hash.new do |hash, user| @participants ||= Hash.new do |hash, user|
hash[user] = raw_participants(user) hash[user] = raw_participants(user)
......
...@@ -55,6 +55,7 @@ module ProtectedRef ...@@ -55,6 +55,7 @@ module ProtectedRef
private private
# rubocop:disable Cop/ModuleWithInstanceVariables
def ref_matcher def ref_matcher
@ref_matcher ||= ProtectedRefMatcher.new(self) @ref_matcher ||= ProtectedRefMatcher.new(self)
end end
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module RelativePositioning module RelativePositioning
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module ResolvableDiscussion module ResolvableDiscussion
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
# Store object full path in separate table for easy lookup and uniq validation # Store object full path in separate table for easy lookup and uniq validation
# Object must have name and path db fields and respond to parent and parent_changed? methods. # Object must have name and path db fields and respond to parent and parent_changed? methods.
# rubocop:disable Cop/ModuleWithInstanceVariables
module Routable module Routable
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
...@@ -35,7 +35,7 @@ module Spammable ...@@ -35,7 +35,7 @@ module Spammable
end end
def spam? def spam?
@spam spam
end end
def check_for_spam def check_for_spam
......
...@@ -49,6 +49,7 @@ module Storage ...@@ -49,6 +49,7 @@ module Storage
private private
# rubocop:disable Cop/ModuleWithInstanceVariables
def old_repository_storage_paths def old_repository_storage_paths
@old_repository_storage_paths ||= repository_storage_paths @old_repository_storage_paths ||= repository_storage_paths
end end
......
...@@ -17,6 +17,7 @@ module StripAttribute ...@@ -17,6 +17,7 @@ module StripAttribute
strip_attrs.concat(attrs) strip_attrs.concat(attrs)
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def strip_attrs def strip_attrs
@strip_attrs ||= [] @strip_attrs ||= []
end end
......
...@@ -6,6 +6,7 @@ require 'task_list/filter' ...@@ -6,6 +6,7 @@ require 'task_list/filter'
# bugs". # bugs".
# #
# Used by MergeRequest and Issue # Used by MergeRequest and Issue
# rubocop:disable Cop/ModuleWithInstanceVariables
module Taskable module Taskable
COMPLETED = 'completed'.freeze COMPLETED = 'completed'.freeze
INCOMPLETE = 'incomplete'.freeze INCOMPLETE = 'incomplete'.freeze
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# #
# Used by Issue and MergeRequest. # Used by Issue and MergeRequest.
# #
# rubocop:disable Cop/ModuleWithInstanceVariables
module TimeTrackable module TimeTrackable
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
...@@ -2,11 +2,13 @@ module Issues ...@@ -2,11 +2,13 @@ module Issues
module ResolveDiscussions module ResolveDiscussions
attr_reader :merge_request_to_resolve_discussions_of_iid, :discussion_to_resolve_id attr_reader :merge_request_to_resolve_discussions_of_iid, :discussion_to_resolve_id
# rubocop:disable Cop/ModuleWithInstanceVariables
def filter_resolve_discussion_params def filter_resolve_discussion_params
@merge_request_to_resolve_discussions_of_iid ||= params.delete(:merge_request_to_resolve_discussions_of) @merge_request_to_resolve_discussions_of_iid ||= params.delete(:merge_request_to_resolve_discussions_of)
@discussion_to_resolve_id ||= params.delete(:discussion_to_resolve) @discussion_to_resolve_id ||= params.delete(:discussion_to_resolve)
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def merge_request_to_resolve_discussions_of def merge_request_to_resolve_discussions_of
return @merge_request_to_resolve_discussions_of if defined?(@merge_request_to_resolve_discussions_of) return @merge_request_to_resolve_discussions_of if defined?(@merge_request_to_resolve_discussions_of)
...@@ -15,6 +17,7 @@ module Issues ...@@ -15,6 +17,7 @@ module Issues
.find_by(iid: merge_request_to_resolve_discussions_of_iid) .find_by(iid: merge_request_to_resolve_discussions_of_iid)
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def discussions_to_resolve def discussions_to_resolve
return [] unless merge_request_to_resolve_discussions_of return [] unless merge_request_to_resolve_discussions_of
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
# Dependencies: # Dependencies:
# - params with :request # - params with :request
# #
# rubocop:disable Cop/ModuleWithInstanceVariables
module SpamCheckService module SpamCheckService
def filter_spam_check_params def filter_spam_check_params
@request = params.delete(:request) @request = params.delete(:request)
......
...@@ -662,6 +662,7 @@ module SystemNoteService ...@@ -662,6 +662,7 @@ module SystemNoteService
Rack::Utils.escape_html(text) Rack::Utils.escape_html(text)
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def url_helpers def url_helpers
@url_helpers ||= Gitlab::Routing.url_helpers @url_helpers ||= Gitlab::Routing.url_helpers
end end
......
...@@ -8,12 +8,14 @@ module NewIssuable ...@@ -8,12 +8,14 @@ module NewIssuable
user && issuable user && issuable
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def set_user(user_id) def set_user(user_id)
@user = User.find_by(id: user_id) @user = User.find_by(id: user_id)
log_error(User, user_id) unless @user log_error(User, user_id) unless @user
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def set_issuable(issuable_id) def set_issuable(issuable_id)
@issuable = issuable_class.find_by(id: issuable_id) @issuable = issuable_class.find_by(id: issuable_id)
......
...@@ -4,6 +4,7 @@ module LocalCacheRegistryCleanupWithEnsure ...@@ -4,6 +4,7 @@ module LocalCacheRegistryCleanupWithEnsure
LocalStore = LocalStore =
ActiveSupport::Cache::Strategy::LocalCache::LocalStore ActiveSupport::Cache::Strategy::LocalCache::LocalStore
# rubocop:disable Cop/ModuleWithInstanceVariables
def call(env) def call(env)
LocalCacheRegistry.set_cache_for(local_cache_key, LocalStore.new) LocalCacheRegistry.set_cache_for(local_cache_key, LocalStore.new)
response = @app.call(env) response = @app.call(env)
......
...@@ -16,6 +16,7 @@ module RspecProfilingExt ...@@ -16,6 +16,7 @@ module RspecProfilingExt
end end
module Run module Run
# rubocop:disable Cop/ModuleWithInstanceVariables
def example_finished(*args) def example_finished(*args)
super super
rescue => err rescue => err
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
# anyway, and there is no great efficiency gain from just fetching the listed # anyway, and there is no great efficiency gain from just fetching the listed
# attributes with our implementation, so we ignore the additional arguments. # attributes with our implementation, so we ignore the additional arguments.
# #
# rubocop:disable Cop/ModuleWithInstanceVariables
module Rugged module Rugged
class Repository class Repository
module UseGitlabGitAttributes module UseGitlabGitAttributes
......
## Usually modules with instance variables considered harmful
### Background
Rails somehow encourages people using modules and instance variables
everywhere. For example, using instance variables in the controllers,
helpers, and views. They're also encouraging the use of
`ActiveSupport::Concern`, which further strengthens the idea of
saving everything in a giant, single object, and people could access
everything in that one giant object.
### The problems
Of course this is convenient to develop, because we just have everything
within reach. However this has a number of downsides when that chosen object
is growing, it would later become out of control for the same reason.
There are just too many things in the same context, and we don't know if
those things are tightly coupled or not, depending on each others or not.
It's very hard to tell when the complexity grows to a point, and it makes
tracking the code also extremely hard. For example, a class could be using
3 different instance variables, and all of them could be initialized and
manipulated from 3 different modules. It's hard to track when those variables
start giving us troubles. We don't know which module would suddenly change
one of the variables. Everything could touch anything.
### Similar concerns
People are saying multiple inheritance is bad. Mixing multiple modules with
multiple instance variables scattering everywhere suffer from the same issue.
The same applies to `ActiveSupport::Concern`. See:
[Consider replacing concerns with dedicated classes & composition](
https://gitlab.com/gitlab-org/gitlab-ce/issues/23786)
There's also a similar idea:
[Use decorators and interface segregation to solve overgrowing models problem](
https://gitlab.com/gitlab-org/gitlab-ce/issues/13484)
Note that `included` doesn't solve the whole issue. They define the
dependencies, but they still allow each modules to talk implicitly via the
instance variables in the final giant object, and that's where the problem is.
### Solutions
We should split the giant object into multiple objects, and they communicate
each other with the API, i.e. public methods. In short, composition over
inheritance. This way, each smaller objects would have their own respective
limited states, i.e. instance variables. If one instance variable goes wrong,
we would be very clear that it's from that single small object, because
no one else could be touching it.
With clearly defined API, this would make things less coupled and much easier
to debug and track, and much more extensible for other objects to use, because
they communicate in a clear way, rather than implicit dependencies.
### Exceptions
However, it's not all that bad when using instance variables in a module,
as long as it's contained in the same module, that is no other modules or
objects are touching them. If that's the case, then it would be an acceptable
use. Unfortunately it's a bit hard to code this principle in the cop, so
for now we rely on people turning off the cops, if they think that the use
conform this rule.
Here's an acceptable case:
``` ruby
# This is ok, as long as `@attributes` is never used anywhere else.
# Consider adding some prefix or suffix to avoid name conflicts though.
# rubocop:disable Cop/ModuleWithInstanceVariables
module Rugged
class Repository
module UseGitlabGitAttributes
def fetch_attributes(name, *)
@attributes ||= Gitlab::Git::Attributes.new(path)
@attributes.attributes(name)
end
end
prepend UseGitlabGitAttributes
end
end
```
Here's a bad example which we should rewrite:
``` ruby
module SpamCheckService
def filter_spam_check_params
@request = params.delete(:request)
@api = params.delete(:api)
@recaptcha_verified = params.delete(:recaptcha_verified)
@spam_log_id = params.delete(:spam_log_id)
end
def spam_check(spammable, user)
spam_service = SpamService.new(spammable, @request)
spam_service.when_recaptcha_verified(@recaptcha_verified, @api) do
user.spam_logs.find_by(id: @spam_log_id)&.update!(recaptcha_verified: true)
end
end
end
```
There are several implicit dependencies here. First, `params` should be
defined before using. Second, `filter_spam_check_params` should be called
before `spam_check`. These are all implicit and the includer could be using
those instance variables without awareness.
This should be rewritten like:
``` ruby
class SpamCheckService
def initialize(request:, api:, recaptcha_verified:, spam_log_id:)
@request = request
@api = api
@recaptcha_verified = recaptcha_verified
@spam_log_id = spam_log_id
end
def spam_check(spammable, user)
spam_service = SpamService.new(spammable, @request)
spam_service.when_recaptcha_verified(@recaptcha_verified, @api) do
user.spam_logs.find_by(id: @spam_log_id)&.update!(recaptcha_verified: true)
end
end
end
```
And use it like:
``` ruby
class UpdateSnippetService < BaseService
def execute
# ...
spam = SpamCheckService.new(params.slice!(:request, :api, :recaptcha_verified, :spam_log_id))
spam.check(snippet, current_user)
# ...
end
end
```
This way, all those instance variables are isolated in `SpamCheckService`
rather than who ever include the module, and those modules which were also
included, making it much easier to track down the issues if there's any,
and it also reduce the chance of having name conflicts.
### Things we might need to ignore right now
Since the way how Rails helpers and mailers work, we might not be able to
avoid the use of instance variables there. For those cases, we could ignore
them at the moment. At least we're not going to share those modules with
other random objects, so they're still somehow isolated.
### Instance variables in the views
They're terrible, because they're also shared between different controllers,
and it's very hard to track where those instance variables were set when we
saw somewhere is using it, neither do we know where those were used when we
saw somewhere is setting up them. We hit into a number of 500 errors when we
tried to remove some instance variables in the controller in the past.
Somewhere, some partials might be using it, and we don't know.
We're trying to use something like this instead:
``` haml
= render 'projects/commits/commit', commit: commit, ref: ref, project: project
```
And in the partial:
``` haml
- ref = local_assigns.fetch(:ref)
- commit = local_assigns.fetch(:commit)
- project = local_assigns.fetch(:project)
```
This way it's very clear where those values were coming from. In the future,
we should also forbid the use of instance variables in partials.
...@@ -42,11 +42,11 @@ module StdoutReporterWithScenarioLocation ...@@ -42,11 +42,11 @@ module StdoutReporterWithScenarioLocation
# Override the standard reporter to show filename and line number next to each # Override the standard reporter to show filename and line number next to each
# scenario for easy, focused re-runs # scenario for easy, focused re-runs
def before_scenario_run(scenario, step_definitions = nil) def before_scenario_run(scenario, step_definitions = nil)
@max_step_name_length = scenario.steps.map(&:name).map(&:length).max if scenario.steps.any? max_step_name_length = scenario.steps.map(&:name).map(&:length).max if scenario.steps.any?
name = scenario.name name = scenario.name
# This number has no significance, it's just to line things up # This number has no significance, it's just to line things up
max_length = @max_step_name_length + 19 max_length = max_step_name_length + 19
out.puts "\n #{'Scenario:'.green} #{name.light_green.ljust(max_length)}" \ out.puts "\n #{'Scenario:'.green} #{name.light_green.ljust(max_length)}" \
" # #{scenario.feature.filename}:#{scenario.line}" " # #{scenario.feature.filename}:#{scenario.line}"
end end
......
...@@ -20,6 +20,7 @@ module AfterCommitQueue ...@@ -20,6 +20,7 @@ module AfterCommitQueue
end end
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def _after_commit_queue def _after_commit_queue
@after_commit_queue ||= [] @after_commit_queue ||= []
end end
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
require 'rack/oauth2' require 'rack/oauth2'
# rubocop:disable Cop/ModuleWithInstanceVariables
module API module API
module APIGuard module APIGuard
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module API module API
module Helpers module Helpers
include Gitlab::Utils include Gitlab::Utils
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module API module API
module Helpers module Helpers
module InternalHelpers module InternalHelpers
...@@ -57,6 +58,7 @@ module API ...@@ -57,6 +58,7 @@ module API
private private
# rubocop:disable Cop/ModuleWithInstanceVariables
def set_project def set_project
if params[:gl_repository] if params[:gl_repository]
@project, @wiki = Gitlab::GlRepository.parse(params[:gl_repository]) @project, @wiki = Gitlab::GlRepository.parse(params[:gl_repository])
......
...@@ -21,6 +21,7 @@ module API ...@@ -21,6 +21,7 @@ module API
forbidden! unless current_runner forbidden! unless current_runner
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def current_runner def current_runner
@runner ||= ::Ci::Runner.find_by_token(params[:token].to_s) @runner ||= ::Ci::Runner.find_by_token(params[:token].to_s)
end end
......
# Module providing methods for dealing with separating a tree-ish string and a # Module providing methods for dealing with separating a tree-ish string and a
# file path string when combined in a request parameter # file path string when combined in a request parameter
# rubocop:disable Cop/ModuleWithInstanceVariables
module ExtractsPath module ExtractsPath
# Raised when given an invalid file path # Raised when given an invalid file path
InvalidPathError = Class.new(StandardError) InvalidPathError = Class.new(StandardError)
......
...@@ -45,6 +45,7 @@ module Gitlab ...@@ -45,6 +45,7 @@ module Gitlab
klass.prepend(extension) klass.prepend(extension)
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def request_cache_key(&block) def request_cache_key(&block)
if block_given? if block_given?
@request_cache_key = block @request_cache_key = block
......
...@@ -2,6 +2,7 @@ module Gitlab ...@@ -2,6 +2,7 @@ module Gitlab
module Ci module Ci
module Charts module Charts
module DailyInterval module DailyInterval
# rubocop:disable Cop/ModuleWithInstanceVariables
def grouped_count(query) def grouped_count(query)
query query
.group("DATE(#{::Ci::Pipeline.table_name}.created_at)") .group("DATE(#{::Ci::Pipeline.table_name}.created_at)")
...@@ -9,6 +10,7 @@ module Gitlab ...@@ -9,6 +10,7 @@ module Gitlab
.transform_keys { |date| date.strftime(@format) } .transform_keys { |date| date.strftime(@format) }
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def interval_step def interval_step
@interval_step ||= 1.day @interval_step ||= 1.day
end end
...@@ -28,6 +30,7 @@ module Gitlab ...@@ -28,6 +30,7 @@ module Gitlab
end end
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def interval_step def interval_step
@interval_step ||= 1.month @interval_step ||= 1.month
end end
......
...@@ -13,6 +13,7 @@ module Gitlab ...@@ -13,6 +13,7 @@ module Gitlab
# script: ... # script: ...
# artifacts: ... # artifacts: ...
# #
# rubocop:disable Cop/ModuleWithInstanceVariables
module Configurable module Configurable
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module Gitlab module Gitlab
module Ci module Ci
class Config class Config
......
...@@ -5,6 +5,7 @@ module Gitlab ...@@ -5,6 +5,7 @@ module Gitlab
"ci_" "ci_"
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def model_name def model_name
@model_name ||= ActiveModel::Name.new(self, nil, self.name.split("::").last) @model_name ||= ActiveModel::Name.new(self, nil, self.name.split("::").last)
end end
......
...@@ -52,6 +52,7 @@ module Gitlab ...@@ -52,6 +52,7 @@ module Gitlab
::ApplicationSetting.create_from_defaults || in_memory_application_settings ::ApplicationSetting.create_from_defaults || in_memory_application_settings
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def in_memory_application_settings def in_memory_application_settings
@in_memory_application_settings ||= ::ApplicationSetting.new(::ApplicationSetting.defaults) @in_memory_application_settings ||= ::ApplicationSetting.new(::ApplicationSetting.defaults)
rescue ActiveRecord::StatementInvalid, ActiveRecord::UnknownAttributeError rescue ActiveRecord::StatementInvalid, ActiveRecord::UnknownAttributeError
......
...@@ -59,6 +59,12 @@ module Gitlab ...@@ -59,6 +59,12 @@ module Gitlab
nil nil
end end
def load_allowed_ids
allowed_ids_finder_class
.new(@options[:current_user], project_id: @project.id)
.execute.where(id: event_result_ids).pluck(:id)
end
def event_result_ids def event_result_ids
event_result.map { |event| event['id'] } event_result.map { |event| event['id'] }
end end
......
...@@ -7,6 +7,7 @@ module Gitlab ...@@ -7,6 +7,7 @@ module Gitlab
private private
# rubocop:disable Cop/ModuleWithInstanceVariables
def base_query def base_query
@base_query ||= stage_query @base_query ||= stage_query
end end
......
module Gitlab module Gitlab
module CycleAnalytics module CycleAnalytics
class CodeEventFetcher < BaseEventFetcher class CodeEventFetcher < BaseEventFetcher
include MergeRequestAllowed
def initialize(*args) def initialize(*args)
@projections = [mr_table[:title], @projections = [mr_table[:title],
mr_table[:iid], mr_table[:iid],
...@@ -20,6 +18,14 @@ module Gitlab ...@@ -20,6 +18,14 @@ module Gitlab
def serialize(event) def serialize(event)
AnalyticsMergeRequestSerializer.new(project: @project).represent(event) AnalyticsMergeRequestSerializer.new(project: @project).represent(event)
end end
def allowed_ids
load_allowed_ids
end
def allowed_ids_finder_class
MergeRequestsFinder
end
end end
end end
end end
module Gitlab
module CycleAnalytics
module IssueAllowed
def allowed_ids
@allowed_ids ||= IssuesFinder.new(@options[:current_user], project_id: @project.id).execute.where(id: event_result_ids).pluck(:id)
end
end
end
end
module Gitlab module Gitlab
module CycleAnalytics module CycleAnalytics
class IssueEventFetcher < BaseEventFetcher class IssueEventFetcher < BaseEventFetcher
include IssueAllowed
def initialize(*args) def initialize(*args)
@projections = [issue_table[:title], @projections = [issue_table[:title],
issue_table[:iid], issue_table[:iid],
...@@ -18,6 +16,14 @@ module Gitlab ...@@ -18,6 +16,14 @@ module Gitlab
def serialize(event) def serialize(event)
AnalyticsIssueSerializer.new(project: @project).represent(event) AnalyticsIssueSerializer.new(project: @project).represent(event)
end end
def allowed_ids
load_allowed_ids
end
def allowed_ids_finder_class
IssuesFinder
end
end end
end end
end end
module Gitlab
module CycleAnalytics
module MergeRequestAllowed
def allowed_ids
@allowed_ids ||= MergeRequestsFinder.new(@options[:current_user], project_id: @project.id).execute.where(id: event_result_ids).pluck(:id)
end
end
end
end
# rubocop:disable Cop/ModuleWithInstanceVariables
module Gitlab module Gitlab
module CycleAnalytics module CycleAnalytics
module ProductionHelper module ProductionHelper
......
module Gitlab module Gitlab
module CycleAnalytics module CycleAnalytics
class ReviewEventFetcher < BaseEventFetcher class ReviewEventFetcher < BaseEventFetcher
include MergeRequestAllowed
def initialize(*args) def initialize(*args)
@projections = [mr_table[:title], @projections = [mr_table[:title],
mr_table[:iid], mr_table[:iid],
...@@ -14,9 +12,19 @@ module Gitlab ...@@ -14,9 +12,19 @@ module Gitlab
super(*args) super(*args)
end end
private
def serialize(event) def serialize(event)
AnalyticsMergeRequestSerializer.new(project: @project).represent(event) AnalyticsMergeRequestSerializer.new(project: @project).represent(event)
end end
def allowed_ids
load_allowed_ids
end
def allowed_ids_finder_class
MergeRequestsFinder
end
end end
end end
end end
...@@ -3,6 +3,7 @@ module Gitlab ...@@ -3,6 +3,7 @@ module Gitlab
module RenameReservedPathsMigration module RenameReservedPathsMigration
module V1 module V1
module MigrationClasses module MigrationClasses
# rubocop:disable Cop/ModuleWithInstanceVariables
module Routable module Routable
def full_path def full_path
if route && route.path.present? if route && route.path.present?
......
...@@ -12,6 +12,7 @@ module Gitlab ...@@ -12,6 +12,7 @@ module Gitlab
raise NotImplementedError raise NotImplementedError
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def message def message
@message ||= process_message @message ||= process_message
end end
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module Gitlab module Gitlab
module Emoji module Emoji
extend self extend self
......
...@@ -13,15 +13,15 @@ module Gitlab ...@@ -13,15 +13,15 @@ module Gitlab
vars = { "PWD" => path } vars = { "PWD" => path }
options = { chdir: path } options = { chdir: path }
@cmd_output = "" cmd_output = ""
@cmd_status = 0 cmd_status = 0
Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr| Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
@cmd_output << stdout.read cmd_output << stdout.read
@cmd_output << stderr.read cmd_output << stderr.read
@cmd_status = wait_thr.value.exitstatus cmd_status = wait_thr.value.exitstatus
end end
[@cmd_output, @cmd_status] [cmd_output, cmd_status]
end end
end end
end end
......
...@@ -52,6 +52,7 @@ module Gitlab ...@@ -52,6 +52,7 @@ module Gitlab
end end
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def identification_cache def identification_cache
@identification_cache ||= { @identification_cache ||= {
email: {}, email: {},
......
...@@ -30,6 +30,7 @@ module Gitlab ...@@ -30,6 +30,7 @@ module Gitlab
execute(%W(tar -#{options} #{archive} -C #{dir})) execute(%W(tar -#{options} #{archive} -C #{dir}))
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def execute(cmd) def execute(cmd)
output, status = Gitlab::Popen.popen(cmd) output, status = Gitlab::Popen.popen(cmd)
@shared.error(Gitlab::ImportExport::Error.new(output.to_s)) unless status.zero? @shared.error(Gitlab::ImportExport::Error.new(output.to_s)) unless status.zero?
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module Gitlab module Gitlab
module Metrics module Metrics
module InfluxDb module InfluxDb
......
require 'prometheus/client' require 'prometheus/client'
# rubocop:disable Cop/ModuleWithInstanceVariables
module Gitlab module Gitlab
module Metrics module Metrics
module Prometheus module Prometheus
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module Gitlab module Gitlab
module PathRegex module PathRegex
extend self extend self
......
...@@ -26,6 +26,7 @@ module Gitlab ...@@ -26,6 +26,7 @@ module Gitlab
load_yaml_file&.map(&:deep_symbolize_keys).freeze load_yaml_file&.map(&:deep_symbolize_keys).freeze
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def load_yaml_file def load_yaml_file
@loaded_yaml_file ||= YAML.load_file(Rails.root.join('config/prometheus/additional_metrics.yml')) @loaded_yaml_file ||= YAML.load_file(Rails.root.join('config/prometheus/additional_metrics.yml'))
end end
......
...@@ -56,6 +56,7 @@ module Gitlab ...@@ -56,6 +56,7 @@ module Gitlab
query query
end end
# rubocop:disable Cop/ModuleWithInstanceVariables
def available_metrics def available_metrics
@available_metrics ||= client_label_values || [] @available_metrics ||= client_label_values || []
end end
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module Gitlab module Gitlab
module Regex module Regex
extend self extend self
......
# rubocop:disable Cop/ModuleWithInstanceVariables
module Gitlab module Gitlab
module SlashCommands module SlashCommands
module Presenters module Presenters
......
...@@ -72,6 +72,7 @@ module Gitlab ...@@ -72,6 +72,7 @@ module Gitlab
private private
# rubocop:disable Cop/ModuleWithInstanceVariables
def default_id def default_id
@default_id ||= begin @default_id ||= begin
id = Gitlab.config.gitlab.default_theme.to_i id = Gitlab.config.gitlab.default_theme.to_i
......
require 'rainbow/ext/string' require 'rainbow/ext/string'
# rubocop:disable Cop/ModuleWithInstanceVariables
module Gitlab module Gitlab
TaskFailedError = Class.new(StandardError) TaskFailedError = Class.new(StandardError)
TaskAbortedByUserError = Class.new(StandardError) TaskAbortedByUserError = Class.new(StandardError)
......
...@@ -3,6 +3,7 @@ module QA ...@@ -3,6 +3,7 @@ module QA
module Namespace module Namespace
extend self extend self
# rubocop:disable Cop/ModuleWithInstanceVariables
def time def time
@time ||= Time.now @time ||= Time.now
end end
......
module RuboCop
module Cop
class ModuleWithInstanceVariables < RuboCop::Cop::Cop
MSG = <<~EOL.freeze
Do not use instance variables in a module. Please read this
for the rationale behind it:
doc/development/module_with_instance_variables.md
If you think the use for this is fine, please just add:
# rubocop:disable Cop/ModuleWithInstanceVariables
EOL
def on_module(node)
return if
rails_helper?(node) || rails_mailer?(node) || spec_helper?(node)
check_method_definition(node)
# Not sure why some module would have an extra begin wrapping around
node.each_child_node(:begin) do |begin_node|
check_method_definition(begin_node)
end
end
private
# We ignore Rails helpers right now because it's hard to workaround it
def rails_helper?(node)
node.source_range.source_buffer.name =~
%r{app/helpers/\w+_helper.rb\z}
end
# We ignore Rails mailers right now because it's hard to workaround it
def rails_mailer?(node)
node.source_range.source_buffer.name =~
%r{app/mailers/emails/}
end
# We ignore spec helpers because it usually doesn't matter
def spec_helper?(node)
node.source_range.source_buffer.name =~
%r{spec/support/|features/steps/}
end
def check_method_definition(node)
node.each_child_node(:def) do |definition|
definition.each_descendant(:ivar, :ivasgn) do |offense|
add_offense(offense, :expression)
end
end
end
end
end
end
...@@ -6,6 +6,7 @@ require_relative 'cop/polymorphic_associations' ...@@ -6,6 +6,7 @@ require_relative 'cop/polymorphic_associations'
require_relative 'cop/project_path_helper' require_relative 'cop/project_path_helper'
require_relative 'cop/active_record_dependent' require_relative 'cop/active_record_dependent'
require_relative 'cop/in_batches' require_relative 'cop/in_batches'
require_relative 'cop/module_with_instance_variables'
require_relative 'cop/migration/add_column' require_relative 'cop/migration/add_column'
require_relative 'cop/migration/add_column_with_default_to_large_table' require_relative 'cop/migration/add_column_with_default_to_large_table'
require_relative 'cop/migration/add_concurrent_foreign_key' require_relative 'cop/migration/add_concurrent_foreign_key'
......
require 'spec_helper'
require 'rubocop'
require 'rubocop/rspec/support'
require_relative '../../../rubocop/cop/module_with_instance_variables'
describe RuboCop::Cop::ModuleWithInstanceVariables do
include CopHelper
subject(:cop) { described_class.new }
shared_examples('registering offense') do
it 'registers an offense when instance variable is used in a module' do
inspect_source(cop, source)
aggregate_failures do
expect(cop.offenses.size).to eq(offending_lines.size)
expect(cop.offenses.map(&:line)).to eq(offending_lines)
end
end
end
context 'when source is a regular module' do
let(:source) do
<<~RUBY
module M
def f
@f ||= true
end
end
RUBY
end
let(:offending_lines) { [3] }
it_behaves_like 'registering offense'
end
context 'when source is a nested module' do
let(:source) do
<<~RUBY
module N
module M
def f
@f = true
end
end
end
RUBY
end
let(:offending_lines) { [4] }
it_behaves_like 'registering offense'
end
context 'when source is a nested module with multiple offenses' do
let(:source) do
<<~RUBY
module N
module M
def f
@f ||= true
end
def g
true
end
def h
@h = true
end
end
end
RUBY
end
let(:offending_lines) { [4, 12] }
it_behaves_like 'registering offense'
end
context 'when source is offending but it is a rails helper' do
before do
allow(cop).to receive(:rails_helper?).and_return(true)
end
it 'does not register offenses' do
inspect_source(cop, <<~RUBY)
module M
def f
@f ||= true
end
end
RUBY
expect(cop.offenses).to be_empty
end
end
context 'when source is offending but it is a rails mailer' do
before do
allow(cop).to receive(:rails_mailer?).and_return(true)
end
it 'does not register offenses' do
inspect_source(cop, <<~RUBY)
module M
def f
@f = true
end
end
RUBY
expect(cop.offenses).to be_empty
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment