Commit 392227ee authored by Robert Speicher's avatar Robert Speicher

Merge branch 'no-ivar-in-modules-ee' into 'master'

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

See merge request gitlab-org/gitlab-ee!3008
parents 3e665261 4974f15d
......@@ -1190,7 +1190,22 @@ RSpec/SubjectStub:
RSpec/VerifiedDoubles:
Enabled: false
# GitlabSecurity ##############################################################
# Gitlab ###################################################################
Gitlab/ModuleWithInstanceVariables:
Enable: true
Exclude:
# We ignore Rails helpers right now because it's hard to workaround it
- app/helpers/**/*_helper.rb
- ee/app/helpers/**/*_helper.rb
# We ignore Rails mailers right now because it's hard to workaround it
- app/mailers/emails/**/*.rb
- ee/**/emails/**/*.rb
# We ignore spec helpers because it usually doesn't matter
- spec/support/**/*.rb
- features/steps/**/*.rb
# GitlabSecurity ###########################################################
GitlabSecurity/DeepMunge:
Enabled: true
......
......@@ -24,11 +24,11 @@ module BoardsResponses
end
def respond_with_boards
respond_with(@boards)
respond_with(@boards) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def respond_with_board
respond_with(@board)
respond_with(@board) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def respond_with(resource)
......
module CreatesCommit
extend ActiveSupport::Concern
include Gitlab::Utils::StrongMemoize
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def create_commit(service, success_path:, failure_path:, failure_view: nil, success_notice: nil)
if can?(current_user, :push_code, @project)
@project_to_commit_into = @project
......@@ -45,6 +47,7 @@ module CreatesCommit
end
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def authorize_edit_tree!
return if can_collaborate_with_project?
......@@ -77,6 +80,7 @@ module CreatesCommit
end
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def new_merge_request_path
project_new_merge_request_path(
@project_to_commit_into,
......@@ -88,20 +92,28 @@ module CreatesCommit
}
)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def existing_merge_request_path
project_merge_request_path(@project, @merge_request)
project_merge_request_path(@project, @merge_request) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def merge_request_exists?
return @merge_request if defined?(@merge_request)
@merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened
.find_by(source_project_id: @project_to_commit_into, source_branch: @branch_name, target_branch: @start_branch)
strong_memoize(:merge_request) do
MergeRequestsFinder.new(current_user, project_id: @project.id)
.execute
.opened
.find_by(
source_project_id: @project_to_commit_into,
source_branch: @branch_name,
target_branch: @start_branch)
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def different_project?
@project_to_commit_into != @project
@project_to_commit_into != @project # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def create_merge_request?
......@@ -109,6 +121,6 @@ module CreatesCommit
# as the target branch in the same project,
# we don't want to create a merge request.
params[:create_merge_request].present? &&
(different_project? || @start_branch != @branch_name)
(different_project? || @start_branch != @branch_name) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
module GroupTree
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def render_group_tree(groups)
@groups = if params[:filter].present?
Gitlab::GroupHierarchy.new(groups.search(params[:filter]))
......@@ -20,5 +21,6 @@ module GroupTree
render json: serializer.represent(@groups)
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
end
......@@ -17,7 +17,7 @@ module IssuableActions
end
def update
@issuable = update_service.execute(issuable)
@issuable = update_service.execute(issuable) # rubocop:disable Gitlab/ModuleWithInstanceVariables
respond_to do |format|
format.html do
......@@ -81,7 +81,7 @@ module IssuableActions
private
def recaptcha_check_if_spammable(should_redirect = true, &block)
return yield unless @issuable.is_a? Spammable
return yield unless issuable.is_a? Spammable
recaptcha_check_with_fallback(should_redirect, &block)
end
......@@ -89,7 +89,7 @@ module IssuableActions
def render_conflict_response
respond_to do |format|
format.html do
@conflict = true
@conflict = true # rubocop:disable Gitlab/ModuleWithInstanceVariables
render :edit
end
......@@ -104,7 +104,7 @@ module IssuableActions
end
def labels
@labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute
@labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def authorize_destroy_issuable!
......@@ -114,7 +114,7 @@ module IssuableActions
end
def authorize_admin_issuable!
unless can?(current_user, :"admin_#{resource_name}", @project)
unless can?(current_user, :"admin_#{resource_name}", @project) # rubocop:disable Gitlab/ModuleWithInstanceVariables
return access_denied!
end
end
......@@ -149,6 +149,7 @@ module IssuableActions
@resource_name ||= controller_name.singularize
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def render_entity_json
if @issuable.valid?
render json: serializer.represent(@issuable)
......@@ -156,6 +157,7 @@ module IssuableActions
render json: { errors: @issuable.errors.full_messages }, status: :unprocessable_entity
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def serializer
raise NotImplementedError
......@@ -166,6 +168,6 @@ module IssuableActions
end
def parent
@project || @group
@project || @group # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
......@@ -2,6 +2,7 @@ module IssuableCollections
extend ActiveSupport::Concern
include SortingHelper
include Gitlab::IssuableMetadata
include Gitlab::Utils::StrongMemoize
included do
helper_method :finder
......@@ -9,6 +10,7 @@ module IssuableCollections
private
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def set_issuables_index
@issuables = issuables_collection
@issuables = @issuables.page(params[:page])
......@@ -33,6 +35,7 @@ module IssuableCollections
@users.push(author) if author
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def issuables_collection
finder.execute.preload(preload_for_collection)
......@@ -41,7 +44,7 @@ module IssuableCollections
def redirect_out_of_range(total_pages)
return false if total_pages.zero?
out_of_range = @issuables.current_page > total_pages
out_of_range = @issuables.current_page > total_pages # rubocop:disable Gitlab/ModuleWithInstanceVariables
if out_of_range
redirect_to(url_for(params.merge(page: total_pages, only_path: true)))
......@@ -51,7 +54,7 @@ module IssuableCollections
end
def issuable_page_count
page_count_for_relation(@issuables, finder.row_count)
page_count_for_relation(@issuables, finder.row_count) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def page_count_for_relation(relation, row_count)
......@@ -66,6 +69,7 @@ module IssuableCollections
finder_class.new(current_user, filter_params)
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def filter_params
set_sort_order_from_cookie
set_default_state
......@@ -90,6 +94,7 @@ module IssuableCollections
@filter_params.permit(IssuableFinder::VALID_PARAMS)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def set_default_state
params[:state] = 'opened' if params[:state].blank?
......@@ -131,9 +136,9 @@ module IssuableCollections
end
def finder
return @finder if defined?(@finder)
@finder = issuable_finder_for(@finder_type)
strong_memoize(:finder) do
issuable_finder_for(@finder_type) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
def collection_type
......
......@@ -2,6 +2,7 @@ module IssuesAction
extend ActiveSupport::Concern
include IssuableCollections
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def issues
@finder_type = IssuesFinder
@label = finder.labels.first
......@@ -17,4 +18,5 @@ module IssuesAction
format.atom { render layout: 'xml.atom' }
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
......@@ -2,6 +2,7 @@ module MergeRequestsAction
extend ActiveSupport::Concern
include IssuableCollections
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def merge_requests
@finder_type = MergeRequestsFinder
@label = finder.labels.first
......@@ -10,6 +11,7 @@ module MergeRequestsAction
@issuable_meta_data = issuable_meta_data(@merge_requests, collection_type)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
private
......
......@@ -6,7 +6,7 @@ module MilestoneActions
format.html { redirect_to milestone_redirect_path }
format.json do
render json: tabs_json("shared/milestones/_merge_requests_tab", {
merge_requests: @milestone.sorted_merge_requests,
merge_requests: @milestone.sorted_merge_requests, # rubocop:disable Gitlab/ModuleWithInstanceVariables
show_project_name: true
})
end
......@@ -18,7 +18,7 @@ module MilestoneActions
format.html { redirect_to milestone_redirect_path }
format.json do
render json: tabs_json("shared/milestones/_participants_tab", {
users: @milestone.participants
users: @milestone.participants # rubocop:disable Gitlab/ModuleWithInstanceVariables
})
end
end
......@@ -29,7 +29,7 @@ module MilestoneActions
format.html { redirect_to milestone_redirect_path }
format.json do
render json: tabs_json("shared/milestones/_labels_tab", {
labels: @milestone.labels
labels: @milestone.labels # rubocop:disable Gitlab/ModuleWithInstanceVariables
})
end
end
......@@ -43,6 +43,7 @@ module MilestoneActions
}
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def milestone_redirect_path
if @project
project_milestone_path(@project, @milestone)
......@@ -52,4 +53,5 @@ module MilestoneActions
dashboard_milestone_path(@milestone.safe_title, title: @milestone.title)
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
module NotesActions
include RendersNotes
include Gitlab::Utils::StrongMemoize
extend ActiveSupport::Concern
included do
......@@ -30,6 +31,7 @@ module NotesActions
render json: notes_json
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def create
create_params = note_params.merge(
merge_request_diff_head_sha: params[:merge_request_diff_head_sha],
......@@ -47,7 +49,9 @@ module NotesActions
format.html { redirect_back_or_default }
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def update
@note = Notes::UpdateService.new(project, current_user, note_params).execute(note)
......@@ -60,6 +64,7 @@ module NotesActions
format.html { redirect_back_or_default }
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def destroy
if note.editable?
......@@ -138,7 +143,7 @@ module NotesActions
end
else
template = "discussions/_diff_discussion"
@fresh_discussion = true
@fresh_discussion = true # rubocop:disable Gitlab/ModuleWithInstanceVariables
locals = { discussions: [discussion], on_image: on_image }
end
......@@ -191,7 +196,7 @@ module NotesActions
end
def noteable
@noteable ||= notes_finder.target || @note&.noteable
@noteable ||= notes_finder.target || @note&.noteable # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def require_noteable!
......@@ -211,20 +216,21 @@ module NotesActions
end
def note_project
return @note_project if defined?(@note_project)
return nil unless project
strong_memoize(:note_project) do
return nil unless project
note_project_id = params[:note_project_id]
note_project_id = params[:note_project_id]
@note_project =
if note_project_id.present?
Project.find(note_project_id)
else
project
end
the_project =
if note_project_id.present?
Project.find(note_project_id)
else
project
end
return access_denied! unless can?(current_user, :create_note, @note_project)
return access_denied! unless can?(current_user, :create_note, the_project)
@note_project
the_project
end
end
end
......@@ -14,6 +14,6 @@ module OauthApplications
end
def load_scopes
@scopes = Doorkeeper.configuration.scopes
@scopes ||= Doorkeeper.configuration.scopes
end
end
module PreviewMarkdown
extend ActiveSupport::Concern
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def preview_markdown
result = PreviewMarkdownService.new(@project, current_user, params).execute
......@@ -20,4 +21,5 @@ module PreviewMarkdown
}
}
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
module RendersCommits
def prepare_commits_for_rendering(commits)
Banzai::CommitRenderer.render(commits, @project, current_user)
Banzai::CommitRenderer.render(commits, @project, current_user) # rubocop:disable Gitlab/ModuleWithInstanceVariables
commits
end
......
module RendersNotes
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def prepare_notes_for_rendering(notes, noteable = nil)
preload_noteable_for_regular_notes(notes)
preload_max_access_for_authors(notes, @project)
......@@ -7,6 +8,7 @@ module RendersNotes
notes
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
private
......
......@@ -67,7 +67,7 @@ module ServiceParams
FILTER_BLANK_PARAMS = [:password].freeze
def service_params
dynamic_params = @service.event_channel_names + @service.event_names
dynamic_params = @service.event_channel_names + @service.event_names # rubocop:disable Gitlab/ModuleWithInstanceVariables
service_params = params.permit(:id, service: allowed_service_params + dynamic_params)
if service_params[:service].is_a?(Hash)
......
......@@ -4,6 +4,7 @@ module SnippetsActions
def edit
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def raw
disposition = params[:inline] == 'false' ? 'attachment' : 'inline'
......@@ -14,6 +15,7 @@ module SnippetsActions
filename: @snippet.sanitized_file_name
)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
private
......
......@@ -2,6 +2,7 @@ module SpammableActions
extend ActiveSupport::Concern
include Recaptcha::Verify
include Gitlab::Utils::StrongMemoize
included do
before_action :authorize_submit_spammable!, only: :mark_as_spam
......@@ -18,9 +19,9 @@ module SpammableActions
private
def ensure_spam_config_loaded!
return @spam_config_loaded if defined?(@spam_config_loaded)
@spam_config_loaded = Gitlab::Recaptcha.load_configurations!
strong_memoize(:spam_config_loaded) do
Gitlab::Recaptcha.load_configurations!
end
end
def recaptcha_check_with_fallback(should_redirect = true, &fallback)
......
......@@ -12,7 +12,7 @@ module ToggleSubscriptionAction
private
def subscribable_project
@project || raise(NotImplementedError)
@project ||= raise(NotImplementedError)
end
def subscribable_resource
......
class AuditEvent < ActiveRecord::Base
prepend EE::AuditEvent
include Gitlab::Utils::StrongMemoize
serialize :details, Hash # rubocop:disable Cop/ActiveRecordSerialize
......
......@@ -44,13 +44,11 @@ module Mentionable
end
def all_references(current_user = nil, extractor: nil)
@extractors ||= {}
# Use custom extractor if it's passed in the function parameters.
if extractor
@extractors[current_user] = extractor
extractors[current_user] = extractor
else
extractor = @extractors[current_user] ||= Gitlab::ReferenceExtractor.new(project, current_user)
extractor = extractors[current_user] ||= Gitlab::ReferenceExtractor.new(project, current_user)
extractor.reset_memoized_values
end
......@@ -69,6 +67,10 @@ module Mentionable
extractor
end
def extractors
@extractors ||= {}
end
def mentioned_users(current_user = nil)
all_references(current_user).users
end
......
......@@ -103,9 +103,11 @@ module Milestoneish
end
def memoize_per_user(user, method_name)
@memoized ||= {}
@memoized[method_name] ||= {}
@memoized[method_name][user&.id] ||= yield
memoized_users[method_name][user&.id] ||= yield
end
def memoized_users
@memoized_users ||= Hash.new { |h, k| h[k] = {} }
end
# override in a class that includes this module to get a faster query
......
......@@ -46,6 +46,7 @@ module Noteable
notes.inc_relations_for_view.grouped_diff_discussions(*args)
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def resolvable_discussions
@resolvable_discussions ||=
if defined?(@discussions)
......@@ -54,6 +55,7 @@ module Noteable
discussion_notes.resolvable.discussions(self)
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def discussions_resolvable?
resolvable_discussions.any?(&:resolvable?)
......
......@@ -56,15 +56,17 @@ module Participable
#
# Returns an Array of User instances.
def participants(current_user = nil)
@participants ||= Hash.new do |hash, user|
hash[user] = raw_participants(user)
end
@participants[current_user]
all_participants[current_user]
end
private
def all_participants
@all_participants ||= Hash.new do |hash, user|
hash[user] = raw_participants(user)
end
end
def raw_participants(current_user = nil)
current_user ||= author
ext = Gitlab::ReferenceExtractor.new(project, current_user)
......
......@@ -52,7 +52,7 @@ module RelativePositioning
# to its predecessor. This process will recursively move all the predecessors until we have a place
if (after.relative_position - before.relative_position) < 2
before.move_before
@positionable_neighbours = [before]
@positionable_neighbours = [before] # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
self.relative_position = position_between(before.relative_position, after.relative_position)
......@@ -65,7 +65,7 @@ module RelativePositioning
if before.shift_after?
issue_to_move = self.class.in_projects(project_ids).find_by!(relative_position: pos_after)
issue_to_move.move_after
@positionable_neighbours = [issue_to_move]
@positionable_neighbours = [issue_to_move] # rubocop:disable Gitlab/ModuleWithInstanceVariables
pos_after = issue_to_move.relative_position
end
......@@ -80,7 +80,7 @@ module RelativePositioning
if after.shift_before?
issue_to_move = self.class.in_projects(project_ids).find_by!(relative_position: pos_before)
issue_to_move.move_before
@positionable_neighbours = [issue_to_move]
@positionable_neighbours = [issue_to_move] # rubocop:disable Gitlab/ModuleWithInstanceVariables
pos_before = issue_to_move.relative_position
end
......@@ -132,6 +132,7 @@ module RelativePositioning
end
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def save_positionable_neighbours
return unless @positionable_neighbours
......@@ -140,4 +141,5 @@ module RelativePositioning
status
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
......@@ -31,15 +31,11 @@ module ResolvableDiscussion
end
def resolvable?
return @resolvable if @resolvable.present?
@resolvable = potentially_resolvable? && notes.any?(&:resolvable?)
@resolvable ||= potentially_resolvable? && notes.any?(&:resolvable?)
end
def resolved?
return @resolved if @resolved.present?
@resolved = resolvable? && notes.none?(&:to_be_resolved?)
@resolved ||= resolvable? && notes.none?(&:to_be_resolved?)
end
def first_note
......@@ -49,13 +45,13 @@ module ResolvableDiscussion
def first_note_to_resolve
return unless resolvable?
@first_note_to_resolve ||= notes.find(&:to_be_resolved?)
@first_note_to_resolve ||= notes.find(&:to_be_resolved?) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def last_resolved_note
return unless resolved?
@last_resolved_note ||= resolved_notes.sort_by(&:resolved_at).last
@last_resolved_note ||= resolved_notes.sort_by(&:resolved_at).last # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def resolved_notes
......@@ -95,7 +91,7 @@ module ResolvableDiscussion
yield(notes_relation)
# Set the notes array to the updated notes
@notes = notes_relation.fresh.to_a
@notes = notes_relation.fresh.to_a # rubocop:disable Gitlab/ModuleWithInstanceVariables
self.class.memoized_values.each do |var|
instance_variable_set(:"@#{var}", nil)
......
......@@ -88,7 +88,7 @@ module Routable
def full_name
if route && route.name.present?
@full_name ||= route.name
@full_name ||= route.name # rubocop:disable Gitlab/ModuleWithInstanceVariables
else
update_route if persisted?
......@@ -112,7 +112,7 @@ module Routable
def expires_full_path_cache
RequestStore.delete(full_path_key) if RequestStore.active?
@full_path = nil
@full_path = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def build_full_path
......@@ -127,7 +127,7 @@ module Routable
def uncached_full_path
if route && route.path.present?
@full_path ||= route.path
@full_path ||= route.path # rubocop:disable Gitlab/ModuleWithInstanceVariables
else
update_route if persisted?
......@@ -166,7 +166,7 @@ module Routable
route || build_route(source: self)
route.path = build_full_path
route.name = build_full_name
@full_path = nil
@full_name = nil
@full_path = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
@full_name = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
......@@ -12,6 +12,7 @@ module Spammable
attr_accessor :spam
attr_accessor :spam_log
alias_method :spam?, :spam
after_validation :check_for_spam, on: [:create, :update]
......@@ -34,10 +35,6 @@ module Spammable
end
end
def spam?
@spam
end
def check_for_spam
error_msg = if Gitlab::Recaptcha.enabled?
"Your #{spammable_entity_type} has been recognized as spam. "\
......
......@@ -39,7 +39,7 @@ module Taskable
def task_list_items
return [] if description.blank?
@task_list_items ||= Taskable.get_tasks(description)
@task_list_items ||= Taskable.get_tasks(description) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def tasks
......
......@@ -21,6 +21,7 @@ module TimeTrackable
has_many :timelogs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def spend_time(options)
@time_spent = options[:duration]
@time_spent_user = options[:user]
......@@ -36,6 +37,7 @@ module TimeTrackable
end
end
alias_method :spend_time=, :spend_time
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def total_time_spent
timelogs.sum(:time_spent)
......@@ -52,9 +54,10 @@ module TimeTrackable
private
def reset_spent_time
timelogs.new(time_spent: total_time_spent * -1, user: @time_spent_user)
timelogs.new(time_spent: total_time_spent * -1, user: @time_spent_user) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def add_or_subtract_spent_time
timelogs.new(
time_spent: time_spent,
......@@ -62,16 +65,19 @@ module TimeTrackable
spent_at: @spent_at
)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def check_negative_time_spent
return if time_spent.nil? || time_spent == :reset
# we need to cache the total time spent so multiple calls to #valid?
# doesn't give a false error
@original_total_time_spent ||= total_time_spent
if time_spent < 0 && (time_spent.abs > @original_total_time_spent)
if time_spent < 0 && (time_spent.abs > original_total_time_spent)
errors.add(:time_spent, 'Time to subtract exceeds the total time spent')
end
end
# we need to cache the total time spent so multiple calls to #valid?
# doesn't give a false error
def original_total_time_spent
@original_total_time_spent ||= total_time_spent
end
end
......@@ -14,7 +14,7 @@ module WithPagination
# we shouldn't try to paginate single resources
def represent(resource, opts = {})
if paginated? && resource.respond_to?(:page)
super(@paginator.paginate(resource), opts)
super(paginator.paginate(resource), opts)
else
super(resource, opts)
end
......
module Issues
module ResolveDiscussions
include Gitlab::Utils::StrongMemoize
attr_reader :merge_request_to_resolve_discussions_of_iid, :discussion_to_resolve_id
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def filter_resolve_discussion_params
@merge_request_to_resolve_discussions_of_iid ||= params.delete(:merge_request_to_resolve_discussions_of)
@discussion_to_resolve_id ||= params.delete(:discussion_to_resolve)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def merge_request_to_resolve_discussions_of
return @merge_request_to_resolve_discussions_of if defined?(@merge_request_to_resolve_discussions_of)
@merge_request_to_resolve_discussions_of = MergeRequestsFinder.new(current_user, project_id: project.id)
.execute
.find_by(iid: merge_request_to_resolve_discussions_of_iid)
strong_memoize(:merge_request_to_resolve_discussions_of) do
MergeRequestsFinder.new(current_user, project_id: project.id)
.execute
.find_by(iid: merge_request_to_resolve_discussions_of_iid)
end
end
def discussions_to_resolve
return [] unless merge_request_to_resolve_discussions_of
@discussions_to_resolve ||=
@discussions_to_resolve ||= # rubocop:disable Gitlab/ModuleWithInstanceVariables
if discussion_to_resolve_id
discussion_or_nil = merge_request_to_resolve_discussions_of
.find_discussion(discussion_to_resolve_id)
......
......@@ -7,16 +7,19 @@
# - params with :request
#
module SpamCheckService
# rubocop:disable Gitlab/ModuleWithInstanceVariables
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
# rubocop:enable Gitlab/ModuleWithInstanceVariables
# In order to be proceed to the spam check process, @spammable has to be
# a dirty instance, which means it should be already assigned with the new
# attribute values.
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def spam_check(spammable, user)
spam_service = SpamService.new(spammable, @request)
......@@ -24,4 +27,5 @@ module SpamCheckService
user.spam_logs.find_by(id: @spam_log_id)&.update!(recaptcha_verified: true)
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
......@@ -9,15 +9,15 @@ module NewIssuable
end
def set_user(user_id)
@user = User.find_by(id: user_id)
@user = User.find_by(id: user_id) # rubocop:disable Gitlab/ModuleWithInstanceVariables
log_error(User, user_id) unless @user
log_error(User, user_id) unless @user # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def set_issuable(issuable_id)
@issuable = issuable_class.find_by(id: issuable_id)
@issuable = issuable_class.find_by(id: issuable_id) # rubocop:disable Gitlab/ModuleWithInstanceVariables
log_error(issuable_class, issuable_id) unless @issuable
log_error(issuable_class, issuable_id) unless @issuable # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def log_error(record_class, record_id)
......
......@@ -10,8 +10,8 @@ module Prependable
super
base.singleton_class.send(:prepend, const_get('ClassMethods')) if const_defined?(:ClassMethods)
@_dependencies.each { |dep| base.send(:prepend, dep) }
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
@_dependencies.each { |dep| base.send(:prepend, dep) } # rubocop:disable Gitlab/ModuleWithInstanceVariables
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
end
......
......@@ -6,7 +6,7 @@ module LocalCacheRegistryCleanupWithEnsure
def call(env)
LocalCacheRegistry.set_cache_for(local_cache_key, LocalStore.new)
response = @app.call(env)
response = @app.call(env) # rubocop:disable Gitlab/ModuleWithInstanceVariables
response[2] = ::Rack::BodyProxy.new(response[2]) do
LocalCacheRegistry.set_cache_for(local_cache_key, nil)
end
......
......@@ -19,10 +19,10 @@ module RspecProfilingExt
def example_finished(*args)
super
rescue => err
return if @already_logged_example_finished_error
return if @already_logged_example_finished_error # rubocop:disable Gitlab/ModuleWithInstanceVariables
$stderr.puts "rspec_profiling couldn't collect an example: #{err}. Further warnings suppressed."
@already_logged_example_finished_error = true
@already_logged_example_finished_error = true # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
alias_method :example_passed, :example_finished
......
......@@ -15,8 +15,11 @@ module Rugged
class Repository
module UseGitlabGitAttributes
def fetch_attributes(name, *)
attributes.attributes(name)
end
def attributes
@attributes ||= Gitlab::Git::Attributes.new(path)
@attributes.attributes(name)
end
end
......
......@@ -37,6 +37,7 @@ comments: false
- [`Gemfile` guidelines](gemfile.md)
- [Sidekiq debugging](sidekiq_debugging.md)
- [Gotchas](gotchas.md) to avoid
- [Avoid modules with instance variables](module_with_instance_variables.md) if possible
- [Issue and merge requests state models](object_state_models.md)
- [How to dump production data to staging](db_dump.md)
- [Working with the GitHub importer](github_importer.md)
......
## Modules with instance variables could be 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
with 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.
### Acceptable use
However, it's not always bad to use 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, then it would be an acceptable use.
We especially allow the case where a single instance variable is used with
`||=` to setup the value. This would look like:
``` ruby
module M
def f
@f ||= true
end
end
```
Unfortunately it's not easy to code more complex rules into the cop, so
we rely on people's best judgement. If we could find another good pattern
we could easily add to the cop, we should do it.
### How to rewrite and avoid disabling this cop
Even if we could just disable the cop, we should avoid doing so. Some code
could be easily rewritten in simple form. Consider this acceptable method:
``` ruby
module Gitlab
module Emoji
def emoji_unicode_version(name)
@emoji_unicode_versions_by_name ||=
JSON.parse(File.read(Rails.root.join('fixtures', 'emojis', 'emoji-unicode-version-map.json')))
@emoji_unicode_versions_by_name[name]
end
end
end
```
This method is totally fine because it's already self-contained. No other
methods should be using `@emoji_unicode_versions_by_name` and we're good.
However it's still offending the cop because it's not just `||=`, and the
cop is not smart enough to judge that this is fine.
On the other hand, we could split this method into two:
``` ruby
module Gitlab
module Emoji
def emoji_unicode_version(name)
emoji_unicode_versions_by_name[name]
end
private
def emoji_unicode_versions_by_name
@emoji_unicode_versions_by_name ||=
JSON.parse(File.read(Rails.root.join('fixtures', 'emojis', 'emoji-unicode-version-map.json')))
end
end
end
```
Now the cop won't complain. Here's a bad example which we could 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 use. 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 whatever includes the module, and those modules which were also
included, making it much easier to track down any issues,
and reducing the chance of having name conflicts.
### How to disable this cop
Put the disabling comment right after your code in the same line:
``` ruby
module M
def violating_method
@f + @g # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
```
If there are multiple lines, you could also enable and disable for a section:
``` ruby
module M
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def violating_method
@f = 0
@g = 1
@h = 2
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
```
Note that you need to enable it at some point, otherwise everything below
won't be checked.
### Things we might need to ignore right now
Because of the way 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 somewhat isolated.
### Instance variables in views
They're bad because we can't easily tell who's using the instance variables
(from controller's point of view) and where we set them up (from partials'
point of view), making it extremely hard to track data dependency.
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 clearer where those values were coming from, and we gain the
benefit to have typo check over using instance variables. In the future,
we should also forbid the use of instance variables in partials.
module EE
module LfsRequest
extend ActiveSupport::Concern
include ::Gitlab::Utils::StrongMemoize
def lfs_forbidden!
if project.above_size_limit? || objects_exceed_repo_limit?
......@@ -13,7 +14,7 @@ module EE
def render_size_error
render(
json: {
message: ::Gitlab::RepositorySizeError.new(project).push_error(@exceeded_limit),
message: ::Gitlab::RepositorySizeError.new(project).push_error(@exceeded_limit), # rubocop:disable Gitlab/ModuleWithInstanceVariables
documentation_url: help_url
},
content_type: "application/vnd.git-lfs+json",
......@@ -23,13 +24,14 @@ module EE
def objects_exceed_repo_limit?
return false unless project.size_limit_enabled?
return @limit_exceeded if defined?(@limit_exceeded)
lfs_push_size = objects.sum { |o| o[:size] }
size_with_lfs_push = project.repository_and_lfs_size + lfs_push_size
strong_memoize(:limit_exceeded) do
lfs_push_size = objects.sum { |o| o[:size] }
size_with_lfs_push = project.repository_and_lfs_size + lfs_push_size
@exceeded_limit = size_with_lfs_push - project.actual_size_limit
@limit_exceeded = @exceeded_limit > 0
@exceeded_limit = size_with_lfs_push - project.actual_size_limit # rubocop:disable Gitlab/ModuleWithInstanceVariables
@exceeded_limit > 0 # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
end
end
......@@ -14,6 +14,6 @@ module SafeMirrorParams
end
def default_mirror_users
[current_user, @project.mirror_user].compact.uniq
[current_user, project.mirror_user].compact.uniq
end
end
module EE::Admin::LogsController
include ::Gitlab::Utils::StrongMemoize
def loggers
raise NotImplementedError unless defined?(super)
@loggers ||= super + [
Gitlab::GeoLogger
]
strong_memoize(:loggers) do
super + [
Gitlab::GeoLogger
]
end
end
end
......@@ -24,6 +24,7 @@ module EE
end
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def update
service = ::Boards::UpdateService.new(parent, current_user, board_params)
......@@ -40,10 +41,11 @@ module EE
end
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def destroy
service = ::Boards::DestroyService.new(parent, current_user)
service.execute(@board)
service.execute(@board) # rubocop:disable Gitlab/ModuleWithInstanceVariables
respond_to do |format|
format.json { head :ok }
......@@ -62,15 +64,15 @@ module EE
end
def find_board
@board = parent.boards.find(params[:id])
@board = parent.boards.find(params[:id]) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def parent
@parent ||= @project || @group
@parent ||= @project || @group # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def boards_path
if @group
if @group # rubocop:disable Gitlab/ModuleWithInstanceVariables
group_boards_path(parent)
else
project_boards_path(parent)
......@@ -78,7 +80,7 @@ module EE
end
def board_path(board)
if @group
if @group # rubocop:disable Gitlab/ModuleWithInstanceVariables
group_board_path(parent, board)
else
project_board_path(parent, board)
......
module EE
module Groups
module GroupMembersController
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def override
@group_member = @group.group_members.find(params[:id])
......@@ -14,6 +15,7 @@ module EE
end
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
protected
......
......@@ -3,7 +3,7 @@ module EE
protected
def fail_login
log_failed_login(@user.username, oauth['provider'])
log_failed_login(@user.username, oauth['provider']) # rubocop:disable Gitlab/ModuleWithInstanceVariables
super
end
......
......@@ -33,7 +33,7 @@ module EE
payload = ::Gitlab::Geo::JwtRequestDecoder.new(request.headers['Authorization']).decode
if payload
@authentication_result = ::Gitlab::Auth::Result.new(nil, project, :geo, [:download_code])
@authentication_result = ::Gitlab::Auth::Result.new(nil, project, :geo, [:download_code]) # rubocop:disable Gitlab/ModuleWithInstanceVariables
return # grant access
end
......
......@@ -11,8 +11,8 @@ module EE
end
def service_desk
@issues = @issuables
@users.push(::User.support_bot)
@issues = @issuables # rubocop:disable Gitlab/ModuleWithInstanceVariables
@users.push(::User.support_bot) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def export_csv
......
......@@ -7,11 +7,11 @@ module EE
private
def set_suggested_approvers
if @merge_request.requires_approve?
@suggested_approvers = ::Gitlab::AuthorityAnalyzer.new(
@merge_request,
@merge_request.author || current_user
).calculate(@merge_request.approvals_required)
if merge_request.requires_approve?
@suggested_approvers = ::Gitlab::AuthorityAnalyzer.new( # rubocop:disable Gitlab/ModuleWithInstanceVariables
merge_request,
merge_request.author || current_user
).calculate(merge_request.approvals_required)
end
end
......@@ -38,12 +38,12 @@ module EE
# Target the MR target project in priority, else it depends whether the project
# is forked.
target_project = if @merge_request
@merge_request.target_project
elsif @project.forked? && @project.id.to_s != mr_params[:target_project_id]
@project.forked_from_project
target_project = if @merge_request # rubocop:disable Gitlab/ModuleWithInstanceVariables
@merge_request.target_project # rubocop:disable Gitlab/ModuleWithInstanceVariables
elsif project.forked? && project.id.to_s != mr_params[:target_project_id]
project.forked_from_project
else
@project
project
end
if mr_params[:approvals_before_merge].to_i <= target_project.approvals_before_merge
......
......@@ -9,19 +9,19 @@ module EE
end
def rebase
RebaseWorker.perform_async(@merge_request.id, current_user.id)
RebaseWorker.perform_async(merge_request.id, current_user.id)
render nothing: true, status: 200
end
def approve
unless @merge_request.can_approve?(current_user)
unless merge_request.can_approve?(current_user)
return render_404
end
::MergeRequests::ApprovalService
.new(project, current_user)
.execute(@merge_request)
.execute(merge_request)
render_approvals_json
end
......@@ -31,10 +31,10 @@ module EE
end
def unapprove
if @merge_request.has_approved?(current_user)
if merge_request.has_approved?(current_user)
::MergeRequests::RemoveApprovalService
.new(project, current_user)
.execute(@merge_request)
.execute(merge_request)
end
render_approvals_json
......@@ -51,7 +51,7 @@ module EE
def render_approvals_json
respond_to do |format|
format.json do
entity = ::API::Entities::MergeRequestApprovals.new(@merge_request, current_user: current_user)
entity = ::API::Entities::MergeRequestApprovals.new(merge_request, current_user: current_user)
render json: entity
end
end
......@@ -65,11 +65,11 @@ module EE
end
def check_user_can_push_to_source_branch!
return access_denied! unless @merge_request.source_branch_exists?
return access_denied! unless merge_request.source_branch_exists?
access_check = ::Gitlab::UserAccess
.new(current_user, project: @merge_request.source_project)
.can_push_to_branch?(@merge_request.source_branch)
.new(current_user, project: merge_request.source_project)
.can_push_to_branch?(merge_request.source_branch)
access_denied! unless access_check
end
......
......@@ -15,15 +15,16 @@ module EE
return unless project.feature_available?(:push_rules)
project.create_push_rule unless project.push_rule
@push_rule = project.push_rule
@push_rule = project.push_rule # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def remote_mirror
return unless project.feature_available?(:repository_mirrors)
@remote_mirror = @project.remote_mirrors.first_or_initialize
@remote_mirror = project.remote_mirrors.first_or_initialize # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def acces_levels_options
super.merge(
selected_merge_access_levels: @protected_branch.merge_access_levels.map { |access_level| access_level.user_id || access_level.access_level },
......@@ -31,11 +32,12 @@ module EE
selected_create_access_levels: @protected_tag.create_access_levels.map { |access_level| access_level.user_id || access_level.access_level }
)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def load_gon_index
super
gon.push(current_project_id: @project.id) if @project
gon.push(current_project_id: project.id) if project
end
end
end
......
......@@ -26,7 +26,7 @@ module EE
return unless ::Gitlab::Geo.secondary?
oauth = ::Gitlab::Geo::OauthSession.new(access_token: session[:access_token])
@geo_logout_state = oauth.generate_logout_state
@geo_logout_state = oauth.generate_logout_state # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def log_failed_login
......
......@@ -10,7 +10,7 @@ module EE
return unless entity_type && entity_id
# Avoiding exception if the record doesn't exist
@entity ||= entity_type.constantize.find_by_id(entity_id)
@entity ||= entity_type.constantize.find_by_id(entity_id) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def present
......
......@@ -5,6 +5,7 @@ module EE
# and be prepended in the `Namespace` model
module Namespace
extend ActiveSupport::Concern
include ::Gitlab::Utils::StrongMemoize
FREE_PLAN = 'free'.freeze
......@@ -67,19 +68,23 @@ module EE
# for a given Namespace plan. This method should consider ancestor groups
# being licensed.
def feature_available?(feature)
@feature_available ||= Hash.new do |h, feature|
h[feature] = load_feature_available(feature)
available_features = strong_memoize(:feature_available) do
Hash.new do |h, feature|
h[feature] = load_feature_available(feature)
end
end
@feature_available[feature]
available_features[feature]
end
def feature_available_in_plan?(feature)
@features_available_in_plan ||= Hash.new do |h, feature|
h[feature] = (plans.map(&:name) & self.class.plans_with_feature(feature)).any?
available_features = strong_memoize(:features_available_in_plan) do
Hash.new do |h, feature|
h[feature] = (plans.map(&:name) & self.class.plans_with_feature(feature)).any?
end
end
@features_available_in_plan[feature]
available_features[feature]
end
# The main difference between the "plan" column and this method is that "plan"
......@@ -128,9 +133,9 @@ module EE
# These helper methods are required to not break the Namespace API.
def plan=(plan_name)
if plan_name.is_a?(String)
@plan_name = plan_name
@plan_name = plan_name # rubocop:disable Gitlab/ModuleWithInstanceVariables
super(Plan.find_by(name: @plan_name))
super(Plan.find_by(name: @plan_name)) # rubocop:disable Gitlab/ModuleWithInstanceVariables
else
super
end
......@@ -149,7 +154,7 @@ module EE
private
def validate_plan_name
if @plan_name.present? && PLANS.exclude?(@plan_name)
if @plan_name.present? && PLANS.exclude?(@plan_name) # rubocop:disable Gitlab/ModuleWithInstanceVariables
errors.add(:plan, 'is not included in the list')
end
end
......
......@@ -5,6 +5,7 @@ module EE
# and be prepended in the `Project` model
module Project
extend ActiveSupport::Concern
include ::Gitlab::Utils::StrongMemoize
prepended do
include Elastic::ProjectsSearch
......@@ -258,8 +259,9 @@ module EE
def deployment_platform(environment: nil)
return super unless environment && feature_available?(:multiple_clusters)
@deployment_platform ||= clusters.enabled.on_environment(environment.name)
.last&.platform_kubernetes
@deployment_platform ||= # rubocop:disable Gitlab/ModuleWithInstanceVariables
clusters.enabled.on_environment(environment.name)
.last&.platform_kubernetes
super # Wildcard or KubernetesService
end
......@@ -323,8 +325,11 @@ module EE
end
def find_path_lock(path, exact_match: false, downstream: false)
@path_lock_finder ||= ::Gitlab::PathLocksFinder.new(self)
@path_lock_finder.find(path, exact_match: exact_match, downstream: downstream)
path_lock_finder = strong_memoize(:path_lock_finder) do
::Gitlab::PathLocksFinder.new(self)
end
path_lock_finder.find(path, exact_match: exact_match, downstream: downstream)
end
def import_url_updated?
......@@ -450,15 +455,15 @@ module EE
end
def disabled_services
return @disabled_services if defined?(@disabled_services)
strong_memoize(:disabled_services) do
disabled_services = []
@disabled_services = []
unless feature_available?(:jenkins_integration)
disabled_services.push('jenkins', 'jenkins_deprecated')
end
unless feature_available?(:jenkins_integration)
@disabled_services.push('jenkins', 'jenkins_deprecated')
disabled_services
end
@disabled_services
end
def remote_mirror_available?
......@@ -479,11 +484,13 @@ module EE
end
def licensed_feature_available?(feature)
@licensed_feature_available ||= Hash.new do |h, feature|
h[feature] = load_licensed_feature_available(feature)
available_features = strong_memoize(:licensed_feature_available) do
Hash.new do |h, feature|
h[feature] = load_licensed_feature_available(feature)
end
end
@licensed_feature_available[feature]
available_features[feature]
end
def load_licensed_feature_available(feature)
......
module EE
module Applications
# rubocop:disable Gitlab/ModuleWithInstanceVariables
module CreateService
def execute(request)
super.tap do |application|
......
module EE
module AuditEventService
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def for_member(member)
action = @details[:action]
old_access_level = @details[:old_access_level]
......@@ -199,5 +200,6 @@ module EE
}
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
end
......@@ -3,8 +3,8 @@ module EE
module Issues
module ListService
def set_parent
if @parent.is_a?(Group)
params[:group_id] = @parent.id
if parent.is_a?(Group)
params[:group_id] = parent.id
else
super
end
......
......@@ -12,11 +12,11 @@ module EE
end
def audit_event_service
::AuditEventService.new(@user,
@user,
::AuditEventService.new(user,
user,
action: :custom,
custom_message: 'Added SSH key',
ip_address: @ip_address)
ip_address: @ip_address) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
end
......
......@@ -25,8 +25,8 @@ module EE
private
def check_size_limit
if @merge_request.target_project.above_size_limit?
message = ::Gitlab::RepositorySizeError.new(@merge_request.target_project).merge_error
if merge_request.target_project.above_size_limit?
message = ::Gitlab::RepositorySizeError.new(merge_request.target_project).merge_error
raise ::MergeRequests::MergeService::MergeError, message
end
......
......@@ -41,7 +41,7 @@ module EE
create_predefined_push_rule
@project.group&.refresh_members_authorized_projects
project.group&.refresh_members_authorized_projects
end
def create_predefined_push_rule
......
......@@ -8,12 +8,12 @@ module EE
super
EE::Audit::ProjectChangesAuditor.new(@current_user, project).execute
EE::Audit::ProjectChangesAuditor.new(current_user, project).execute
::Geo::RepositoryRenamedEventStore.new(
project,
old_path: project.path,
old_path_with_namespace: @old_path
old_path_with_namespace: @old_path # rubocop:disable Gitlab/ModuleWithInstanceVariables
).create
end
end
......
......@@ -26,15 +26,15 @@ module EE
end
def groups_accessible?
group_ids = @merge_params.group_ids + @push_params.group_ids
allowed_groups = @project.invited_groups.where(id: group_ids)
group_ids = @merge_params.group_ids + @push_params.group_ids # rubocop:disable Gitlab/ModuleWithInstanceVariables
allowed_groups = @project.invited_groups.where(id: group_ids) # rubocop:disable Gitlab/ModuleWithInstanceVariables
group_ids.count == allowed_groups.count
end
def users_accessible?
user_ids = @merge_params.user_ids + @push_params.user_ids
allowed_users = @project.team.users.where(id: user_ids)
user_ids = @merge_params.user_ids + @push_params.user_ids # rubocop:disable Gitlab/ModuleWithInstanceVariables
allowed_users = @project.team.users.where(id: user_ids) # rubocop:disable Gitlab/ModuleWithInstanceVariables
user_ids.count == allowed_users.count
end
......
......@@ -3,7 +3,7 @@ module EE
def execute(blocking: true)
result = super
@user_ids.each do |id|
@user_ids.each do |id| # rubocop:disable Gitlab/ModuleWithInstanceVariables
::Gitlab::Database::LoadBalancing::Sticking.stick(:user, id)
end
......
......@@ -6,7 +6,7 @@ module EE
private
def notify_success(user_exists)
notify_new_user(@user, nil) unless user_exists
notify_new_user(@user, nil) unless user_exists # rubocop:disable Gitlab/ModuleWithInstanceVariables
audit_changes(:email, as: 'email address')
audit_changes(:encrypted_password, as: 'password', skip_changes: true)
......
......@@ -2,14 +2,14 @@ module EE
module API
module Helpers
def current_user
return @current_user if defined?(@current_user)
strong_memoize(:current_user) do
user = super
user = super
::Gitlab::Database::LoadBalancing::RackMiddleware
.stick_or_unstick(env, :user, user.id) if user
::Gitlab::Database::LoadBalancing::RackMiddleware
.stick_or_unstick(env, :user, user.id) if user
user
user
end
end
def check_project_feature_available!(feature)
......
......@@ -6,7 +6,7 @@ module EE
private
def project_or_wiki
@project.wiki
project.wiki
end
end
end
......
......@@ -2,24 +2,26 @@ module EE
module Gitlab
module OAuth
module AuthHash
include ::Gitlab::Utils::StrongMemoize
def kerberos_default_realm
::Gitlab::Kerberos::Authentication.kerberos_default_realm
end
def uid
return @ee_uid if defined?(@ee_uid)
strong_memoize(:ee_uid) do
ee_uid = super
ee_uid = super
# For Kerberos, usernames `principal` and `principal@DEFAULT.REALM`
# are equivalent and may be used indifferently, but omniauth_kerberos
# does not normalize them as of version 0.3.0, so add the default
# realm ourselves if appropriate
if provider == 'kerberos' && ee_uid.present?
ee_uid += "@#{kerberos_default_realm}" unless ee_uid.include?('@')
end
# For Kerberos, usernames `principal` and `principal@DEFAULT.REALM`
# are equivalent and may be used indifferently, but omniauth_kerberos
# does not normalize them as of version 0.3.0, so add the default
# realm ourselves if appropriate
if provider == 'kerberos' && ee_uid.present?
ee_uid += "@#{kerberos_default_realm}" unless ee_uid.include?('@')
ee_uid
end
@ee_uid = ee_uid
end
end
end
......
......@@ -44,9 +44,9 @@ module Gitlab
end
def abort_if_no_geo_config!
@geo_config_exists ||= File.exist?(Rails.root.join(GEO_DATABASE_CONFIG))
@geo_config_exists ||= File.exist?(Rails.root.join(GEO_DATABASE_CONFIG)) # rubocop:disable Gitlab/ModuleWithInstanceVariables
unless @geo_config_exists
unless @geo_config_exists # rubocop:disable Gitlab/ModuleWithInstanceVariables
abort("Failed to open #{GEO_DATABASE_CONFIG}. Consult the documentation on how to set up GitLab Geo.")
end
end
......
......@@ -50,11 +50,11 @@ module StdoutReporterWithScenarioLocation
# Override the standard reporter to show filename and line number next to each
# scenario for easy, focused re-runs
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? # rubocop:disable Gitlab/ModuleWithInstanceVariables
name = scenario.name
# 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 # rubocop:disable Gitlab/ModuleWithInstanceVariables
out.puts "\n #{'Scenario:'.green} #{name.light_green.ljust(max_length)}" \
" # #{scenario.feature.filename}:#{scenario.line}"
end
......
......@@ -3,6 +3,7 @@ module API
prepend EE::API::Helpers
include Gitlab::Utils
include Gitlab::Utils::StrongMemoize
include Helpers::Pagination
SUDO_HEADER = "HTTP_SUDO".freeze
......@@ -34,6 +35,11 @@ module API
end
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
# We can't rewrite this with StrongMemoize because `sudo!` would
# actually write to `@current_user`, and `sudo?` would immediately
# call `current_user` again which reads from `@current_user`.
# We should rewrite this in a way that using StrongMemoize is possible
def current_user
return @current_user if defined?(@current_user)
......@@ -47,6 +53,7 @@ module API
@current_user
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def sudo?
initial_current_user != current_user
......@@ -432,7 +439,7 @@ module API
end
def job_token_authentication?
initial_current_user && @job_token_authentication
initial_current_user && @job_token_authentication # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def warden
......@@ -450,10 +457,10 @@ module API
end
def initial_current_user
return @initial_current_user if defined?(@initial_current_user)
return @initial_current_user if defined?(@initial_current_user) # rubocop:disable Gitlab/ModuleWithInstanceVariables
begin
@initial_current_user = Gitlab::Auth::UniqueIpsLimiter.limit_user! { find_current_user! }
@initial_current_user = Gitlab::Auth::UniqueIpsLimiter.limit_user! { find_current_user! } # rubocop:disable Gitlab/ModuleWithInstanceVariables
rescue Gitlab::Auth::UnauthorizedError
unauthorized!
end
......@@ -477,7 +484,7 @@ module API
sudoed_user = find_user(sudo_identifier)
not_found!("User with ID or username '#{sudo_identifier}'") unless sudoed_user
@current_user = sudoed_user
@current_user = sudoed_user # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def sudo_identifier
......
......@@ -6,18 +6,16 @@ module API
'git-upload-pack' => [:ssh_upload_pack, Gitlab::GitalyClient::MigrationStatus::OPT_OUT]
}.freeze
attr_reader :redirected_path
def wiki?
set_project unless defined?(@wiki)
@wiki
set_project unless defined?(@wiki) # rubocop:disable Gitlab/ModuleWithInstanceVariables
@wiki # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def project
set_project unless defined?(@project)
@project
end
def redirected_path
@redirected_path
set_project unless defined?(@project) # rubocop:disable Gitlab/ModuleWithInstanceVariables
@project # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def ssh_authentication_abilities
......@@ -69,6 +67,7 @@ module API
private
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def set_project
if params[:gl_repository]
@project, @wiki = Gitlab::GlRepository.parse(params[:gl_repository])
......@@ -77,6 +76,7 @@ module API
@project, @wiki, @redirected_path = Gitlab::RepoPath.parse(params[:project])
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
# Project id to pass between components that don't share/don't have
# access to the same filesystem mounts
......
......@@ -3,7 +3,7 @@ module EE
module Changes
def audit_changes(column, options = {})
column = options[:column] || column
@model = options[:model]
@model = options[:model] # rubocop:disable Gitlab/ModuleWithInstanceVariables
return unless changed?(column)
......@@ -39,7 +39,7 @@ module EE
end
def audit_event(options)
::AuditEventService.new(@current_user, model, options)
::AuditEventService.new(@current_user, model, options) # rubocop:disable Gitlab/ModuleWithInstanceVariables
.for_changes.security_event
end
end
......
......@@ -40,7 +40,7 @@ module ExtractsPath
def extract_ref(id)
pair = ['', '']
return pair unless @project
return pair unless @project # rubocop:disable Gitlab/ModuleWithInstanceVariables
if id =~ /^(\h{40})(.+)/
# If the ref appears to be a SHA, we're done, just split the string
......@@ -104,6 +104,7 @@ module ExtractsPath
#
# Automatically renders `not_found!` if a valid tree path could not be
# resolved (e.g., when a user inserts an invalid path or ref).
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def assign_ref_vars
# assign allowed options
allowed_options = ["filter_ref"]
......@@ -131,9 +132,10 @@ module ExtractsPath
rescue RuntimeError, NoMethodError, InvalidPathError
render_404
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def tree
@tree ||= @repo.tree(@commit.id, @path)
@tree ||= @repo.tree(@commit.id, @path) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
private
......@@ -146,8 +148,8 @@ module ExtractsPath
end
def ref_names
return [] unless @project
return [] unless @project # rubocop:disable Gitlab/ModuleWithInstanceVariables
@ref_names ||= @project.repository.ref_names
@ref_names ||= @project.repository.ref_names # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
......@@ -57,7 +57,7 @@ module Gitlab
job = ::Ci::Build.find_by(token: token)
raise UnauthorizedError unless job
@job_token_authentication = true
@job_token_authentication = true # rubocop:disable Gitlab/ModuleWithInstanceVariables
job.user
end
......
......@@ -45,11 +45,13 @@ module Gitlab
klass.prepend(extension)
end
attr_accessor :request_cache_key_block
def request_cache_key(&block)
if block_given?
@request_cache_key = block
self.request_cache_key_block = block
else
@request_cache_key
request_cache_key_block
end
end
......
......@@ -6,7 +6,7 @@ module Gitlab
query
.group("DATE(#{::Ci::Pipeline.table_name}.created_at)")
.count(:created_at)
.transform_keys { |date| date.strftime(@format) }
.transform_keys { |date| date.strftime(@format) } # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def interval_step
......
......@@ -29,15 +29,15 @@ module Gitlab
self.class.nodes.each do |key, factory|
factory
.value(@config[key])
.value(config[key])
.with(key: key, parent: self)
@entries[key] = factory.create!
entries[key] = factory.create!
end
yield if block_given?
@entries.each_value do |entry|
entries.each_value do |entry|
entry.compose!(deps)
end
end
......@@ -59,13 +59,13 @@ module Gitlab
def helpers(*nodes)
nodes.each do |symbol|
define_method("#{symbol}_defined?") do
@entries[symbol]&.specified?
entries[symbol]&.specified?
end
define_method("#{symbol}_value") do
return unless @entries[symbol] && @entries[symbol].valid?
return unless entries[symbol] && entries[symbol].valid?
@entries[symbol].value
entries[symbol].value
end
end
end
......
......@@ -90,6 +90,12 @@ module Gitlab
def self.aspects
@aspects ||= []
end
private
def entries
@entries
end
end
end
end
......
......@@ -13,7 +13,7 @@ module Gitlab
end
def errors
@validator.messages + descendants.flat_map(&:errors)
@validator.messages + descendants.flat_map(&:errors) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
class_methods do
......
......@@ -53,7 +53,7 @@ module Gitlab
end
def in_memory_application_settings
@in_memory_application_settings ||= ::ApplicationSetting.new(::ApplicationSetting.defaults)
@in_memory_application_settings ||= ::ApplicationSetting.new(::ApplicationSetting.defaults) # rubocop:disable Gitlab/ModuleWithInstanceVariables
rescue ActiveRecord::StatementInvalid, ActiveRecord::UnknownAttributeError
# In case migrations the application_settings table is not created yet,
# we fallback to a simple OpenStruct
......
......@@ -56,7 +56,9 @@ module Gitlab
end
def allowed_ids
nil
@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
......
......@@ -14,9 +14,9 @@ module Gitlab
def stage_query
query = mr_closing_issues_table.join(issue_table).on(issue_table[:id].eq(mr_closing_issues_table[:issue_id]))
.join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id]))
.where(issue_table[:project_id].eq(@project.id))
.where(issue_table[:project_id].eq(@project.id)) # rubocop:disable Gitlab/ModuleWithInstanceVariables
.where(issue_table[:deleted_at].eq(nil))
.where(issue_table[:created_at].gteq(@options[:from]))
.where(issue_table[:created_at].gteq(@options[:from])) # rubocop:disable Gitlab/ModuleWithInstanceVariables
# Load merge_requests
query = query.join(mr_table, Arel::Nodes::OuterJoin)
......
module Gitlab
module CycleAnalytics
class CodeEventFetcher < BaseEventFetcher
include MergeRequestAllowed
def initialize(*args)
@projections = [mr_table[:title],
mr_table[:iid],
......@@ -20,6 +18,10 @@ module Gitlab
def serialize(event)
AnalyticsMergeRequestSerializer.new(project: @project).represent(event)
end
def allowed_ids_finder_class
MergeRequestsFinder
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 CycleAnalytics
class IssueEventFetcher < BaseEventFetcher
include IssueAllowed
def initialize(*args)
@projections = [issue_table[:title],
issue_table[:iid],
......@@ -18,6 +16,10 @@ module Gitlab
def serialize(event)
AnalyticsIssueSerializer.new(project: @project).represent(event)
end
def allowed_ids_finder_class
IssuesFinder
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
......@@ -18,6 +18,10 @@ module Gitlab
private
def allowed_ids
nil
end
def merge_request_diff_commits
@merge_request_diff_commits ||=
MergeRequestDiffCommit
......
......@@ -2,7 +2,9 @@ module Gitlab
module CycleAnalytics
module ProductionHelper
def stage_query
super.where(mr_metrics_table[:first_deployed_to_production_at].gteq(@options[:from]))
super
.where(mr_metrics_table[:first_deployed_to_production_at]
.gteq(@options[:from])) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
end
......
module Gitlab
module CycleAnalytics
class ReviewEventFetcher < BaseEventFetcher
include MergeRequestAllowed
def initialize(*args)
@projections = [mr_table[:title],
mr_table[:iid],
......@@ -14,9 +12,15 @@ module Gitlab
super(*args)
end
private
def serialize(event)
AnalyticsMergeRequestSerializer.new(project: @project).represent(event)
end
def allowed_ids_finder_class
MergeRequestsFinder
end
end
end
end
......@@ -22,6 +22,10 @@ module Gitlab
private
def allowed_ids
nil
end
def serialize(event)
AnalyticsBuildSerializer.new.represent(event['build'])
end
......
......@@ -6,7 +6,7 @@ module Gitlab
module Routable
def full_path
if route && route.path.present?
@full_path ||= route.path
@full_path ||= route.path # rubocop:disable Gitlab/ModuleWithInstanceVariables
else
update_route if persisted?
......@@ -30,7 +30,7 @@ module Gitlab
def prepare_route
route || build_route(source: self)
route.path = build_full_path
@full_path = nil
@full_path = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
......
......@@ -31,8 +31,7 @@ module Gitlab
end
def emoji_unicode_version(name)
@emoji_unicode_versions_by_name ||= JSON.parse(File.read(Rails.root.join('fixtures', 'emojis', 'emoji-unicode-version-map.json')))
@emoji_unicode_versions_by_name[name]
emoji_unicode_versions_by_name[name]
end
def normalize_emoji_name(name)
......@@ -56,5 +55,12 @@ module Gitlab
ActionController::Base.helpers.content_tag('gl-emoji', emoji_info['moji'], title: emoji_info['description'], data: data)
end
private
def emoji_unicode_versions_by_name
@emoji_unicode_versions_by_name ||=
JSON.parse(File.read(Rails.root.join('fixtures', 'emojis', 'emoji-unicode-version-map.json')))
end
end
end
......@@ -16,8 +16,8 @@ module Gitlab
vars['PWD'] = path
options = { chdir: path }
@cmd_output = ""
@cmd_status = 0
cmd_output = ""
cmd_status = 0
Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
yield(stdin) if block_given?
stdin.close
......@@ -25,14 +25,14 @@ module Gitlab
if lazy_block
return lazy_block.call(stdout.lazy)
else
@cmd_output << stdout.read
cmd_output << stdout.read
end
@cmd_output << stderr.read
@cmd_status = wait_thr.value.exitstatus
cmd_output << stderr.read
cmd_status = wait_thr.value.exitstatus
end
[@cmd_output, @cmd_status]
[cmd_output, cmd_status]
end
def popen_with_timeout(cmd, timeout, path, vars = {})
......
......@@ -32,7 +32,7 @@ module Gitlab
def execute(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 Gitlab/ModuleWithInstanceVariables
status.zero?
end
......
......@@ -156,6 +156,7 @@ module Gitlab
# When enabled this should be set before being used as the usual pattern
# "@foo ||= bar" is _not_ thread-safe.
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def pool
if influx_metrics_enabled?
if @pool.nil?
......@@ -172,6 +173,7 @@ module Gitlab
@pool
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
end
end
......@@ -4,6 +4,7 @@ module Gitlab
module Metrics
module Prometheus
include Gitlab::CurrentSettings
include Gitlab::Utils::StrongMemoize
REGISTRY_MUTEX = Mutex.new
PROVIDER_MUTEX = Mutex.new
......@@ -17,16 +18,18 @@ module Gitlab
end
def prometheus_metrics_enabled?
return @prometheus_metrics_enabled if defined?(@prometheus_metrics_enabled)
@prometheus_metrics_enabled = prometheus_metrics_enabled_unmemoized
strong_memoize(:prometheus_metrics_enabled) do
prometheus_metrics_enabled_unmemoized
end
end
def registry
return @registry if @registry
REGISTRY_MUTEX.synchronize do
@registry ||= ::Prometheus::Client.registry
strong_memoize(:registry) do
REGISTRY_MUTEX.synchronize do
strong_memoize(:registry) do
::Prometheus::Client.registry
end
end
end
end
......
......@@ -11,37 +11,41 @@ module Gitlab
end
def project
@resource.project
resource.project
end
def author
@resource.author
resource.author
end
def fields
[
{
title: "Assignee",
value: @resource.assignees.any? ? @resource.assignees.first.name : "_None_",
value: resource.assignees.any? ? resource.assignees.first.name : "_None_",
short: true
},
{
title: "Milestone",
value: @resource.milestone ? @resource.milestone.title : "_None_",
value: resource.milestone ? resource.milestone.title : "_None_",
short: true
},
{
title: "Labels",
value: @resource.labels.any? ? @resource.label_names.join(', ') : "_None_",
value: resource.labels.any? ? resource.label_names.join(', ') : "_None_",
short: true
},
{
title: "Weight",
value: @resource.weight? ? @resource.weight : "_None_",
value: resource.weight? ? resource.weight : "_None_",
short: true
}
]
end
private
attr_reader :resource
end
end
end
......
......@@ -23,6 +23,7 @@ namespace :gettext do
desc 'Lint all po files in `locale/'
task lint: :environment do
require 'simple_po_parser'
require 'gitlab/utils'
FastGettext.silence_errors
files = Dir.glob(Rails.root.join('locale/*/gitlab.po'))
......
require 'rainbow/ext/string'
require 'gitlab/utils/strong_memoize'
module Gitlab
TaskFailedError = Class.new(StandardError)
TaskAbortedByUserError = Class.new(StandardError)
module TaskHelpers
include Gitlab::Utils::StrongMemoize
extend self
# Ask if the user wants to continue
......@@ -105,16 +108,16 @@ module Gitlab
end
def gitlab_user?
return @is_gitlab_user unless @is_gitlab_user.nil?
current_user = run_command(%w(whoami)).chomp
@is_gitlab_user = current_user == gitlab_user
strong_memoize(:is_gitlab_user) do
current_user = run_command(%w(whoami)).chomp
current_user == gitlab_user
end
end
def warn_user_is_not_gitlab
return if @warned_user_not_gitlab
return if gitlab_user?
unless gitlab_user?
strong_memoize(:warned_user_not_gitlab) do
current_user = run_command(%w(whoami)).chomp
puts " Warning ".color(:black).background(:yellow)
......@@ -122,8 +125,6 @@ module Gitlab
puts " Things may work\/fail for the wrong reasons."
puts " For correct results you should run this as user #{gitlab_user.color(:magenta)}."
puts ""
@warned_user_not_gitlab = true
end
end
......
......@@ -6,13 +6,15 @@ module QA
module Scenario
extend self
attr_reader :attributes
def attributes
@attributes ||= {}
end
def define(attribute, value)
(@attributes ||= {}).store(attribute.to_sym, value)
attributes.store(attribute.to_sym, value)
define_singleton_method(attribute) do
@attributes[attribute.to_sym].tap do |value|
attributes[attribute.to_sym].tap do |value|
if value.to_s.empty?
raise ArgumentError, "Empty `#{attribute}` attribute!"
end
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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