Commit 71fefe14 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Properly implement prepending for Concern

parent c56f2b96
# 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
# 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
# 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
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)
......
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