2

I'm using OpenResty and my app is setup as:

app/
   clients/
      photoClient.lua
   init.lua
   auth.lua

Let's say photoClient has both an unauthenticated and a authenticated endpoint (similar to an avatar endpoint that is accessible without logging in, but there may be private photos that you need to login first)

So in terms of dependencies, I have:

-- auth.lua

local photoClient = require('app.clients.photoClient')
-- this is used to show avatar on the login page

local auth = {}

auth.isAuthenticated = function ()
   -- logic to check authentication
end

return auth

and the client is

-- photoClient.lua
local auth = require('app.auth')

local photoClient = {}
photoClient.privateEndpoint = function()
   if (!auth.isAuthenticated()) {
       ngx.exit(403)
   }
   ...
end

photoClient.getAvator = function() {
   -- this is a public method used by auth
}

return photoClient

This is giving me a circular dependency issue. I've seen on other SO post that you can use global variables, i.e. to do photoClient = photoClient or require('app.clients.photoClient') but I do not want to use global variables and want to keep each module scoped to itself.

How can I do this?

idbrii
  • 10,975
  • 5
  • 66
  • 107
Kousha
  • 32,871
  • 51
  • 172
  • 296
  • Inside client module, move the line `local auth = require('app.auth')` inside the body of `privateEndpoint` function – Egor Skriptunoff Feb 27 '19 at 06:04
  • @EgorSkriptunoff is that efficient? In almost all other languages you `require` your modules at the beginning, global to the scope of the module itself. If I were to have `anotherPrivateMethod` then I'd have to add `local auth = require('app.auth')` there too. Lua seems different than other languages such as Node, Python in the matter. – Kousha Feb 27 '19 at 06:08
  • There is absolutely no reason to strive moving `require` to the top of the Lua script. – Egor Skriptunoff Feb 27 '19 at 07:20
  • Slightly confused, you do not want to use global variables as in the example photoClient = photoClient or require ('...') However the act of 'requiring' your module is going to create a global variable called photoClient. It is already there, so what is the harm in testing for it? – Chris Mar 04 '19 at 14:39

2 Answers2

0

Found the solution on LuaFlare Documentation by Kate Adams: basically add package.loaded[...] = your_module before you do return your_module to every file, i.e.

-- auth.lua
local auth = {}
package.loaded[...] = auth

local photoClient = require('app.clients.photoClient')

...

return auth


-- photoClient.lua
local photoClient = {}
package.loaded[...] = photoClient

local auth = require('app.auth')

...

return photoClient

Fixed the problem. Here is the link to the book's page for anyone who is interested to read more.

Egor Skriptunoff
  • 906
  • 1
  • 8
  • 23
Kousha
  • 32,871
  • 51
  • 172
  • 296
  • 1
    This is not a correct solution. It looks like an ugly workaround. The correct solution is to use `require` only where you really need this module. – Egor Skriptunoff Feb 27 '19 at 07:17
  • @EgorSkriptunoff can you explain why this is not a correct solution? In terms of "looks" I actually prefer this far over `require` the module inside every method, you call (as a preference). – Kousha Feb 27 '19 at 08:11
  • Your solution is nothing better then using globals. Actually, `package.loaded` is a global variable used internally by Lua. Using implementation-specific details of interpreter to solve pure-logical problem of your code is a workaround, not a solution. What you really need is a "deferred/lazy require", and there are more clean ways to do this in Lua (compared to using `package.loaded`). If you have many functions like `provateEndpoint` just write your own `lazy_require()` and place it at the beginning of your script. – Egor Skriptunoff Feb 27 '19 at 08:21
  • I see, isn't that exactly like Node require work? The `require` also contains a `Module._load` that manages the new modules and caches and is a global variable. But in Node land, when you `export` and `import/require` in another module, the bit of `package.loaded` that I added is self-managed by the engine directly. So I don't really see why it's the correct way in Node, and the wrong way in Lua. – Kousha Feb 27 '19 at 08:25
  • 2
    Why do you want to bring your old Node-land habits to Lua land? Lua is better. :-) – Egor Skriptunoff Feb 27 '19 at 08:28
  • BTW, your solution doesn't solve the problem. You must place `package.loaded[...] = ` at the top of a module, prior to invoking any `require` – Egor Skriptunoff Feb 27 '19 at 08:32
  • 1
    Haha touche! Okay, thanks for your help. Want to write an answer so I can mark as solution? – Kousha Feb 27 '19 at 08:32
  • Why writing another answer? Instead I've edited yours. – Egor Skriptunoff Feb 27 '19 at 08:40
0

Two main solutions:

Split

Split up auth into two modules that handle different aspects: an auth module for common logic that might be used by photoClient and a login module that is the login page.

This solution will prevent the same problem in other cases where you need to use auth.

Inject

In parent code (init.lua) that owns both of these modules, inject one into the other (call a function that passes the module).

idbrii
  • 10,975
  • 5
  • 66
  • 107