Skip to content

basiliscos/lua-gear

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
src
 
 
t
 
 
 
 
 
 
 
 
 
 
 
 

lua-gear

Inversion of Control implementation in lua

Build Status

If you know Java spring framework https://spring.io/ , this library may be for you. The Inversion of Control https://en.wikipedia.org/wiki/Inversion_of_control approach allows you to get rid of Singleton https://en.wikipedia.org/wiki/Singleton_pattern anti-pattern, and more easily manage complex depencencies.

The gear library optionally provide Dependecy Injection https://en.wikipedia.org/wiki/Dependency_injection via custom resolver; The gear cannot do DI as the as there is no reflection in lua, but custom OO-libraries might provide that.

Synopsis

Chicken / egg problem

local Gear = require "gear"
local gear = Gear.create()
gear:declare("chicken", {
  dependencies = {"egg"},                                            -- optional
  constructor  = function() return { class = "chicken" } end,
  initializer  = function(g, instance, egg) instance.egg = egg end,  -- optional
})
gear:declare("egg", {
  dependencies = {"chicken"},
  constructor  = function() return { class = "egg" } end,
  initializer  = function(g, instance, chicken) instance.chicken = chicken end,
})

-- solved!
local chicken = gear:get("chicken")
print(chiken.class)
print(chiken.egg.class)
local egg = gear:get("egg")
print(egg.class)
print(egg.chiken.class)

API

local Gear = require "gear"
local gear = Gear.create()          -- construct the container

-- the order of declaring components does NOT matter

gear:declare("car",                                                -- component name, required
  {                                                                -- component descriptor, required
    dependencies = {"wheels", "engine", "data/year"},              -- list of names of dependecies, optional
    -- dependencies = function() return { "wheels" } end           -- alternative for DI
    resolver     = function(component_name)                        -- "dynamic" dependencies, needed for DI
      return {"wheels", "engine", "data/year"}
    end,
    constructor = function()                                       -- constructor, required
      return { class = "car" },                                    -- must return something non-nill
    end,
    initializer = function(gear, instance, wheels, engine, year)   -- initializer, optional
      instance.wheels = wheels                                     -- if "provides" is defined, then initialized is 
      instance.engine = engine                                     -- mandatory, and it should return the 
      instance.year   = year                                       -- list of objects, which are defined in "provdes"
    end,                                                           -- section
    provides = { "some-other-components" },
  }
)

-- simple constructors
gear:declare("wheels",function() return { class = "wheels" } end)
gear:declare("engine",function() return { class = "engine" } end)

-- directly set pre-initialized (or externally initialized components)
gear:set("data/year", 2015)

-- Voila!
local engine = gear:get("engine") 

Provides

Let's assume that we have nuclear-reaction component, which is able to generate Thorium-234 and Helium-4 atoms (componets), when Uranium-238 is provided as input. Here is an example, how gear can handle that

local gear = Gear.create()
gear:declare("nuclear-reaction", {
  dependencies = {"Uranium-238"},
  provides     = {"Thorium-234", "Helium-4"},
  constructor  = function() return "process/nuclear-reaction" end,
  initializer  = function() return "element/Thorium-234", "element/Helium-4" end,
})
gear:declare("Uranium-238", {
  constructor  = function() return "element/Uranium-238" end,
  initializer  = function() end,
})

-- Oh, yes!

local thorium = gear:get("Thorium-234") -- element/Thorium-234
local helium = geag:get("Helium-4")     -- "element/Helium-4"

If provides section is defined, then initialize should return the provided objects after the root object initialization.

Dependency injection example

Dependency Injection is possible when underlying class-system provides field-level fields enumeration for used classes, and class can be dynamically looked up via it's name.

For example lua-Coat https://github.com/fperrad/lua-Coat provides that. Simplified example

require 'Coat'

local meta = require 'Coat.Meta.Class'

class 'Egg'
has.name = { is = 'ro' }
has.chicken = { is = 'ro', isa = 'Chicken' }

class 'Chicken'
has.egg = { is = 'ro', isa = 'Egg' }

local resolver = function(component_name)
  -- drop "my/" prefix
  local my_class_name = string.sub(component_name, 4)
  local my_class = _ENV[my_class_name]
  local dependencies = {}
  for name, attr in meta.attributes(my_class) do
    local property_class_name = attr.isa
    local property_class = _ENV[property_class_name]
    if (property_class) then
      -- add "my/" prefix
      table.insert(dependencies, "my/" .. property_class_name)
    end
  end
  return dependencies
end

local Gear = require "gear"
local gear = Gear.create()

gear:declare("my/Chicken", {
  resolver    = resolver,
  constructor = function() return Chicken { } end,
  initializer = function(gear, instance, egg) instance.egg = egg end,
})
gear:declare("my/Egg", {
  resolver    = resolver,
  constructor = function() return Egg { name = "smallie" } end,
  initializer = function(gear, instance, chicken) instance.chicken = chicken end,
})

local chicken = gear:get("my/Chicken")
print(chicken.egg.name)

Installation

luarocks install gear

License

Artistic License 2.0

Author

Ivan Baidakou (basiliscos), https://github.com/basiliscos

About

Inversion of Control implementation in lua

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages