MiddleClass.lua 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. -----------------------------------------------------------------------------------
  2. -- MiddleClass.lua
  3. -- Enrique García ( enrique.garcia.cota [AT] gmail [DOT] com ) - 19 Oct 2009
  4. -- Based on YaciCode, from Julien Patte and LuaObject, from Sébastien Rocca-Serra
  5. -----------------------------------------------------------------------------------
  6. local classes = setmetatable({}, {__mode = "k"}) -- weak table storing references to all declared classes
  7. -- The 'Object' class
  8. Object = { name = "Object" }
  9. -- creates a new instance
  10. Object.new = function(class, ...)
  11. assert(classes[class]~=nil, "Use class:new instead of class.new")
  12. local instance = setmetatable({ class = class }, class.__classDict) -- the class dictionary is the instance's metatable
  13. instance:initialize(...)
  14. return instance
  15. end
  16. -- creates a subclass
  17. Object.subclass = function(superclass, name)
  18. assert(classes[superclass]~=nil, "Use class:subclass instead of class.subclass")
  19. if type(name)~="string" then name = "Unnamed" end
  20. local theClass = { name = name, superclass = superclass, __classDict = {} }
  21. local classDict = theClass.__classDict
  22. -- This one is weird. Since:
  23. -- a) the class dict is the instances' metatable (so it must have an __index for looking up the methods) and
  24. -- b) The instance methods are in the class dict itself, then ...
  25. classDict.__index = classDict
  26. -- if a method isn't found on the class dict, look on its super class
  27. setmetatable(classDict, {__index = superclass.__classDict} )
  28. -- theClass also needs some metamethods
  29. setmetatable(theClass, {
  30. __index = function(_,methodName)
  31. local localMethod = classDict[methodName] -- this allows using classDic as a class method AND instance method dict
  32. if localMethod ~= nil then return localMethod end
  33. return superclass[methodName]
  34. end,
  35. -- FIXME add support for __index method here
  36. __newindex = function(_, methodName, method) -- when adding new methods, include a "super" function
  37. if type(method) == 'function' then
  38. local fenv = getfenv(method)
  39. local newenv = setmetatable( {super = superclass.__classDict}, {__index = fenv, __newindex = fenv} )
  40. setfenv( method, newenv )
  41. end
  42. rawset(classDict, methodName, method)
  43. end,
  44. __tostring = function() return ("class ".. name) end,
  45. __call = function(_, ...) return theClass:new(...) end
  46. })
  47. -- instance methods go after the setmetatable, so we can use "super"
  48. theClass.initialize = function(instance,...) super.initialize(instance) end
  49. classes[theClass]=theClass --registers the new class on the list of classes
  50. return theClass
  51. end
  52. -- Mixin extension function - simulates very basically ruby's include(module)
  53. -- module is a lua table of functions. The functions will be copied to the class
  54. -- if present in the module, the included() method will be called
  55. Object.includes = function(class, module, ... )
  56. assert(classes[class]~=nil, "Use class:includes instead of class.includes")
  57. for methodName,method in pairs(module) do
  58. if methodName ~="included" then class[methodName] = method end
  59. end
  60. if type(module.included)=="function" then module:included(class, ... ) end
  61. end
  62. classes[Object]=Object -- adds Object to the list of classes
  63. Object.__classDict = {
  64. initialize = function(instance, ...) end, -- end of the initialize() call chain
  65. __tostring = function(instance) return ("instance of ".. instance.class.name) end
  66. }
  67. setmetatable(Object, { __index = Object.__classDict, __newindex = Object.__classDict,
  68. __tostring = function() return ("class Object") end,
  69. __call = Object.new
  70. })
  71. function Object.getterFor(class, attr) return 'get' .. attr:gsub("^%l", string.upper) end
  72. function Object.setterFor(class, attr) return 'set' .. attr:gsub("^%l", string.upper) end
  73. function Object.getter(class, attributeName, defaultValue)
  74. class[class:getterFor(attributeName)] = function(self)
  75. if(self[attributeName]~=nil) then return self[attributeName] end
  76. return defaultValue
  77. end
  78. end
  79. function Object.setter(class, attributeName)
  80. class[class:setterFor(attributeName)] = function(self, value) self[attributeName] = value end
  81. end
  82. function Object.getterSetter(class, attributeName, defaultValue)
  83. class:getter(attributeName, defaultValue)
  84. class:setter(attributeName)
  85. end
  86. -- Returns true if class is a subclass of other, false otherwise
  87. function subclassOf(other, class)
  88. if class.superclass==nil then return false end --class is Object, or a non-class
  89. return class.superclass == other or subclassOf(other, class.superclass)
  90. end
  91. -- Returns true if obj is an instance of class (or one of its subclasses) false otherwise
  92. function instanceOf(class, obj)
  93. if obj==nil or classes[class]==nil or classes[obj.class]==nil then return false end
  94. if obj.class==class then return true end
  95. return subclassOf(class, obj.class)
  96. end
  97. function class(name, baseClass)
  98. baseClass = baseClass or Object
  99. return baseClass:subclass(name)
  100. end