Lua inheritance - oop

I have two classes in Lua.
test1 = {test1Data = 123, id= {0,3}}
function test1:hello()
print 'HELLO!'
end
function test1:new (inp)
inp = inp or {}
setmetatable(inp, self)
self.__index = self
return inp
end
test2 = {}
function test2:bye ()
print 'BYE!'
end
function test2:create_inst( baseClass )
local new_class = {}
local class_mt = { __index = new_class }
function new_class:create()
local newinst = {}
setmetatable( newinst, class_mt )
return newinst
end
if baseClass then
setmetatable( new_class, { __index = baseClass } )
end
return new_class
end
a = test1:new({passData='abc'})
print (a.test1Data, a.passData, a:hello())
c = test2:create_inst(a)
print (c.test1Data, c.passData,c:hello(), c:bye())
I want to inherit test2 from test but leave in the specified test2 methods bye.
Everything works except for bye:method.
How can I solve this problem?

You return an empty table in test2:create_inst(), at no point does anything reference test2, so the function test2:bye() is not in the table returned by test2:create_inst()

In your code, test2 actually has nothing to do with the table you are instancing, this new_class table you return from test2:create_inst is the new instance. So naturally it has no field named bye. Simple modification:
function test2:create_inst( baseClass )
...
if baseClass then
setmetatable( new_class, { __index = baseClass } )
end
...
function new_class:bye()
print("bye!")
end
return new_class
end

I think the answer want to implement Multi-Inheritance that new_class inherit "test2" and "baseClass".
local function search(k, objlist)
local i = 0
local v
while objlist[i] then
v = objlist[i][k]
if v then return v end
end
end
function class(...)
local parents = (...)
local object = {}
function object:new(o)
o = o or {}
setmetatable(o, object)
return o
end
object.__index = object
setmetatable(object,
{__index=function(t, k)
search(k, parents)
end})
return object
end
c = class(a, test2)
print(c.test1Data, c.passData,c:hello(), c:bye())

Related

Lua oop static object value

How would I make self.character.data().bank the same to all instances of the class
even when using the function setBank? currently when using setBank it only updates self.character.data().bank on the one instance if it makes sense
_G.GetUser = {
users = {},
users_group = {},
userData = {},
CurrentCharacter = {}
}
_G.GetUserMethods = {}
GetUserMethods.__call = function(self, source)
local o = setmetatable({}, {
__index = self
})
o.src = source
o.id = self.users[o.src]
o.rank = self.users_group[o.src]
return o
end
GetUserMethods.__index = {
getCurrentCharacter = function(self)
self.character = {}
self.character.id = self.CurrentCharacter[self.id]
self.character.data = function()
return self.userData[self.character.id]
end
self.character.setBank = function(v)
if self.character.data() then
self.character.data().bank = v
end
end
self.character.getBank = function()
if self.character.data() then
return self.character.data().bank
end
return nil
end
return self.character
end
}
setmetatable(GetUser, GetUserMethods)
Replace
importClass {'getuser'}
with
if not _G.GetUser then
importClass {'getuser'}
end
in each file.

"attempt to call table" without for

When I call the method stream that I made, with any table as argument, I get the error "attempt to call table".
As far as I know this error only occurs when I use a for wrong, but I don't have a for in the code that is executed...
function stream(input)
...
local function _stream(input)
local result = {
_stream = true,
_data = input._data or input,
-- Without the Operation-wrapping no function could access the input
-- Intermediate Operations
concat = function(str) return _concat(input,str) end,
distinct = function(func) return _distinct(input,func) end,
filter = function(func) return _filter(input,func) end,
limit = function(n) return _limit(input,n) end,
map = function(func) return _map(input,func) end,
skip = function(n) return _skip(input,n) end,
sort = function(func) return _sort(input,func) end,
split = function(func) return _split(input,func) end,
-- Terminal Operations
allmatch = function(func) return _allmatch(input,func) end,
anymatch = function(func) return _anymatch(input,func) end,
collect = function(coll) return _collect(input,coll) end,
count = function() return _count(input) end,
foreach = function(func) return _foreach(input,func) end,
max = function(func) return _max(input,func) end,
min = function(func) return _min(input,func) end,
nonematch = function(func) return _nonematch(input,func) end,
reduce = function(init,op) return _reduce(input,init,op) end,
toArray = function() return _toArray(input) end
}
return result
end
if input == nil then
error("input must be of type table, but was nil")
elseif type(input) ~= "table" then
error("input must be of type table, but was a "..type(input)..": "..input)
end
return _stream(input)
end
table.stream = stream
Full Source - Older Version (where the argument gets deleted for some reason)
I want to use the method for a more stream-based programming style.
Very similar to this project, but more not only for numbers and with named keys.
In your code
function stream(input)
-- ...
local function _count()
local count = 0
for k, v in pairs(input._data) do
count = count + 1
end
return count
end
-- ...
local function _stream(input)
local result = {
_stream = true,
_data = input._data or input,
-- ...
count = _count,
-- ...
}
return result
end
return _stream(input)
end
print(stream({1,2,3}).count())
the created object _stream(input) does contain _data field, but input upvalue still refers to your argument {1,2,3} which doesn't have _data field.
It could be fixed by working with the object instead of input argument:
function stream(input)
-- ...
local obj
local function _count()
local count = 0
for k, v in pairs(obj._data) do
count = count + 1
end
return count
end
-- ...
local function _stream(input)
local result = {
_stream = true,
_data = input._data or input,
-- ...
count = _count,
-- ...
}
return result
end
obj = _stream(input)
return obj
end
print(stream({1,2,3}).count())

Lua script throws error "attempt to call a nil value (field 'deposit')"

I have this Lua script, which is supposed to create a new class, create an instance and call functions, but there's an error in which I actually call the methods.
Account = {
balance = 0,
new = function(self,o)
o = o or {}
setmetatable(o,self)
self.__index = self
return o
end,
deposit = function(self,money)
self.balance = self.balance + money
end,
withdraw = function(self,money)
self.balance = self.balance - money
end
}
new_account = Account.new()
print(new_account.balance)
new_account.deposit(23)
new_account.deposit(1)
print(new_account.balance)
It keeps throwing this error:
attempt to call a nil value (field 'deposit')
It seems to work like this:
Account = {
balance = 0,
}
function Account:new(o)
o = o or {}
setmetatable(o,self)
self.__index = self
return o
end
function Account:deposit(money)
self.balance = self.balance + money
end
function Account:withdraw(money)
self.balance = self.balance - money
end
function Account:get_balance()
return self.balance
end
acc = Account:new({})
print(acc.balance)
acc:deposit(1920)
print(acc:get_balance())
I don't seem to get what's wrong. Maybe it's the ':' operator that only works?
Yes, you need to use : to call methods:
new_account = Account:new()
print(new_account.balance)
new_account:deposit(23)
new_account:deposit(1)
print(new_account.balance)
Account:new() is sugar for Account.new(Account), etc.

A function inside a class modifies prototype instead of just returning an object

I have this code in LiveScript (1.1.1):
class A
b = -> { a: 1 b: 2 }
It compiles into this:
var A;
A = (function(){
A.displayName = 'A';
var b, prototype = A.prototype, constructor = A;
b = function(){
return prototype.a = 1, prototype.b = 2, prototype;
};
function A(){}
return A;
}());
Why does b() modify prototype? I expected it to just return a { a: 1, b: 2 } associative array.
It's because of this :
https://github.com/gkz/LiveScript/commit/d49b3ee8e8e2d5d7b9f128fa98c210b582e095fe
Which should probably be removed then, mmh.

How to use ScalaQuery to insert a BLOB field?

I used ScalaQuery and Scala.
If I have an Array[Byte] object, how do I insert it into the table?
object TestTable extends BasicTable[Test]("test") {
def id = column[Long]("mid", O.NotNull)
def extInfo = column[Blob]("mbody", O.Nullable)
def * = id ~ extInfo <> (Test, Test.unapply _)
}
case class Test(id: Long, extInfo: Blob)
Can I define the method used def extInfo = column[Array[Byte]]("mbody", O.Nullable), how to operate(UPDATE, INSERT, SELECT) with the BLOB type field?
BTW: no ScalaQuery tag
Since the BLOB field is nullable, I suggest changing its Scala type to Option[Blob], for the following definition:
object TestTable extends Table[Test]("test") {
def id = column[Long]("mid")
def extInfo = column[Option[Blob]]("mbody")
def * = id ~ extInfo <> (Test, Test.unapply _)
}
case class Test(id: Long, extInfo: Option[Blob])
You can use a raw, nullable Blob value if you prefer, but then you need to use orElse(null) on the column to actually get a null value out of it (instead of throwing an Exception):
def * = id ~ extInfo.orElse(null) <> (Test, Test.unapply _)
Now for the actual BLOB handling. Reading is straight-forward: You just get a Blob object in the result which is implemented by the JDBC driver, e.g.:
Query(TestTable) foreach { t =>
println("mid=" + t.id + ", mbody = " +
Option(t.extInfo).map { b => b.getBytes(1, b.length.toInt).mkString })
}
If you want to insert or update data, you need to create your own BLOBs. A suitable implementation for a stand-alone Blob object is provided by JDBC's RowSet feature:
import javax.sql.rowset.serial.SerialBlob
TestTable insert Test(1, null)
TestTable insert Test(2, new SerialBlob(Array[Byte](1,2,3)))
Edit: And here's a TypeMapper[Array[Byte]] for Postgres (whose BLOBs are not yet supported by ScalaQuery):
implicit object PostgresByteArrayTypeMapper extends
BaseTypeMapper[Array[Byte]] with TypeMapperDelegate[Array[Byte]] {
def apply(p: BasicProfile) = this
val zero = new Array[Byte](0)
val sqlType = java.sql.Types.BLOB
override val sqlTypeName = "BYTEA"
def setValue(v: Array[Byte], p: PositionedParameters) {
p.pos += 1
p.ps.setBytes(p.pos, v)
}
def setOption(v: Option[Array[Byte]], p: PositionedParameters) {
p.pos += 1
if(v eq None) p.ps.setBytes(p.pos, null) else p.ps.setBytes(p.pos, v.get)
}
def nextValue(r: PositionedResult) = {
r.pos += 1
r.rs.getBytes(r.pos)
}
def updateValue(v: Array[Byte], r: PositionedResult) {
r.pos += 1
r.rs.updateBytes(r.pos, v)
}
override def valueToSQLLiteral(value: Array[Byte]) =
throw new SQueryException("Cannot convert BYTEA to literal")
}
I just post an updated code for Scala and SQ, maybe it will save some time for somebody:
object PostgresByteArrayTypeMapper extends
BaseTypeMapper[Array[Byte]] with TypeMapperDelegate[Array[Byte]] {
def apply(p: org.scalaquery.ql.basic.BasicProfile) = this
val zero = new Array[Byte](0)
val sqlType = java.sql.Types.BLOB
override val sqlTypeName = "BYTEA"
def setValue(v: Array[Byte], p: PositionedParameters) {
p.pos += 1
p.ps.setBytes(p.pos, v)
}
def setOption(v: Option[Array[Byte]], p: PositionedParameters) {
p.pos += 1
if(v eq None) p.ps.setBytes(p.pos, null) else p.ps.setBytes(p.pos, v.get)
}
def nextValue(r: PositionedResult) = {
r.nextBytes()
}
def updateValue(v: Array[Byte], r: PositionedResult) {
r.updateBytes(v)
}
override def valueToSQLLiteral(value: Array[Byte]) =
throw new org.scalaquery.SQueryException("Cannot convert BYTEA to literal")
}
and then usage, for example:
...
// defining a column
def content = column[Array[Byte]]("page_Content")(PostgresByteArrayTypeMapper)