Skip to content

Commit

Permalink
Prevent instantiation of abstract classes whose superclass isn't Object
Browse files Browse the repository at this point in the history
Fixes #9
  • Loading branch information
misfo committed Oct 4, 2013
1 parent c42b767 commit 6ad24c7
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 24 deletions.
2 changes: 1 addition & 1 deletion config/reek.yml
Expand Up @@ -47,7 +47,7 @@ UncommunicativeModuleName:
NestedIterators: NestedIterators:
ignore_iterators: [] ignore_iterators: []
exclude: [ exclude: [
'AbstractType::ClassMethods#create_abstract_singleton_method' 'AbstractType::AbstractMethodDeclarations#create_abstract_singleton_method'
] ]
enabled: true enabled: true
max_allowed_nesting: 1 max_allowed_nesting: 1
Expand Down
40 changes: 22 additions & 18 deletions lib/abstract_type.rb
Expand Up @@ -13,31 +13,35 @@ module AbstractType
# @api private # @api private
def self.included(descendant) def self.included(descendant)
super super
descendant.extend(ClassMethods) create_new_method(descendant)
descendant.extend(AbstractMethodDeclarations)
end end


private_class_method :included private_class_method :included


module ClassMethods # Define the new method on the abstract type

#
# Instantiate a new object # Ensures that the instance cannot be of the abstract type
# # and must be a descendant.
# Ensures that the instance cannot be of the abstract type #
# and must be a descendant. # @param [Class] abstract_class
# #
# @example # @return [undefined]
# object = AbstractType.new #
# # @api private
# @return [Object] def self.create_new_method(abstract_class)
# abstract_class.define_singleton_method(:new) do |*args, &block|
# @api public if equal?(abstract_class)
def new(*)
if superclass.equal?(Object)
raise NotImplementedError, "#{inspect} is an abstract type" raise NotImplementedError, "#{inspect} is an abstract type"
else else
super super(*args, &block)
end end
end end
end

private_class_method :create_new_method

module AbstractMethodDeclarations


# Create abstract instance methods # Create abstract instance methods
# #
Expand Down Expand Up @@ -109,5 +113,5 @@ def create_abstract_instance_method(name)
end end
end end


end # module ClassMethods end # module AbstractMethodDeclarations
end # module AbstractType end # module AbstractType
Expand Up @@ -2,7 +2,7 @@


require 'spec_helper' require 'spec_helper'


describe AbstractType::ClassMethods, '#abstract_method' do describe AbstractType::AbstractMethodDeclarations, '#abstract_method' do
subject { object.abstract_method(:some_method) } subject { object.abstract_method(:some_method) }


let(:object) { Class.new { include AbstractType } } let(:object) { Class.new { include AbstractType } }
Expand Down
Expand Up @@ -2,7 +2,7 @@


require 'spec_helper' require 'spec_helper'


describe AbstractType::ClassMethods, '#abstract_singleton_method' do describe AbstractType::AbstractMethodDeclarations, '#abstract_singleton_method' do
subject { object.abstract_singleton_method(:some_method) } subject { object.abstract_singleton_method(:some_method) }


let(:object) { Class.new { include AbstractType } } let(:object) { Class.new { include AbstractType } }
Expand Down
Expand Up @@ -9,9 +9,9 @@
let(:klass) { Class.new } let(:klass) { Class.new }


it 'extends the klass' do it 'extends the klass' do
klass.singleton_class.should_not include(described_class::ClassMethods) klass.singleton_class.should_not include(described_class::AbstractMethodDeclarations)
klass.send(:include, subject) klass.send(:include, subject)
klass.singleton_class.should include(described_class::ClassMethods) klass.singleton_class.should include(described_class::AbstractMethodDeclarations)
end end


it 'delegates to the ancestor' do it 'delegates to the ancestor' do
Expand Down
Expand Up @@ -2,7 +2,7 @@


require 'spec_helper' require 'spec_helper'


describe AbstractType::ClassMethods, '#new' do describe AbstractType, '.create_new_method' do
context 'with arguments' do context 'with arguments' do
subject { object.new(:foo) } subject { object.new(:foo) }


Expand Down Expand Up @@ -46,4 +46,22 @@ def initialize(foo)
specify { expect { subject }.to raise_error(NotImplementedError, "#{object} is an abstract type") } specify { expect { subject }.to raise_error(NotImplementedError, "#{object} is an abstract type") }
end end
end 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 } }

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 { expect { subject }.to raise_error(NotImplementedError, "#{object} is an abstract type") }
end
end
end end

0 comments on commit 6ad24c7

Please sign in to comment.