mirror of https://github.com/rails/rails
Concerns learn to be prepended
This commit is contained in:
parent
6e2ca1a186
commit
ba2bea5e07
|
@ -106,6 +106,12 @@ module ActiveSupport
|
|||
end
|
||||
end
|
||||
|
||||
class MultiplePrependBlocks < StandardError #:nodoc:
|
||||
def initialize
|
||||
super "Cannot define multiple 'prepended' blocks for a Concern"
|
||||
end
|
||||
end
|
||||
|
||||
def self.extended(base) #:nodoc:
|
||||
base.instance_variable_set(:@_dependencies, [])
|
||||
end
|
||||
|
@ -123,6 +129,19 @@ module ActiveSupport
|
|||
end
|
||||
end
|
||||
|
||||
def prepend_features(base) #:nodoc:
|
||||
if base.instance_variable_defined?(:@_dependencies)
|
||||
base.instance_variable_get(:@_dependencies).unshift self
|
||||
false
|
||||
else
|
||||
return false if base < self
|
||||
@_dependencies.each { |dep| base.prepend(dep) }
|
||||
super
|
||||
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
|
||||
base.class_eval(&@_prepended_block) if instance_variable_defined?(:@_prepended_block)
|
||||
end
|
||||
end
|
||||
|
||||
# Evaluate given block in context of base class,
|
||||
# so that you can write class macros here.
|
||||
# When you define more than one +included+ block, it raises an exception.
|
||||
|
@ -140,6 +159,23 @@ module ActiveSupport
|
|||
end
|
||||
end
|
||||
|
||||
# Evaluate given block in context of base class,
|
||||
# so that you can write class macros here.
|
||||
# When you define more than one +prepended+ block, it raises an exception.
|
||||
def prepended(base = nil, &block)
|
||||
if base.nil?
|
||||
if instance_variable_defined?(:@_prepended_block)
|
||||
if @_prepended_block.source_location != block.source_location
|
||||
raise MultiplePrependBlocks
|
||||
end
|
||||
else
|
||||
@_prepended_block = block
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
# Define class methods from given block.
|
||||
# You can define private class methods as well.
|
||||
#
|
||||
|
|
|
@ -19,12 +19,24 @@ class ConcernTest < ActiveSupport::TestCase
|
|||
def included_ran
|
||||
@included_ran
|
||||
end
|
||||
|
||||
def prepended_ran=(value)
|
||||
@prepended_ran = value
|
||||
end
|
||||
|
||||
def prepended_ran
|
||||
@prepended_ran
|
||||
end
|
||||
end
|
||||
|
||||
included do
|
||||
self.included_ran = true
|
||||
end
|
||||
|
||||
prepended do
|
||||
self.prepended_ran = true
|
||||
end
|
||||
|
||||
def baz
|
||||
"baz"
|
||||
end
|
||||
|
@ -71,12 +83,24 @@ class ConcernTest < ActiveSupport::TestCase
|
|||
assert_includes @klass.included_modules, ConcernTest::Baz
|
||||
end
|
||||
|
||||
def test_module_is_prepended_normally
|
||||
@klass.prepend(Baz)
|
||||
assert_equal "baz", @klass.new.baz
|
||||
assert_includes @klass.included_modules, ConcernTest::Baz
|
||||
end
|
||||
|
||||
def test_class_methods_are_extended
|
||||
@klass.include(Baz)
|
||||
assert_equal "baz", @klass.baz
|
||||
assert_equal ConcernTest::Baz::ClassMethods, (class << @klass; included_modules; end)[0]
|
||||
end
|
||||
|
||||
def test_class_methods_are_extended_when_prepended
|
||||
@klass.prepend(Baz)
|
||||
assert_equal "baz", @klass.baz
|
||||
assert_equal ConcernTest::Baz::ClassMethods, (class << @klass; included_modules; end)[0]
|
||||
end
|
||||
|
||||
def test_class_methods_are_extended_only_on_expected_objects
|
||||
::Object.include(Qux)
|
||||
Object.extend(Qux::ClassMethods)
|
||||
|
@ -102,6 +126,21 @@ class ConcernTest < ActiveSupport::TestCase
|
|||
assert_equal true, @klass.included_ran
|
||||
end
|
||||
|
||||
def test_included_block_is_not_ran_when_prepended
|
||||
@klass.prepend(Baz)
|
||||
assert_nil @klass.included_ran
|
||||
end
|
||||
|
||||
def test_prepended_block_is_ran
|
||||
@klass.prepend(Baz)
|
||||
assert_equal true, @klass.prepended_ran
|
||||
end
|
||||
|
||||
def test_prepended_block_is_not_ran_when_included
|
||||
@klass.include(Baz)
|
||||
assert_nil @klass.prepended_ran
|
||||
end
|
||||
|
||||
def test_modules_dependencies_are_met
|
||||
@klass.include(Bar)
|
||||
assert_equal "bar", @klass.new.bar
|
||||
|
@ -115,6 +154,11 @@ class ConcernTest < ActiveSupport::TestCase
|
|||
assert_equal [ConcernTest::Foo, ConcernTest::Bar, ConcernTest::Baz], @klass.included_modules[0..2]
|
||||
end
|
||||
|
||||
def test_dependencies_with_multiple_modules_when_prepended
|
||||
@klass.prepend(Foo)
|
||||
assert_equal [ConcernTest::Foo, ConcernTest::Bar, ConcernTest::Baz], @klass.included_modules[0..2]
|
||||
end
|
||||
|
||||
def test_raise_on_multiple_included_calls
|
||||
assert_raises(ActiveSupport::Concern::MultipleIncludedBlocks) do
|
||||
Module.new do
|
||||
|
@ -129,7 +173,21 @@ class ConcernTest < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_no_raise_on_same_included_call
|
||||
def test_raise_on_multiple_prepended_calls
|
||||
assert_raises(ActiveSupport::Concern::MultiplePrependBlocks) do
|
||||
Module.new do
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
prepended do
|
||||
end
|
||||
|
||||
prepended do
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_no_raise_on_same_included_or_prepended_call
|
||||
assert_nothing_raised do
|
||||
2.times do
|
||||
load File.expand_path("../fixtures/concern/some_concern.rb", __FILE__)
|
||||
|
|
|
@ -8,4 +8,8 @@ module SomeConcern
|
|||
included do
|
||||
# shouldn't raise when module is loaded more than once
|
||||
end
|
||||
|
||||
prepended do
|
||||
# shouldn't raise when module is loaded more than once
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue