module Abstract
def abstract_methods(*args)
args.each do |name|
class_eval(<<-END, __FILE__, __LINE__)
def #{name}(*args)
raise NotImplementedError.new("You must implement #{name}.")
end
END
# important that this END is capitalized, since it marks the end of <<-END
end
end
end
require 'rubygems'
require 'rspec'
describe "abstract methods" do
before(:each) do
@klass = Class.new do
extend Abstract
abstract_methods :foo, :bar
end
end
it "raises NoMethodError" do
proc {
@klass.new.foo
}.should raise_error(NoMethodError)
end
it "can be overridden" do
subclass = Class.new(@klass) do
def foo
:overridden
end
end
subclass.new.foo.should == :overridden
end
end
class A
class AbstractClassInstiationError < RuntimeError; end
def initialize
raise AbstractClassInstiationError, "Cannot instantiate this class directly, etc..."
end
end
module Abstract
def self.append_features(klass)
# access an object's copy of its class's methods & such
metaclass = lambda { |obj| class << obj; self ; end }
metaclass[klass].instance_eval do
old_new = instance_method(:new)
undef_method :new
define_method(:inherited) do |subklass|
metaclass[subklass].instance_eval do
define_method(:new, old_new)
end
end
end
end
end
puts 'test inheritance'
module Abstract
def new
throw 'abstract!'
end
def inherited(child)
@abstract = true
puts 'inherited'
non_abstract_parent = self.superclass;
while non_abstract_parent.instance_eval {@abstract}
non_abstract_parent = non_abstract_parent.superclass
end
puts "Non abstract superclass is #{non_abstract_parent}"
(class << child;self;end).instance_eval do
define_method :new, non_abstract_parent.method('new')
# # Or this can be done in this style:
# define_method :new do |*args,&block|
# non_abstract_parent.method('new').unbind.bind(self).call(*args,&block)
# end
end
end
end
class AbstractParent
extend Abstract
def initialize
puts 'parent initializer'
end
end
class Child < AbstractParent
def initialize
puts 'child initializer'
super
end
end
# AbstractParent.new
puts Child.new
class AbstractChild < AbstractParent
extend Abstract
end
class Child2 < AbstractChild
end
puts Child2.new
module Abstract
extend ActiveSupport::Concern
included do
# Interface for declaratively indicating that one or more methods are to be
# treated as abstract methods, only to be implemented in child classes.
#
# Arguments:
# - methods (Symbol or Array) list of method names to be treated as
# abstract base methods
#
def self.abstract_methods(*methods)
methods.each do |method_name|
define_method method_name do
raise NotImplementedError, 'This is an abstract base method. Implement in your subclass.'
end
end
end
end
end
# Usage:
class AbstractBaseWidget
include Abstract
abstract_methods :widgetify
end
class SpecialWidget < AbstractBaseWidget
end
SpecialWidget.new.widgetify # <= raises NotImplementedError
class Foo
include AbstractType
# Declare abstract instance method
abstract_method :bar
# Declare abstract singleton method
abstract_singleton_method :baz
end
Foo.new # raises NotImplementedError: Foo is an abstract type
Foo.baz # raises NotImplementedError: Foo.baz is not implemented
# Subclassing to allow instantiation
class Baz < Foo; end
object = Baz.new
object.bar # raises NotImplementedError: Baz#bar is not implemented