Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #10 from misfo/fix-new-method-for-classes-without-…

…object-as-their-superclass

Prevent instantiation of abstract classes whose superclass isn't Object
  • Loading branch information...
commit 6b1cc801676142c28057e2c51894a284537c560b 2 parents 5d47011 + 667fbda
@dkubb authored
View
2  config/reek.yml
@@ -47,7 +47,7 @@ UncommunicativeModuleName:
NestedIterators:
ignore_iterators: []
exclude: [
- 'AbstractType::ClassMethods#create_abstract_singleton_method'
+ 'AbstractType::AbstractMethodDeclarations#create_abstract_singleton_method'
]
enabled: true
max_allowed_nesting: 1
View
40 lib/abstract_type.rb
@@ -13,31 +13,35 @@ module AbstractType
# @api private
def self.included(descendant)
super
- descendant.extend(ClassMethods)
+ create_new_method(descendant)
+ descendant.extend(AbstractMethodDeclarations)
end
private_class_method :included
- module ClassMethods
-
- # Instantiate a new object
- #
- # Ensures that the instance cannot be of the abstract type
- # and must be a descendant.
- #
- # @example
- # object = AbstractType.new
- #
- # @return [Object]
- #
- # @api public
- def new(*)
- if superclass.equal?(Object)
+ # Define the new method on the abstract type
+ #
+ # Ensures that the instance cannot be of the abstract type
+ # and must be a descendant.
+ #
+ # @param [Class] abstract_class
+ #
+ # @return [undefined]
+ #
+ # @api private
+ def self.create_new_method(abstract_class)
+ abstract_class.define_singleton_method(:new) do |*args, &block|
+ if equal?(abstract_class)
raise NotImplementedError, "#{inspect} is an abstract type"
else
- super
+ super(*args, &block)
end
end
+ end
+
+ private_class_method :create_new_method
+
+ module AbstractMethodDeclarations
# Create abstract instance methods
#
@@ -109,7 +113,7 @@ def create_abstract_instance_method(name)
end
end
- end # module ClassMethods
+ end # module AbstractMethodDeclarations
end # module AbstractType
require 'abstract_type/version'
View
20 spec/shared/create_new_method_shared_spec.rb
@@ -0,0 +1,20 @@
+# encoding: utf-8
+
+shared_examples 'AbstractType.create_new_method' do
+ context 'called on a subclass' do
+ let(:object) { Class.new(abstract_type) }
+
+ it { should be_instance_of(object) }
+ end
+
+ context 'called on the class' do
+ let(:object) { abstract_type }
+
+ specify do
+ expect { subject }.to raise_error(
+ NotImplementedError,
+ "#{object} is an abstract type"
+ )
+ end
+ end
+end
View
2  ...ype/class_methods/abstract_method_spec.rb → ...thod_declarations/abstract_method_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe AbstractType::ClassMethods, '#abstract_method' do
+describe AbstractType::AbstractMethodDeclarations, '#abstract_method' do
subject { object.abstract_method(:some_method) }
let(:object) { Class.new { include AbstractType } }
View
3  ...methods/abstract_singleton_method_spec.rb → ...rations/abstract_singleton_method_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-describe AbstractType::ClassMethods, '#abstract_singleton_method' do
+describe AbstractType::AbstractMethodDeclarations,
+ '#abstract_singleton_method' do
subject { object.abstract_singleton_method(:some_method) }
let(:object) { Class.new { include AbstractType } }
View
12 ...tract_type/class_methods/included_spec.rb → ...ract_method_declarations/included_spec.rb
@@ -9,9 +9,17 @@
let(:klass) { Class.new }
it 'extends the klass' do
- expect(klass.singleton_class).to_not include(described_class::ClassMethods)
+ expect(klass.singleton_class)
+ .to_not include(described_class::AbstractMethodDeclarations)
klass.send(:include, subject)
- expect(klass.singleton_class).to include(described_class::ClassMethods)
+ expect(klass.singleton_class)
+ .to include(described_class::AbstractMethodDeclarations)
+ end
+
+ it 'overrides the new singleton method' do
+ expect(klass.method(:new).owner).to eq(Class)
+ klass.send(:include, subject)
+ expect(klass.method(:new).owner).to eq(klass.singleton_class)
end
it 'delegates to the ancestor' do
View
59 spec/unit/abstract_type/class_methods/new_spec.rb
@@ -1,59 +0,0 @@
-# encoding: utf-8
-
-require 'spec_helper'
-
-describe AbstractType::ClassMethods, '#new' do
- context 'with arguments' do
- subject { object.new(:foo) }
-
- let(:abstract_type) do
- Class.new do
- include AbstractType
-
- def initialize(foo)
- @foo = foo
- end
- end
- end
-
- context 'called on a subclass' do
- let(:object) { Class.new(abstract_type) }
-
- it { should be_instance_of(object) }
- end
-
- context 'called on the class' do
- let(:object) { abstract_type }
-
- specify do
- expect { subject }.to raise_error(
- NotImplementedError,
- "#{object} is an abstract type"
- )
- end
- end
- end
-
- context 'without arguments' do
- subject { object.new }
-
- let(:abstract_type) { Class.new { include AbstractType } }
-
- context 'called on a subclass' do
- let(:object) { Class.new(abstract_type) }
-
- it { should be_instance_of(object) }
- end
-
- context 'called on the class' do
- let(:object) { abstract_type }
-
- specify do
- expect { subject }.to raise_error(
- NotImplementedError,
- "#{object} is an abstract type"
- )
- end
- end
- end
-end
View
54 spec/unit/abstract_type/module_methods/create_new_method_spec.rb
@@ -0,0 +1,54 @@
+# encoding: utf-8
+
+require 'spec_helper'
+
+describe AbstractType, '.create_new_method' do
+ context 'with arguments' do
+ subject { object.new(:foo) }
+
+ let(:abstract_type) do
+ Class.new do
+ include AbstractType
+
+ def initialize(foo)
+ @foo = foo
+ end
+ end
+ end
+
+ it_behaves_like 'AbstractType.create_new_method'
+ end
+
+ context 'with a block' do
+ subject { object.new(:foo) { nil } }
+
+ let(:abstract_type) do
+ Class.new do
+ include AbstractType
+
+ def initialize(foo)
+ @foo = foo
+ yield
+ end
+ end
+ end
+
+ it_behaves_like 'AbstractType.create_new_method'
+ end
+
+ context 'without arguments' do
+ subject { object.new }
+
+ let(:abstract_type) { Class.new { include AbstractType } }
+
+ it_behaves_like 'AbstractType.create_new_method'
+ end
+
+ context 'on an class that doesn\'t have Object as its superclass' do
+ subject { object.new }
+
+ let(:abstract_type) { Class.new(RuntimeError) { include AbstractType } }
+
+ it_behaves_like 'AbstractType.create_new_method'
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.