Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
shira-374 committed Jan 18, 2015
0 parents commit ccd885d
Show file tree
Hide file tree
Showing 4 changed files with 357 additions and 0 deletions.
22 changes: 22 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Copyright (c) 2015 Pavel Batečko (ShiraNai7)

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
199 changes: 199 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
Lua object model
================

Simple object model implementation in Lua (~100 lines of code, including comments).

Inspired by [http://lua-users.org/wiki/SimpleLuaClasses](http://lua-users.org/wiki/SimpleLuaClasses)


## Features

- classes
- constructors
- destructors
- inheritance
- instanceof
- parent method calls


## Requirements

- Lua 5.2 or newer


## Installation

Using LuaRocks:

luarocks install object-model


## Module method overview

- `class([parentClass]): table`
- create a new class, optionally inheriting methods and properties of another one
- `instanceof(object, class): boolean`
- check if the given object is an instance of the given class or has that class as
one of its parents
- `new(class, ...): object`
- create an instance of the given class and invoke its constructor
- any additional arguments will be passed to the constructor
- should not be used directly, invoke the class table instead (refer to the *Instantiating a class* section)
- `super(object, methodName, ...): mixed`
- invokes a parent method
- this is the default implementation of `object:super(method, ...)`


## Feature documentation

Let's assume you have imported the `ObjectModel` module like this:

local o = require 'ObjectModel'


### Creating a new class

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

MyClass = o.class()


### Inheritance

If another class is passed to the `class()` function, all of its methods
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.


### Prototype properties

Prototype properties are available in all instances of the class.

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


### 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:

function MyClass:doSomething()
-- do something, self points to the instance
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


### Constructors

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

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


### Destructors

Destructor is a special method that is invoked when an instance is being garbage-collected
by Lua.

function MyClass:destructor()
-- self points to the instance
-- no arguments are passed
end

Destructors are implemented using the `__gc` metamethod.


### Instantiating a class

To create an instance of a class, simply call the class table as a function.

local obj = MyClass('something')

All arguments passed to this function are forwarded to the constructor.


### 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.

local obj = MyClass()

if o.instanceof(obj, MyClass) then
print('obj is instance of MyClass')
end


### Calling parent methods

If a class has inherited from another and overridden some of its methods, these methods can be
invoked using the `:super(methodName, ...)` method.

BaseClass = o.class()

function BaseClass:constructor(foo)
self.foo = foo
end

DerivedClass = o.class(BaseClass)

function DerivedClass:constructor(foo, bar)
self:super('constructor', foo) -- invoke the parent constructor
self.bar = bar
end

local obj = DerivedClass('hello', 'world')

print(obj.foo, obj.bar) -- prints: hello world


### Metamethods

A class is actually a metatable of its instances. This allows metamethods to be defined the same
way as any other methods.

function MyClass:__tostring()
return 'stringified!'
end

local obj = MyClass()

print(obj) -- prints: stringified!
26 changes: 26 additions & 0 deletions rockspec/object-model-0.1.0-1.rockspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package = 'object-model'
version = '0.1.0-1'
source = {
url = 'git://github.com/ShiraNai7/lua-object-model.git',
tag = 'v0.1.0',
}
description = {
summary = "Simple object model implementation in Lua",
detailed = [[
Simple object model implementation in Lua (~100 lines of code, including comments).
Inspired by http://lua-users.org/wiki/SimpleLuaClasses
]],
homepage = 'https://github.com/ShiraNai7/lua-object-model',
maintainer = 'ShiraNai7',
license = 'MIT',
}
dependencies = {
'lua >= 5.2',
}
build = {
type = 'builtin',
modules = {
['ObjectModel'] = 'src/ObjectModel.lua',
},
}
110 changes: 110 additions & 0 deletions src/ObjectModel.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
--
-- Lua object model implementation
--

--- Create an instance of the given class
--
-- @param class the class being constructed
-- @param ... constructor arguments
-- @return table the object
local function new(class, ...)
local object = {}

setmetatable(object, 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
end

--- Invoke a parent method
--
-- @param object the current object
-- @param methodName the parent method name
-- @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, ...)
end

--- See if an object is an instance of the given class
--
-- @param object the object to verify
-- @param classToCompare the class to compare against
-- @return boolean
local function instanceof(object, classToCompare)
local class = getmetatable(object)

while class do
if class == classToCompare then
return true
end

class = class._parent
end

return false
end

--- Object destructor handler
--
-- This is the __gc implementation and should not be called manually.
--
-- @param object instance that is being destructed
local function objectGarbageCollect(object)
if object.destructor then
object:destructor()
end
end

--- Class table factory
--
-- @param parent class definition to inherit from
-- @return class table
local function class(parent)
local class = {}

-- process the parent
if parent then
-- create a shallow copy of the parent class
for i, v in pairs(parent) do
class[i] = v
end

class._parent = parent
end

-- the class will be the metatable for all its instances
-- and they will look up their methods in it
class.__index = class

if not parent then
class.__gc = objectGarbageCollect
end

if parent then
class.super = super
end

-- create a meta table for the class
-- too hook the <class>(<args>) mechanism
local meta = {
__call = new
}
setmetatable(class, meta)

return class
end

-- return the module table
return {
class = class,
instanceof = instanceof,
new = new,
super = super,
}

0 comments on commit ccd885d

Please sign in to comment.