Properly implement prepending for Concern
This commit is contained in:
parent
c56f2b9615
commit
71fefe1474
|
|
@ -1,25 +1,7 @@
|
|||
# This module is based on: https://gist.github.com/bcardarella/5735987
|
||||
|
||||
module Prependable
|
||||
def prepend_features(base)
|
||||
if base.instance_variable_defined?(:@_dependencies)
|
||||
base.instance_variable_get(:@_dependencies) << self
|
||||
false
|
||||
else
|
||||
return false if base < self
|
||||
|
||||
super
|
||||
base.singleton_class.send(:prepend, const_get('ClassMethods')) if const_defined?(:ClassMethods)
|
||||
@_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
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ActiveSupport
|
||||
module Concern
|
||||
prepend Prependable
|
||||
|
||||
alias_method :prepended, :included
|
||||
prepend Gitlab::Patch::Prependable
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
module Gitlab
|
||||
module Patch
|
||||
module Prependable
|
||||
class MultiplePrependedBlocks < StandardError
|
||||
def initialize
|
||||
super "Cannot define multiple 'prepended' blocks for a Concern"
|
||||
end
|
||||
end
|
||||
|
||||
module MetaConcern
|
||||
def extended(base)
|
||||
super
|
||||
base.instance_variable_set(:@_prepend_dependencies, [])
|
||||
end
|
||||
end
|
||||
|
||||
def self.prepended(base)
|
||||
super
|
||||
base.singleton_class.prepend MetaConcern
|
||||
end
|
||||
|
||||
def append_features(base)
|
||||
super
|
||||
|
||||
prepend_features(base)
|
||||
end
|
||||
|
||||
def prepend_features(base)
|
||||
if base.instance_variable_defined?(:@_prepend_dependencies)
|
||||
base.instance_variable_get(:@_prepend_dependencies) << self
|
||||
false
|
||||
else
|
||||
return false if prepended?(base)
|
||||
|
||||
@_prepend_dependencies.each { |dep| base.prepend(dep) }
|
||||
|
||||
super
|
||||
|
||||
if const_defined?(:ClassMethods)
|
||||
base.singleton_class.prepend const_get(:ClassMethods)
|
||||
end
|
||||
|
||||
if instance_variable_defined?(:@_prepended_block)
|
||||
base.class_eval(&@_prepended_block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def prepended(base = nil, &block)
|
||||
if base.nil?
|
||||
raise MultiplePrependedBlocks if
|
||||
instance_variable_defined?(:@_prepended_block)
|
||||
|
||||
@_prepended_block = block
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def prepended?(base)
|
||||
index = base.ancestors.index(base)
|
||||
|
||||
@_prepend_dependencies.index(self) ||
|
||||
base.ancestors[0...index].index(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
|
||||
# Patching ActiveSupport::Concern
|
||||
require_relative '../../../../config/initializers/0_as_concern'
|
||||
|
||||
describe Gitlab::Patch::Prependable do
|
||||
let(:prepended_modules) { [] }
|
||||
|
||||
let(:ee) do
|
||||
# So that block in Module.new could see them
|
||||
prepended_modules_ = prepended_modules
|
||||
|
||||
Module.new do
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
def class_name
|
||||
super.tr('C', 'E')
|
||||
end
|
||||
end
|
||||
|
||||
this = self
|
||||
prepended do
|
||||
prepended_modules_ << this
|
||||
end
|
||||
|
||||
def name
|
||||
super.tr('c', 'e')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
let(:ce) do
|
||||
prepended_modules_ = prepended_modules
|
||||
ee_ = ee
|
||||
|
||||
Module.new do
|
||||
extend ActiveSupport::Concern
|
||||
prepend ee_
|
||||
|
||||
class_methods do
|
||||
def class_name
|
||||
'CE'
|
||||
end
|
||||
end
|
||||
|
||||
this = self
|
||||
prepended do
|
||||
prepended_modules_ << this
|
||||
end
|
||||
|
||||
def name
|
||||
'ce'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'a class including a concern prepending a concern' do
|
||||
subject { Class.new.include(ce) }
|
||||
|
||||
it 'returns values from prepended module ee' do
|
||||
expect(subject.new.name).to eq('ee')
|
||||
expect(subject.class_name).to eq('EE')
|
||||
end
|
||||
|
||||
it 'prepends only once' do
|
||||
ce.prepend(ee)
|
||||
ce.prepend(ee)
|
||||
|
||||
subject
|
||||
|
||||
expect(prepended_modules).to eq([ee, ce])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'a class prepending a concern prepending a concern' do
|
||||
subject { Class.new.prepend(ce) }
|
||||
|
||||
it 'returns values from prepended module ce' do
|
||||
expect(subject.new.name).to eq('ce')
|
||||
expect(subject.class_name).to eq('CE')
|
||||
end
|
||||
|
||||
it 'prepends only once' do
|
||||
subject.prepend(ce)
|
||||
|
||||
expect(prepended_modules).to eq([ee, ce])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'a class prepending a concern' do
|
||||
subject do
|
||||
ee_ = ee
|
||||
|
||||
Class.new do
|
||||
prepend ee_
|
||||
|
||||
def self.class_name
|
||||
'CE'
|
||||
end
|
||||
|
||||
def name
|
||||
'ce'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns values from prepended module ee' do
|
||||
expect(subject.new.name).to eq('ee')
|
||||
expect(subject.class_name).to eq('EE')
|
||||
end
|
||||
|
||||
it 'prepends only once' do
|
||||
subject.prepend(ee)
|
||||
|
||||
expect(prepended_modules).to eq([ee])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
require 'active_support/core_ext/hash/transform_values'
|
||||
require 'active_support/hash_with_indifferent_access'
|
||||
require 'active_support/dependencies'
|
||||
|
||||
require_dependency 'gitlab'
|
||||
|
||||
module StubConfiguration
|
||||
def stub_application_setting(messages)
|
||||
|
|
|
|||
Loading…
Reference in New Issue