nmichel / bloom

A simple OOP layer for Lua language

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

BLOOM Endorse nmichel on Coderwall

BLOOM (as in Basic Lua Object Oriented Model) is a simple OOP layer for Lua language.

BLOOM logo

Motivation

bloom is used to write routing algorithms for a softswitch VoIP Class 5. Although we can do without bloom, it greatly helps scripters can manipulate classes and objects that represent business concepts in a classic OOP manner, as

object = MyClass:instanciate(initParams)
results = object:method(parameters)

By providing a decent inheritance mechanism, bloom also speeds up routing specialization by reducing it to inheritances and method overloading.

bloom is a lot of FUN, in writing it, in playing with it ... in applying it to real world telecommunication software. I hope you will enjoy it.

Running examples

The sample scripts must be executed from the directory examples/.

$ lua [-i] <example.lua> [args]

Use -i option to enter interactive mode after script has been processed.

Usage

bloom is a pure Lua module. As such, all you need is to ensure bloom.lua is in your package.path,

package.path = package.path .. ";/where/bloom/resides/?.lua"

then add require(bloom) in your own script files.

require("bloom")

bloom.loadClass("birds.Parrot")

Class lookup

The default behaviour of bloom is to look for classes in the current working directory. If we consider the preceding code snippet, saying we are in directory /home/nmichel/private/bloom/ (not in examples), bloom wont't be able to load class "birds.Parrot".

In such cases (as to use shared class libraries, ...) you have to add lookup paths.

package.path = package.path .. ";/where/bloom/resides/?.lua"
require("bloom")

bloom.addLookupPath("/usr/nmichel/private/bloom/examples")

bloom.loadClass("birds.Parrot")

Class definition

One can define a class either by calling method makeClass on a instance of a metaclass, or by using class loading.

Explicit declaration

local HelloWorld = bloom.MetaClass:makeClass("HelloWorld", {bloom.Object},
{
    __init__ =
        function(self, who)
            self.who = who
        end,

     salute =
        function(self)
            return tostring(self.who) .. " says \"Hello world!\""
        end
})
        
local helloWorld = HelloWorld:instanciate("Donald")
local salute = helloWorld:salute()
print(salute)

Class Loading

Create a file HelloWorld.lua returning the definition of the HelloWorld class. Class name and file name must match.

return {
    "HelloWorld",
    {bloom.Object},
    {
        __init__ =
            function(self, who)
                self.who = who
            end,

        salute =
            function(self)
                return tostring(self.who) .. " says \"Hello world!\""
            end
    }
}

Then use the class loading mecanism to load the HelloWorld class.

require(bloom)
bloom.loadClass("HelloWorld")

local helloWorld = HelloWorld:instanciate("Donald")
local salute = helloWorld:salute()
print(salute)

Inheritance

A class can inherit another (or several). The former is called "derived class", while the latter is said to be the "base class". A derived class can redefine (or override) some methods of base classes. When a method is called on an object, the version of the method called depends on real type of the object.

A overridden method can call a base class's version, with self:super()(). By providing a class when calling super, one can indicate which base class version of the method must be called, e.g. self:super(inherit.advanced.OtherDerived)().

Simple example

Lets have a base class called ... Base, defined in inherit/simple/Base.lua

return {
    "Base",
    {bloom.Object},
    {
        __init__ =
            function(self, name)
                self.name = name or "John Doe"
            end,

        myType = 
            function (self)
                return "Base"
            end,
            
        says =
            function(self, what, out)
                return (out or print)(self.name .. " of class " .. self:myType() .. " says " .. tostring(what or "nothing"))
            end
    }
}

The method says() uses method myType() (amongst other details) to build a string passed to a function (if provided) or printed out. Note that myType() doesn't use base class Object to retrieve class name (as in self:getClass():getName()). This is because code is evaluated at runtime, so self:getClass() will return the real class, not Base as one might expect at first sight.

We define a class called Derived inheriting Base, in file inherit/simple/Derived.lua

return {
    "Derived",
    {inherit.simple.Base},
    {
        __init__ =
            function(self, ...)
            end,

        myType = 
            function (self)
                return "Derived (derived from " .. self:super()().. ")" -- Note call to base class version of myType() using self:super()()
            end
    }
}

Class Derived overrides method myType(), and uses self:super()() to call Base's version.

Now we can write a script which uses both classes, and calls says() on a instance of each of both.

bloom.loadClass("inherit.simple.Base")
bloom.loadClass("inherit.simple.Derived")

local b = inherit.simple.Base:instanciate("b")
local d = inherit.simple.Derived:instanciate("d")

local function says(who, what)
    who:says(what)
end

says(b, "Hello world!")
says(d, "Hello world!")

Advanced example

Lets modify class Derived, to simply overrides myType() and add method foo().

        // ...
        myType = 
            function (self)
                return "Derived"
            end,

        foo = 
            function (self)
                return "foo"
            end

We define class OtherDerived also inheriting Base. OtherDerived is nearly identical to Derived.

        // ...
        myType = 
            function (self)
                return "OtherDerived"
            end,

        bar = 
            function (self)
                return "bar"
            end

Note that both Derived and OtherDerived redefine myType().

Now we define class MultiDerived inheriting Derived and OtherDerived.

return {
    "MultiDerived",
    {inherit.advanced.Derived, inherit.advanced.OtherDerived}, -- Multiple inheritance
    {
        __init__ =
            function(self, ...)
            end,

        myType = 
            function (self)
                local res = ""
                for _, v in pairs(self:getClass():getSuperClasses()) do
                    res = res .. " " .. self:super(v)() -- Calling myType() of each base class
                end
                return "MultiDerived (" .. res .. " )"
            end
    }
}

Simple isn't it ? The interesting point is how myType() is redefined in MultiDerived. It illustrates the way you can select a base class version of an overridden method, with self:super()(...).

Here follows a client script, which uses all these classes.

bloom.loadClass("inherit.advanced.Base")
bloom.loadClass("inherit.advanced.Derived")
bloom.loadClass("inherit.advanced.OtherDerived")
bloom.loadClass("inherit.advanced.MultiDerived")

local d = inherit.advanced.Derived:instanciate("d")
local od = inherit.advanced.OtherDerived:instanciate("od")
local md = inherit.advanced.MultiDerived:instanciate("md")

print("d:foo()", pcall(d.foo, d))
print("od:foo()", pcall(od.foo, od)) -- Fail

print("d:bar()", pcall(d.bar, d)) -- Fail
print("od:bar()", pcall(od.bar, od))

print("md:bar()", pcall(md.foo, md))
print("md:foo()", pcall(md.bar, md))

local function says(who, what)
    who:says(what)
end

says(d, "Hello world!")
says(od, "Hello world!")
says(md, "Hello world!")

Metaclass

Basically, a metaclass is what you use to build classes. But the metaclass which has build a class also defines the behaviour of this class in every occasion, e.g. how the class finds a method in its set of base classes or (soon) how a method is invoked on a instance.

You can define a new metaclass class by inheriting from existing one(s). Then you just have to instanciate it to be able to create new classes using your brand new metaclass.

Currently bloom.MetaClass is somewhat special as it is not a instanciation of a MetaClass class, and you can not instanciate it. It is a singularity in the model that may change in the future.

To Be Continued ...

  • Inspection
  • More examples

About

A simple OOP layer for Lua language


Languages

Language:Lua 100.0%