Support IP Hash LB policy
[msb/apigateway.git] / openresty-ext / src / assembly / resources / openresty / nginx / luaext / vendor / pl / class.lua
1 --- Provides a reuseable and convenient framework for creating classes in Lua.
2 -- Two possible notations:
3 --
4 --    B = class(A)
5 --    class.B(A)
6 --
7 -- The latter form creates a named class within the current environment. Note
8 -- that this implicitly brings in `pl.utils` as a dependency.
9 --
10 -- See the Guide for further @{01-introduction.md.Simplifying_Object_Oriented_Programming_in_Lua|discussion}
11 -- @module pl.class
12
13 local error, getmetatable, io, pairs, rawget, rawset, setmetatable, tostring, type =
14     _G.error, _G.getmetatable, _G.io, _G.pairs, _G.rawget, _G.rawset, _G.setmetatable, _G.tostring, _G.type
15 local compat
16
17 -- this trickery is necessary to prevent the inheritance of 'super' and
18 -- the resulting recursive call problems.
19 local function call_ctor (c,obj,...)
20     -- nice alias for the base class ctor
21     local base = rawget(c,'_base')
22     if base then
23         local parent_ctor = rawget(base,'_init')
24         while not parent_ctor do
25             base = rawget(base,'_base')
26             if not base then break end
27             parent_ctor = rawget(base,'_init')
28         end
29         if parent_ctor then
30             rawset(obj,'super',function(obj,...)
31                 call_ctor(base,obj,...)
32             end)
33         end
34     end
35     local res = c._init(obj,...)
36     rawset(obj,'super',nil)
37     return res
38 end
39
40 --- initializes an __instance__ upon creation.
41 -- @function class:_init
42 -- @param ... parameters passed to the constructor
43 -- @usage local Cat = class()
44 -- function Cat:_init(name)
45 --   --self:super(name)   -- call the ancestor initializer if needed
46 --   self.name = name
47 -- end
48 --
49 -- local pussycat = Cat("pussycat")
50 -- print(pussycat.name)  --> pussycat
51
52 --- checks whether an __instance__ is derived from some class.
53 -- Works the other way around as `class_of`. It has two ways of using;
54 -- 1) call with a class to check against, 2) call without params.
55 -- @function instance:is_a
56 -- @param some_class class to check against, or `nil` to return the class
57 -- @return `true` if `instance` is derived from `some_class`, or if `some_class == nil` then
58 -- it returns the class table of the instance
59 -- @usage local pussycat = Lion()  -- assuming Lion derives from Cat
60 -- if pussycat:is_a(Cat) then
61 --   -- it's true, it is a Lion, but also a Cat
62 -- end
63 -- 
64 -- if pussycat:is_a() == Lion then
65 --   -- It's true
66 -- end
67 local function is_a(self,klass)
68     if klass == nil then
69         -- no class provided, so return the class this instance is derived from
70         return getmetatable(self)
71     end
72     local m = getmetatable(self)
73     if not m then return false end --*can't be an object!
74     while m do
75         if m == klass then return true end
76         m = rawget(m,'_base')
77     end
78     return false
79 end
80
81 --- checks whether an __instance__ is derived from some class.
82 -- Works the other way around as `is_a`.
83 -- @function some_class:class_of
84 -- @param some_instance instance to check against
85 -- @return `true` if `some_instance` is derived from `some_class`
86 -- @usage local pussycat = Lion()  -- assuming Lion derives from Cat
87 -- if Cat:class_of(pussycat) then
88 --   -- it's true
89 -- end
90 local function class_of(klass,obj)
91     if type(klass) ~= 'table' or not rawget(klass,'is_a') then return false end
92     return klass.is_a(obj,klass)
93 end
94
95 --- cast an object to another class.
96 -- It is not clever (or safe!) so use carefully.
97 -- @param some_instance the object to be changed
98 -- @function some_class:cast
99 local function cast (klass, obj)
100     return setmetatable(obj,klass)
101 end
102
103
104 local function _class_tostring (obj)
105     local mt = obj._class
106     local name = rawget(mt,'_name')
107     setmetatable(obj,nil)
108     local str = tostring(obj)
109     setmetatable(obj,mt)
110     if name then str = name ..str:gsub('table','') end
111     return str
112 end
113
114 local function tupdate(td,ts,dont_override)
115     for k,v in pairs(ts) do
116         if not dont_override or td[k] == nil then
117             td[k] = v
118         end
119     end
120 end
121
122 local function _class(base,c_arg,c)
123     -- the class `c` will be the metatable for all its objects,
124     -- and they will look up their methods in it.
125     local mt = {}   -- a metatable for the class to support __call and _handler
126     -- can define class by passing it a plain table of methods
127     local plain = type(base) == 'table' and not getmetatable(base)
128     if plain then
129         c = base
130         base = c._base
131     else
132         c = c or {}
133     end
134    
135     if type(base) == 'table' then
136         -- our new class is a shallow copy of the base class!
137         -- but be careful not to wipe out any methods we have been given at this point!
138         tupdate(c,base,plain)
139         c._base = base
140         -- inherit the 'not found' handler, if present
141         if rawget(c,'_handler') then mt.__index = c._handler end
142     elseif base ~= nil then
143         error("must derive from a table type",3)
144     end
145
146     c.__index = c
147     setmetatable(c,mt)
148     if not plain then
149         c._init = nil
150     end
151
152     if base and rawget(base,'_class_init') then
153         base._class_init(c,c_arg)
154     end
155
156     -- expose a ctor which can be called by <classname>(<args>)
157     mt.__call = function(class_tbl,...)
158         local obj
159         if rawget(c,'_create') then obj = c._create(...) end
160         if not obj then obj = {} end
161         setmetatable(obj,c)
162
163         if rawget(c,'_init') then -- explicit constructor
164             local res = call_ctor(c,obj,...)
165             if res then -- _if_ a ctor returns a value, it becomes the object...
166                 obj = res
167                 setmetatable(obj,c)
168             end
169         elseif base and rawget(base,'_init') then -- default constructor
170             -- make sure that any stuff from the base class is initialized!
171             call_ctor(base,obj,...)
172         end
173
174         if base and rawget(base,'_post_init') then
175             base._post_init(obj)
176         end
177
178         return obj
179     end
180     -- Call Class.catch to set a handler for methods/properties not found in the class!
181     c.catch = function(self, handler)
182         if type(self) == "function" then
183             -- called using . instead of :
184             handler = self
185         end
186         c._handler = handler
187         mt.__index = handler
188     end
189     c.is_a = is_a
190     c.class_of = class_of
191     c.cast = cast
192     c._class = c
193
194     if not rawget(c,'__tostring') then
195         c.__tostring = _class_tostring
196     end
197
198     return c
199 end
200
201 --- create a new class, derived from a given base class.
202 -- Supporting two class creation syntaxes:
203 -- either `Name = class(base)` or `class.Name(base)`.
204 -- The first form returns the class directly and does not set its `_name`.
205 -- The second form creates a variable `Name` in the current environment set
206 -- to the class, and also sets `_name`.
207 -- @function class
208 -- @param base optional base class
209 -- @param c_arg optional parameter to class constructor
210 -- @param c optional table to be used as class
211 local class
212 class = setmetatable({},{
213     __call = function(fun,...)
214         return _class(...)
215     end,
216     __index = function(tbl,key)
217         if key == 'class' then
218             io.stderr:write('require("pl.class").class is deprecated. Use require("pl.class")\n')
219             return class
220         end
221         compat = compat or require 'pl.compat'
222         local env = compat.getfenv(2)
223         return function(...)
224             local c = _class(...)
225             c._name = key
226             rawset(env,key,c)
227             return c
228         end
229     end
230 })
231
232 class.properties = class()
233
234 function class.properties._class_init(klass)
235     klass.__index = function(t,key)
236         -- normal class lookup!
237         local v = klass[key]
238         if v then return v end
239         -- is it a getter?
240         v = rawget(klass,'get_'..key)
241         if v then
242             return v(t)
243         end
244         -- is it a field?
245         return rawget(t,'_'..key)
246     end
247     klass.__newindex = function (t,key,value)
248         -- if there's a setter, use that, otherwise directly set table
249         local p = 'set_'..key
250         local setter = klass[p]
251         if setter then
252             setter(t,value)
253         else
254             rawset(t,key,value)
255         end
256     end
257 end
258
259
260 return class
261