How to implement an abstract class in Ruby
I don't like using abstract classes in Ruby (there's almost always a better way). If you really think it's the best technique for the situation though, you can use the following snippet to be more declarative about which methods are abstract:
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
Basically, you just call abstract_methods
with the list of methods that are abstract, and when they get called by an instance of the abstract class, a NotImplementedError
exception will be raised.
Integer and other (?) abstract classes in Ruby
You can undefine the new
class method calling undef_method
on the singleton class of your class, for example:
class Foo
class << self
undef_method(:new)
end
end
Foo.new
# NoMethodError: undefined method `new' for Foo:Class
Or alternatively:
class Foo; end
Foo.singleton_class.send(:undef_method, :new)
Update: for the sake of completeness here's a quick (and possibly dirty) solution to the problem mentioned by @tadman in the comments. It uses the inherited
method to define a new
singleton method for the subclasses of Foo
that mimics the implementation of Class#new
:
class Foo
class << self
undef_method(:new)
def inherited(subclass)
subclass.define_singleton_method(:new) do |*args|
obj = subclass.allocate
obj.send(:initialize, *args)
obj
end
end
end
end
class Bar < Foo
attr_reader :bar
def initialize(bar)
@bar = bar
end
end
Bar.new('foobar').bar # => "foobar"
Setting up for an Abstract Super Class in Ruby
In Ruby you don't have the keyword abstract
to defined abstract classes. If you need to encapsulate functionality that is intended to be shared among different classes, you can extract that into a module, and mix
it in with the classes.
For instance:
module Player
# shared functionality
end
class Human
include Player
end
class Computer
include Player
end
This works well since abstract, as in disassociated from any specific instance, is just construct that can be achieve in many different ways. Different language choose to be more explicit about it, in ruby is more implicit.
Alternatives to abstract classes in Ruby?
Ruby does not offer this functionality, no. You are responsible for making sure that your classes implement what they ought to implement.
Part of the reason that such functionality is impossible for Ruby is that Ruby classes can be reopened, and Ruby supports loading arbitrary code at runtime, so we can't know whether a class implements a certain interface until we try to call it.
Supposing an Animal
must have an eat
method, and I do the following:
class Cat < Animal
def talk
puts "meow"
end
end
class Cat
def eat
puts "om nom nom"
end
end
By the end of that file, the Cat
will have its eat
definition, because Ruby classes can reopened and modified multiple times. Should the code error out after the first definition because eat
wasn't defined yet? That implementation would hurt more than it would help, since reopening classes is common, even if this example is contrived. Should it error out once the eat
method is called and does not exist, so we can be certain that it's defined once we need it? Well, if the method were missing, that would happen, anyway. The interpreter can never know if another class definition is on the way, so it can never cut you off until the method is actually called.
In short, superclasses simply cannot possibly require a method to be defined in Ruby, because the dynamic nature of classes contradict such a goal.
Sorry! This is a place where unit testing might come in handy, though, to ensure that your subclasses do what they're supposed to be doing, anyway.
Simulating abstract classes in Ruby (Rails)
Why would you want to do this? The point of abstract/interfaced classes are to hack Strongly typed languages into a dynamic paradigm. If you need your class to fit in the signature, name your methods according to the original class or make a facade and plug it in, no need to trick a compiler into allowing it, it just works.
def my_printer obj
p obj.name
end
So I defined the interface as any object with a name property
class person
attr_accessor :name
def initialize
@name = "Person"
end
end
class Employee
attr_accessor :name
def initialize
@name = "Employee"
@wage = 23
end
end
so nothing stops us from calling our printer method with either of these
my_printer Person.new
my_printer Employee.new
both print there names without a hitch :D
Rails - Best practice for abstract class definition and file naming
First of all, I think you must already realize that ruby does not have true abstract classes. But we can approximate the behavior. And while doing so, it sounds like you have a preference toward organizational structure which I will attempt to address.
I must start by saying, however, that I'm surprised that you're coming at the problem so strongly from the organizational angle. First on my mind would be whether I really wanted to implement single table inheritance or not and then let that drive the organizational problem. Usually the answer here is that Single Table Inheritance is not what you actually want. But... let's dive in!
Using Single Table Inheritance
Here's the standard way to utilize and organize models using Single Table Inheritance:
# app/models/mother_class.rb
class MotherClass < ActiveRecord::Base
# An "abstract" method
def method1
raise NotImplementedError, "Subclasses must define `method1`."
end
def method2
puts method1 # raises NotImplementedError if `method1` is not redefined by a subclass
end
end
# app/models/sub_class_a.rb
class SubClassA < MotherClass
def method1
# do something
end
end
# app/models/sub_class_b.rb
class SubClassB < MotherClass
def method1
# do something
end
end
Given the above, we would get an exception when calling MotherClass.new.method2
but not when calling SubClassA.new.method2
or SubClassB.new.method2
. So we've satisfied the "abstract" requirements. Organizationally, you called this a big mess in the models folder... which I can understand if you've got tons of these subclasses or something. But, remember that in single table inheritance even then parent class is a model and is / should be usable as such! So, that said, if you'd really like to organize your models file system better then you are free to do so. For example, you could do:
app/models/<some_organizational_name>/mother_class.rb
app/models/<some_organizational_name>/sub_class_a.rb
app/models/<some_organizational_name>/sub_class_b.rb
In this, we are keeping all other things (i.e. the Code for each of these models) the same. We're not namespacing these models in any way, we're just organizing them. To make this work it's just a matter of helping Rails to find the models now that we've placed them in a subfolder of the models folder without any other clues (i.e. without namespacing them). Please refer to this other Stack Overflow post for this. But, in short, you simply need to add the following to your config/application.rb file:
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**/}')]
Using Mixins
If you decide that Single Table Inheritance is not what you want (and they often aren't, really) then mixins can give you the same quasi-abstract functionality. And you can, again, be flexible on file organization. The common, organizational pattern for mixins is this:
# app/models/concerns/mother_module.rb
module MotherModule
extend ActiveSupport::Concern
# An "abstract" method
def method1
raise NotImplementedError, "Subclasses must define `method1`."
end
def method2
puts method1 # raises NotImplementedError if `method1` is not redefined
end
end
# app/models/sub_class_a.rb
class SubClassA
include MotherModule
def method1
# do something
end
end
# app/models/sub_class_b.rb
class SubClassB
include MotherModule
def method1
# do something
end
end
With this approach, we continue to not get an exception when calling SubClassA.new.method2
or SubClassB.new.method2
because we've overridden these methods in the "subclasses". And since we can't really call MotherModule#method1
directly it is certainly an abstract method.
In the above organization, we've tucked MotherModule
away into the models/concerns folder. This is the common location for mixins in Rails these days. You didn't mention what rails version you're on, so if you don't already have a models/concerns
folder you'll want to make one and then make rails autoload models from there. This would, again, be done in config/application.rb with the following line:
config.autoload_paths += Dir[Rails.root.join('app', 'concerns', '{**/}')]
The organization with the mixins approach is, in my opinion, simple and clear in that SubclassA
and SubClassB
are (obviously) models and, since they include the MotherModule
concern they get the behaviors of MotherModule
. If you wanted to group the subclass models, organizationally, into a folder then you could still do this of course. Just use the same approach outlined at the end of the Single Table Inheritance section, above. But I'd probably keep MotherModule
located in the models/concerns folder still.
Ruby base class call child class like in abstract classes
Welcome to dynamically typed languages! You were probably nervous about just defining some function that wasn't declared anywhere. Don't be worried. It is very easy:
class Base
def process
# ...
real_processing
end
def real_processing # This method is optional!
raise "real_processing not implemented in #{self.class.name}"
end
end
class Child < Base
def real_processing
# ...
end
end
b = Child.new
b.process
EDIT: Here's another option for you which avoids the need to have two different method names:
class Base
def process
# ...
end
end
class Child < Base
def process
# ...
super # calls the process method defined above in Base
# ...
end
end
Abstract Method in Ruby
Abstract methods are supposed to be less useful in Ruby because it's not strongly statically typed.
However, this is what I do:
class AbstractThing
MESS = "SYSTEM ERROR: method missing"
def method_one; raise MESS; end
def method_two; raise MESS; end
end
class ConcreteThing < AbstractThing
def method_one
puts "hi"
end
end
a = ConcreteThing.new
a.method_two # -> raises error.
It rarely seems to be necessary, however.
Related Topics
What's the Difference Between Ruby'S Dup and Clone Methods
Regex for Checking the Last Character
What Is Ruby'S Double-Colon '::'
Code Block Passed to Each Works With Brackets But Not With 'Do'-'End' (Ruby)
Ruby 2.0.0P0 Irb Warning: "Dl Is Deprecated, Please Use Fiddle"
How to Dynamically Create a Local Variable
Difference Between \A \Z and ^ $ in Ruby Regular Expressions
Ssl_Connect Returned=1 Errno=0 State=Sslv3 Read Server Certificate B: Certificate Verify Failed
Difference Between ≪%, ≪%=, ≪%# and -%≫ in Erb in Rails
How to Write a Switch Statement in Ruby
Pg::Connectionbad - Could Not Connect to Server: Connection Refused
Why Do Ruby Setters Need "Self." Qualification Within the Class
How to Redirect to a 404 in Rails
Difference Between Class Variables and Class Instance Variables