Skip to content

Commit

Permalink
Support nested :super() calls
Browse files Browse the repository at this point in the history
Prefix internal properties by two underscores
Updated README
  • Loading branch information
shira-374 committed Aug 31, 2015
1 parent 7956f92 commit 09a8da2
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 45 deletions.
73 changes: 33 additions & 40 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Let's assume you have imported the `ObjectModel` module like this:

### Creating a new class

The class function returns a new table that you can populate with your methods
The `class` function returns a new table that you can populate with your methods
and properties.

MyClass = o.class()
Expand All @@ -67,65 +67,42 @@ and properties are inherited.
MyClass = o.class(OtherClass)

Internally, a shallow copy of the parent class is created for performance reasons.
Therefore modifications of the parent class at a later point will NOT be propagated
to the child classes.
Modifications of the parent class at a later point will NOT be propagated to the child
classes.


### Prototype properties

Prototype properties are available in all instances of the class.
Prototype properties can be defined in the class table. They are available in all
instances of the class and can be accessed statically. Instance properties will shadow
prototype properties with the same name.

MyClass.foo = 'bar'

**Warning!** Do not put tables into prototype properties, unless you want the table
to be shared across all the instances. If you need to initialize a table property
per-instance, do so in the constructor.


### Static properties

There is no separate namespace for static properties. All prototype properties are
defined in the class table, therefore can be accessed without instantiating the class.

MyClass.staticFoo = 'bar'

print(MyClass.staticFoo) -- prints: bar
**Warning!** Do not put tables into prototype properties unless you want the table
to be shared across all the instances (see instance properties).


### Methods

Methods are defined the same way as prototype properties.

MyClass.doSomething = function(self)
-- do something, self points to the instance
end

However, one can take advantage of Lua's syntactic sugar to make method definitions prettier:
Lua provides syntactic sugar for this purpose.

function MyClass:doSomething()
-- do something, self points to the instance
print(self.someProperty) -- getting properties
print(self:someMethod()) -- calling other methods
end


### Static methods

All methods are defined in the class table (same as prototype properties), therefore can be
accessed and called without instantiating the class.

function MyClass:doSomethingStatic()
-- self points to MyClass
end

MyClass:doSomethingStatic() -- calling a static method
Same as prototype properties, methods defined this way are available in all instances
of the class and can be called statically.


### Constructors

Constructor is a special method that is invoked when a class is being instantiated.

function MyClass:constructor(arg)
function MyClass:constructor(lorem, ipsum)
-- self points to the instance
-- arg is the first argument passed to the constructor, and so on
end


Expand All @@ -149,9 +126,26 @@ To create an instance of a class, simply call the class table as a function.
All arguments passed to this function are forwarded to the constructor.


### Instance properties

Properties defined on an object are privately owned by that object. They will shadow prototype
properties with the same name.

MyClass.test = 'hello from prototype'

local obj = MyClass()

print(obj.test) -- prints: hello from prototype
obj.test = 'hello from instance'
print(obj.test) -- prints: hello from instance
obj.test = nil
print(obj.test) -- prints: hello from prototype


### Instanceof

To check whether an object is instance of a specific class or has that class as one of its parents, use the `instanceof()` function.
To check whether an object is instance of a specific class or has that class as one of its parents,
use the `instanceof()` function.

local obj = MyClass()

Expand Down Expand Up @@ -185,8 +179,7 @@ invoked using the `:super(methodName, ...)` method.

### Metamethods

A class is actually a metatable of its instances. This allows metamethods to be defined the same
way as any other methods.
Metamethods can be defined the same way as any other methods and will work as expected.

function MyClass:__tostring()
return 'stringified!'
Expand Down
26 changes: 26 additions & 0 deletions rockspec/object-model-0.1.2-1.rockspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package = 'object-model'
version = '0.1.2-1'
source = {
url = 'git://github.com/ShiraNai7/lua-object-model.git',
tag = 'v0.1.2',
}
description = {
summary = "Simple object model implementation in Lua",
detailed = [[
Simple object model implementation in Lua.
Inspired by http://lua-users.org/wiki/SimpleLuaClasses
]],
homepage = 'https://github.com/ShiraNai7/lua-object-model',
maintainer = 'ShiraNai7',
license = 'MIT',
}
dependencies = {
'lua >= 5.1',
}
build = {
type = 'builtin',
modules = {
['ObjectModel'] = 'src/ObjectModel.lua',
},
}
38 changes: 33 additions & 5 deletions src/ObjectModel.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
--

local isLua51 = 'Lua 5.1' == _VERSION
local unpack = unpack or table.unpack

--- Create an instance of the given class
--
Expand Down Expand Up @@ -34,8 +35,6 @@ local function new(class, ...)
-- invoke constructor of the class
if class.constructor then
class.constructor(object, ...)
elseif class._parent and class._parent.constructor then
class._parent.constructor(object, ...)
end

return object
Expand All @@ -48,7 +47,36 @@ end
-- @param ... arguments for the parent method
-- @return the return value(s) of the parent method
local function super(object, methodName, ...)
return object._parent[methodName](object, ...)
-- init super call scope table on first use
if nil == object.___superScope then
object.___superScope = {}
end

-- switch to the next parent class
local currentParent = object.___superScope[methodName]
local nextParent

if nil ~= currentParent then
nextParent = currentParent.__parent;
else
nextParent = object.__parent;
end

object.___superScope[methodName] = nextParent

-- call the parent method
local results = {pcall(nextParent[methodName], object, ...)}
local success = table.remove(results, 1)

-- restore previous parent class
object.___superScope[methodName] = currentParent

-- handle call results
if not success then
error(results[1])
end

return unpack(results)
end

--- See if an object is an instance of the given class
Expand All @@ -64,7 +92,7 @@ local function instanceof(object, classToCompare)
return true
end

class = class._parent
class = class.__parent
end

return false
Expand Down Expand Up @@ -95,7 +123,7 @@ local function class(parent)
class[i] = v
end

class._parent = parent
class.__parent = parent
end

-- the class will be the metatable for all its instances
Expand Down

0 comments on commit 09a8da2

Please sign in to comment.