Using LuaRocks dependencies

It is hard and time-consuming to write everything from scratch. Usually, we rely on libraries for common tasks. The libraries themselves bring some dependencies, and so on. Thankfully, there is no need in figuring out the dependency graph and managing dependency versions manually – package managers do the boring job for us.

In the Lua world, LuaRocks is the most commonly used package manager. It is also a repository of libraries for all occasions. Marta supports LuaRocks out-of-the-box.

In this tutorial, we will create a plugin that creates files with arbitrary names. We will use the uuid for name generation.

Multi-file plugins

Single-file plugins does not support LuaRocks dependencies, so we need to create a multi-file plugin.

🚩 Create the plugin structure as in the snippet below. Declare the plugin and the action.

Plugins/
    uuid/
        init.lua

marta module

If you read the previous tutorials, you know that plugins are declared with a plugin { ... } block, and actions – with action { ... } block. Actually, this is only a part of truth.

Marta API comes in two modules: marta contains core API functionality, and martax provides miscellaneous utilities and helpers. We never used marta directly as its contents is implicitly exposed in single-file plugins. The reason is simplicity: trivial plugins should be as simple as possible.

However, this is not always the desirable behavior for complex plugins: one may want to set a global variable with the same name as some marta member. So in multi-file plugins we need either to replace plugin { ... } with marta.plugin { ... }, or expose marta members explicitly:

marta.expose()
plugin { ... }
action { ... }

You are free to choose the way you prefer. In this tutorials we will use marta.expose() as it avoids code duplication.

🚩 Check if your multi-file plugin loads successfully. Fix issues if needed.

Downloading LuaRocks dependencies

Marta supports LuaRocks dependency trees, but does not download dependencies itself. Instead, external libraries have to be bundled with the plugin. We use the luarocks command-line utility to download them.

  • Install LuaRocks. If you use Homebrew, the easiest way to install luarocks is to install the lua package.
  • Open the Terminal, navigate to the plugin directory (~/Library/Application Support/org.yanex.marta/Plugins).
  • Run luarocks install --tree rocks uuid. This will download the uuid library and all its dependencies (if any) to ./rocks.

The resulting tree (find . -type f) should look like this:

./init.lua
./rocks/lib/luarocks/rocks-5.3/manifest
./rocks/lib/luarocks/rocks-5.3/uuid/0.2-1/rock_manifest
./rocks/lib/luarocks/rocks-5.3/uuid/0.2-1/doc/index.html
./rocks/lib/luarocks/rocks-5.3/uuid/0.2-1/doc/ldoc.css
./rocks/lib/luarocks/rocks-5.3/uuid/0.2-1/doc/topics/readme.md.html
./rocks/lib/luarocks/rocks-5.3/uuid/0.2-1/uuid-0.2-1.rockspec
./rocks/share/lua/5.3/uuid.lua

While you can remove the ./rocks/lib/luarocks/rocks-5.3 directory, do not forget to include third-party licenses to your plugin, especially if you plan to distribute it.

Attaching LuaRocks dependencies

Call marta.useRocks() in the beginning of init.lua to import LuaRocks dependencies. By default, useRocks() will import the rocks directory contents. You can provide a custom directory name, e.g. useRocks("dependencies").

Let’s add an action to check if the library is attached correctly:

marta.useRocks()
local uuid = require("uuid")

action {
    id = "check",
    name = "Check UUID",
    apply = function()
        martax.alert(uuid())
    end
}

🚩 Restart Marta and check if the action shows an alert with a UUID.

File creation

Our action creates new files. However, we need to check if the current file system is writable, and notify the user if it is not.

action {
    id = "create.file",
    name = "Create UUID file",
    apply = function(context)
        local dir = context.activePane.model.directory
        if dir.fileSystem.isReadOnly then
            martax.alert("Can not create a file.", "File system is read only.")
            return
        end
    end
}
  • context.activePane.model.directory returns a VirtualFile for the current pane directory.
  • martax.alert() can accept up to four arguments:
    • alert(message)
    • alert(message, informativeText)
    • alert(message, informativeText, buttons)
      • For buttons you can pass a single name or a string array.
      • alert() returns an index of the pressed button.
    • alert(message, informativeText, buttons, style)
      • style can be warning, critical or informational (default).

We need also a bit of code to actually create a file:

local function createFile(dir) 
    local path = dir.absolutePath .. "/" .. uuid()
    local stream = dir.fileSystem:openOutputStream(path, false)
    stream.write("File content.")
    stream.close()
    dir.fileSystem.flush()
end

We could attach luaposix and call open(), but it would only work for the local file system. Our approach is FS-agnostic.

⚠️ Note that we called openOutputStream() as a method, with :. All methods in Marta API should be called this way.

The final flush() call is not required. However, it instructs a file system to write pending data to disk immediately and flush the caches.