I am trying to override a function from a class in Lua, but it is not working as I expected. This is how my class file looks like:
car.lua
function car.tesla:start_car()
-- do some stuff
result = self.crank()
-- do other stuff
end
function car.tesla.crank()
-- calculate some_boolean
return some_boolean
end
In my test file I want to mock the output of crank() function for a specific instance
in my test_car.lua file
local test_car = car_module.car:new("tesla")
test_car.crank = function() return true end
test_car.start()
assertEquals(test_car.is_started, true)
I also tried writing another function and doing the assignation but it didn't work either:
function mock_crank()
return true
end
test_car.crank = mock_crank
When added debugging print statements, I see that the original function is being invoked instead of the overriding i am doing in the test file. What I am doing wrong here? Thanks in advance!
Related
So i'm making my own OS for LIKO-12 and I need to run .lk12 files but i need to have a function to run a string as code and return a table that has all of the variables that code created
Example:
function foo()
return "Hello,"
end
function bar()
return " World!"
end
hello = foo()..bar()
should return a table with 2 functions:
A function called foo that returns "Hello,"
A function called bar that returns " World!"
and a variable called hello that has "Hello, World!"
You should be able to call a function from the code using
vars["foo"]() or vars.foo()
Can anyone help me?
When you create or use a global variable it's actually stored in a table called "the environment", or _ENV (which is not global, it's a local that you get automatically)
So your code:
function foo()
return "Hello,"
end
function bar()
return " World!"
end
hello = foo()..bar()
is really doing:
function _ENV.foo()
return "Hello,"
end
function _ENV.bar()
return " World!"
end
_ENV.hello = _ENV.foo().._ENV.bar()
so if we just set _ENV to a new table, and return it, then this is exactly the table you want. You can do this with load (which is how you run strings) by passing it as the 4th argument. This function will do that:
function loadAndReturnEnv(codeString)
local env = {}
load(codeString, nil, nil, env)() -- error checking skipped for demonstration
return env
end
However, note that all the usual global stuff like string.rep, table.sort, load, print don't exist in this new _ENV. If that's what you want, that's great. But I guess you probably do want them. In that case we can use the metatable __index feature, so that if the code looks for something in its _ENV and it's not there, it looks in our (the caller's) _ENV instead.
function loadAndReturnEnvWithGlobals(codeString)
-- note: env is the environment for the code we're about to run
-- (which will be called _ENV in that code), and _ENV is the environment
-- of the loadAndReturnEnvWithGlobals function (i.e. the "real" environment)
local env = {}
setmetatable(env, {__index=_ENV})
load(codeString, nil, nil, env)() -- error checking skipped for demonstration
setmetatable(env, nil) -- set the table back to normal before we return it
return env
end
But wait... the program can now call print when you run it, but if it defines a function, that function can't call print since we deleted the link back to the real environment after the code returned. So I think the best way to fix that is to leave the environment linked, and just copy the functions etc.
function loadAndReturnEnvWithGlobals2(codeString)
local env = {}
setmetatable(env, {__index=_ENV})
load(codeString, nil, nil, env)() -- error checking skipped for demonstration
local result = {}
for k,v in pairs(env) do result[k]=v end
return result
end
although this copies the values, so if you have any functions that change the variables, you won't see the changed variables.
Which version you use is up to you. They each have pros and cons.
When require is called in testt.lua which is one of two files the return is movee and movee.lua.
movee are for the most part a class to be required, but should be able to accept to be called direct with parameter.
movee.lua
local lib = {} --this is class array
function lib.moveAround( ... )
for i,direction in ipairs(arg) do
print(direction)
end
end
function lib.hello()
print("Hello water jump")
end
lib.moveAround(...)
return lib
testt.la
local move = require("movee")
Expected result is not to call lib.moveAround or print of file name when require is called.
Your expectations are incorrect. Lua, and most scripting languages for that matter, does not recognize much of a distinction between including a module and executing the Lua file which provides that module. Every function statement is a statement whose execution creates a function object. Until those statements are executed, those functions don't exist. Same goes for your local lib = {}. And so on.
Now, if you want to make a distinction between when a user tries to require your script as a module and when a user tries to execute your script on the command line (or via just loadfile or similar), then I would suggest doing the following.
Check the number of arguments the script was given. If no arguments were given, then your script was probably required, so don't do the stuff you don't want to do when the user requires your script:
local nargs = select("#", ...)
if(nargs > 0) then
lib.moveAround(...)
end
Solved by replacing
lib.moveAround(...)
with
local argument = {...}
if argument[1] ~= "movee" and argument[2] ~= "movee" then
lib.moveAround(...)
end
require("movee")
will execute the code within movee.lua
lib.moveAround(...)
is part of that code. Hence if you require "movee" you call lib.moveAround
If the expected result is not to call it, remove that line from your code or don't require that file.
Is it possible to intercept a global function call, and run another function when the global is called.
local test = global()
function test()
print("Hello World")
end
I've tried this but it doesn't seem to work.
Edit
My original question was phrased poorly I ended up coming up with this solution. The functions I'm trying to change are inside of a table so I just make a copy of the table, overwrite the function and then copy the backup table over the original to restore the function.
copy = {}
i,v = next(globaltable, nil)
while i do
copy[i] = v
i,v = next(globaltable,i)
end
function globaltable:function()
do some stuff
globaltable = copy
end
If you want to actually call test when calling global(), assign test to global:
local old_global = global
global = test
This makes global to refer to the function object of test.
Why can't this Lua function using a self ":" be marked "local" without getting:
'(' expected near ':'
That is, in the code below things work. But why can't I make the "scene:createScene" function local (as I get the above-mentioned error when trying).
I note the listener functions need to be made local else I have struck cross-scene issues at times in storyboard. These can be marked local and work fine.
SceneBase = {}
function SceneBase:new()
local scene = Storyboard.newScene()
local function returnButtonTouch_Listener (event)
-- code here
end
function scene:createScene( event ) -- WHY CAN'T THIS BE LOCAL???
-- code here
end
return scene
end
return SceneBase
That is why can't the function line read:
local function scene:createScene( event )
function scene:createScene(event) ... end
Is syntax sugar for:
scene.createScene = function(self, event) ... end
Which is syntax sugar for:
scene["createScene"] = function(self, event) ... end
You want to do:
local scene["createScene"] = function(self, event) ... end
Which makes no sense.
Another way of putting it: local is a qualifier for a variable which makes it local rather than global. What variable would you be qualifying with local function scene:createScene( event )?
createScene is not a variable, it's a key in the table scene.
Actually, that's a bit misleading. When you say:
foo = 10
Without qualification, foo becomes global, which is to say it's stored in the global state like this:
_G.foo = 10;
Which of course means the same as this:
_G["foo"] = 10;
When you use the keyword local, it doesn't get stored in a table, it ends up stored in a VM register, which is both faster and has more tightly constrained scope.
When you write either of these:
function foo.bar() end
function foo:bar() end
You are explicitly storing the function value in a table (foo). Those statements are exactly equivalent to these, respectively:
foo["bar"] = function() end
foo["bar"] = function(self) end
I note the listener functions need to be made local
What do you mean by that? In Lua, a function is a function is a function. It's just another value, like a string or number. Whether it's stored in a global, table, local, or not stored at all is irrelevant.
local foo = print
_G["zap"] = foo
doh = zap
t = { zip = doh }
t.zip("Hello, World") -- Hello, World
assert(print == foo
and zap == foo
and zap == doh
and t.zip == doh)
Here we pass the print function around. It's all the same function, and as long as we have a reference to it we can call it.
I don't know Corona, but I'm guessing that event handlers are not specified by naming convention of locals. You need to register it as an event handler. For instance, according to this video a Button object has an onEvent field which is set to the handler for that button.
I'm creating a lua script that should run on the TI-Nspire calculator. The problem is that while running my script I get the error Attempt to index local 'self' (a nil value) when the button:activate() method is called. The parser says the error is in the 8th line in the code below. The problematic code is as follows:
button = class(view)
function button:init()
self.selected = false
end
function button:activate()
self.selected = true
end
I call the activate function like this:
item = button()
local action = "activate"
local arguments = {}
item[action](unpack(arguments))
I am aware the class() function doesn't exist in "stock" Lua, it's a function available in the TI-Nspire Lua implementation. You can find its definition and usage here.
obj:methodname(args) is sugar for obj.methodname(obj,args). So, if you want to use the syntax item[action](unpack(arguments)), you need to use item[action](item,unpack(arguments)). Otherwise, try item:activate(unpack(arguments)) if you can use method explicitly.