How to inherit method? - lua

I want to inherit method "GetName" or other methods from "Create" for "CreateInherited" and i want save unique methods from "CreateInherited" (like "GetInheritName"), but i dont know how.
My test code:
local MainTbl = {}
function MainTbl:Create(name)
local tbl = {}
tbl.name = name or 'Null'
function tbl:GetName()
return self.name
end
setmetatable(tbl, self)
self.__index = self
return tbl
end
function MainTbl:CreateInherited(name)
local tbl = {}
tbl.name = name or 'Null'
function tbl:GetInheritName()
return self.name
end
setmetatable(tbl, self)
self.__index = self
return tbl
end
local Man = MainTbl:Create('Man')
local Woman = MainTbl:CreateInherited('Woman')
print(Man:GetName())
print(Woman:GetName())
print(Woman:GetInheritName())

If I understand you, you're trying to put two different constructors into a single class. Notice that Create does most of the work that CreateInherited needs to do, so you can save yourself a lot of repeated code by calling Create inside CreateInherited. Instead of starting with an empty table, you can start with a fully formed instance from Create and add a method to it.
function MainTbl:CreateInherited(name)
local tbl = self:Create(name)
function tbl:GetInheritName()
return self.name
end
return tbl
end

Related

LUA Metatable and Classes

local Class = {}
Class.__index = Class
--default implementation
function Class:new() print("Bye!!") end
--create a new Class type from our base class
function Class:derive(type)
local cls = {}
cls.type = type
cls.__index = cls
cls.super = self
setmetatable(cls, self)
return cls
end
function Class:__call(...)
local inst = setmetatable({},self)
inst:new(...)
return inst
end
function Class:get_type()
return self.type
end
--Create 'Player' class
local Player = Class:derive("Player")
function Player:new(name)
print("hello " .. name)
end
--Create player instance
plyr1 = Player('Dylan')
This is a Class module in lua
I have a few questions.
What is the point of Class:new() if it does not do anything?
At Line 19 inst:new(...) in the Class:__call(...) function,why does the program search for the :new function in the Player table and not the original Class table?
Meaning,
When Player("Dylan") is called, "Hello Dylan" is printed instead of "Bye!!"
Isn't the meta table of inst table set to self, which references Class?
Code is taken from youtube video https://www.youtube.com/watch?v=O15GoH7SDn0 about Lua Classes
When doing Player("Bob") we will enter the __call metamethod defined in the metatable of Player which is Class:__call
function Class:__call(...)
local inst = setmetatable({},self)
inst:new(...)
return inst
end
In this call self refers to the Player table not the Class table, so when we create inst we are setting it's metatable to Player which has an __index value that points to itself, so inst:new will be equal to Player.new(inst, name)

Clone object in LUA

I have the next code and I'm trying to create a copy of the object Test:
local Test = {} do
local var1, var2 = {}
function Test:Init(val1, val2, val3)
var1.val1 = val1
var1.val2 = val2
var1.val3 = val3
end
function Test:Print()
print(var1.val1)
print(var1.val2)
print(var1.val3)
end
function Test:New(obj)
obj = obj or {}
setmetatable(obj, self)
self.__index = self
return obj
end
end
Then I'm creating my objects like this:
local obj1 = Test:New()
obj1:Init('a','b','c')
local obj2 = Test:New()
obj2:Init('c','d','e')
obj1:Print()
obj2:Print()
The result will be this:
c, d, e
c, d, e
As you can see the method New is not cloning correctly the object and is sharing the same instance. What am I doing wrong? I haven't cloned an object before and I'm having issues to understand how to do it.
I got that code from here: https://www.lua.org/pil/16.2.html
Thanks.
The problem occurs because you are just changing local variables, not the object itself.
When you do local Test = {} do you are not doing too much about the Test object, you are just creating the table and then creating a new scope which will be executed immediately, and inside this scope you have the variable var1, which will be shared through the scope, not the object.
The correct way to implement variables for each is actually creating fields on it:
local Test = {} do
function Test:Init(val1, val2, val3)
self.val1 = val1
self.val2 = val2
self.val3 = val3
end
function Test:Print()
print(self.val1)
print(self.val2)
print(self.val3)
end
function Test:New(obj)
obj = obj or {}
setmetatable(obj, self)
self.__index = self
return obj
end
end
Anyway setmetatable it's not about cloning objects, it implement shared behaviors and then you can create as many objects you want with those behaviors.

How to change a table's metatable but inherit it's own methods in Lua

in Lua we do OO programming like this:
MyClass = {}
function MyClass:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
obj.hello = "hello world"
return obj
end
function MyClass:sayHi()
print(self.hello)
end
function main()
local obj = MyClass:new()
obj:sayHi()
end
When working with more compelx stuff, usually I take advantage of Lua's metamethods to proxy function calls and do whatever I need with it, like arguments parsing etc, by adding this:
MyClassMeta = {}
function MyClassMeta.__index(obj, funcName)
return function (self, ...)
//do some stuff
print("you called " .. funcName .. " with args", ...)
end
end
and changing the line:
setmetatable(obj, self)
to:
setmetatable(obj, MyClassMeta)
every function I call with an instance of MyClass will execute the code implemented in MyClassMeta.__index metamethod.
What I want to do now is inherit the MyClass existing methods, and execute MyClassMeta.__index only for functions that are not part of MyClass.
In the above example, the code will always execute the MyClassMeta.__index metamethod even when calling MyClass:sayHi():
function main()
local obj = MyClass:new()
obj:sayHi("hello")
end
you called sayHi with args hello
When you set __index to a table, it will look for properties on that table and return them if they don't exist on the instance. Since sayHi exists on the MyClass table it is used.
self.__index = self
When you set __index to a function, it can return anything for properties that don't exist on the instance. You can check if the key exists on the MyClass table and return it, then do something else if it doesn't:
MyClass = {}
MyMetatable = {
__index = function(obj, key)
if MyClass[key] ~= nil then return MyClass[key] end
return function(self, ...)
print("you called "..tostring(key))
print(" self.hello is '"..tostring(self.hello).."'")
print(" with args", ...)
end
end
}
function MyClass:new()
local obj = {}
setmetatable(obj, MyMetatable)
obj.hello = "hello world"
return obj
end
function MyClass:sayHi()
print(self.hello)
end
function main()
local obj = MyClass:new()
obj:sayHi()
end
local obj = MyClass:new()
obj:sayHi("hello")
obj:somethingElse(1, 2, 3)
Version with Egor's comment
MyClass = {}
setmetatable(MyClass, {
-- if it's not found on MyClass, return a function
__index = function(self, funcName)
return function(self, ...)
print("you called "..funcName.." with args", ...)
end
end
})
function MyClass:new()
local obj = {}
-- if it's not found on obj, try self (MyClass)
setmetatable(obj, { __index = self })
obj.hello = "hello world"
return obj
end
function MyClass:sayHi()
print(self.hello)
end
local obj = MyClass:new()
obj:sayHi()
obj:somethingElse(1, 2, 3)
When creating an object this sets the __index of the new object's metatable to MyClass, and MyClass's metatable's index to the function that is a fallback. So if the property isn't on your object or on MyClass, it will use the fallback.

Lua inserting a record to a table is failing

I have a main script as follows . This script is called script.lua (for the sake of this example)
require "modules\\myparentclass"
require "modules\\condition"
require "modules\\helpers"
require "constants"
parentclass = MyParentClass:new()
print ("MyParentClass Type : " .. parentclass:getCode())
-- add conditions
-- condition 1
condition1 = Condition:new(nil,"Are you happy?" , "YES")
parentclass:addCondition(condition1)
-- -- condition 2
condition2 = Condition:new(nil,"Are you sad?" , "NO")
parentclass:addCondition(condition2)
local l = parentclass:getConditions()
print(l[2]:getQuestion())
I have another class called MyParentClass whose code is as follows
require "constants"
require "modules\\condition"
require "modules\\helpers"
-- Meta class
MyParentClass = {code = ""}
function MyParentClass:new (o)
o = o or {}
setmetatable(o, self)
self.__index = self
self.condition = condition
self.conditions = {}
return o
end
function MyParentClass:getCode ()
return "Parent Class"
end
function MyParentClass:addCondition(condition)
print(condition)
table.insert(self.conditions,condition)
print('record inserted')
-- self.conditions = {next= self.conditions, value = condition}
end
function MyParentClass:getConditions()
return self.conditions
end
and I have a third class , Conditions which is as follows
require "constants"
-- Meta class
Condition = {question="", answer=""}
function Condition:new (o, question,answer)
o = o or {}
setmetatable(o, self)
self.__index = self
self.question = question or nil
self.answer = answer or nil
return o
end
function Condition:getCode ()
return CONDITION_TYPE
end
function Condition:getQuestion()
return self.question
end
function Condition:getAnswer()
return self.answer
end
The idea is that in the main script (script.lua) ,
I can create a new parent class.
Each parent class can have multiple conditions (aka questions).
For me, the first part is working. However I am failing with the second part. Whenever I run the script , I get two instances of the second question. Please see the below snapshot for more details.
.
Ideally, I would like to have both the conditions displayed ("are you happy?" and "are you sad?") but that is currently not the case.
Can you please help me out with this one?
Take a look at your constructor
function Condition:new (o, question,answer)
o = o or {}
setmetatable(o, self)
self.__index = self
self.question = question or nil
self.answer = answer or nil
return o
end
Everytime you call this you change the value of Condition.question and Condition.answer as self refers to Condition, not to your new object o!
So what you actually do is create 2 new tables condition1 and condition2 which both don't have their own .answer and .question. Therefor you access their metatable Condition which after you created condition2 contains: "Are you sad?" , "NO")
If you want to initialize members in your constructor you have to use o.answer and o.question.
Make sure you understand how that metatable stuff actually works.

mocking out a lua module using package.preload

I'm trying to write a unit test against a single module function. This module collaborates with a few other modules, so I'd like to mock those modules out to isolate my system under test. Here's some simplified pseudo code:
local moduleFoo={}
local moduleBaz= require("moduleBaz")
moduleFoo.doSomething = function (arg)
if moduleBaz.bar.neatMethod(arg) then
--does something interesting
end
end
return moduleFoo
And here's the code for moduleBaz
local moduleBaz={}
moduleBaz.bar= {}
moduleBaz.bar.neatMethod=function(arg)
--does something neat
end
return moduleBaz
I'm trying to use the package.preload function to inject a mock instance of moduleBaz before my tests run, but it doesn't appear to work (i.e. the real instance of the moduleBaz is used in the test, not my mock)
Here's some psueudo test code:
package.loaded.moduleBaz= nil
local moduleBaz = {}
moduleBaz.bar = {}
moduleBaz.bar.neatMethod= function(guid) return true end
package.preload['moduleBaz'] = function ()
return moduleBaz
end
local foo= require("moduleFoo")
foo.doSomething('asdasdasda')--real moduleBaz is called, not my mock!
Any ideas what I'm doing wrong? I'm very new to Lua, and not at all comfortable with how scope is handled in the language!
You seem to be missing a return statement in your moduleBaz code
return moduleBaz
Why not use package.loaded as it gives you a simpler interface? package.loaded.moduleBaz would simply need to include whatever you'd want to return from your moduleBaz code. Something like this should work or give you an idea:
package.loaded.moduleBaz = {
bar = {
neatmethod = function(arg)
-- your mock code here
end,
}
}
Then require('moduleBaz') would simply return that object you just created.
I cannot reproduce the issue with your setup either. The files I used are below; notice that I added return moduleBaz as I described above, but this is the only change I made:
file moduleBaz.lua:
local moduleBaz={}
moduleBaz.bar= {}
moduleBaz.bar.neatMethod=function(arg)
print "baz"
return true
end
return moduleBaz
file moduleFoo.lua:
local moduleFoo={}
local moduleBaz= require("moduleBaz")
moduleFoo.doSomething = function (arg)
if moduleBaz.bar.neatMethod(arg) then
print "foo"
end
end
return moduleFoo
file testFoo.lua
package.loaded.moduleBaz= nil
local moduleBaz = {}
moduleBaz.bar = {}
moduleBaz.bar.neatMethod= function(guid) print "mock" return true end
package.preload['moduleBaz'] = function ()
return moduleBaz
end
local foo= require("moduleFoo")
foo.doSomething('asdasdasda')--real moduleBaz is called, not my mock!
When I run this, I get mock\nfoo\n printed as expected.

Resources