AceLibrary Tutorial

From WowAce Wiki
Jump to: navigation, search

Note: for full API documentation, see AceLibrary API Documentation

Libraries

Generally speaking, libraries should have certain characteristics:

  • They should encapsulate code that promotes code reuse. Don't write libraries that only one addon will ever use.
  • They should be as efficient as possible. More than one addon will be using your library, don't bog them down with unnecessary function calls, table usage, etc. If you put out a badly coded library, you're not just inconveniencing yourself, but everyone else who uses the library, and their users.

Before you dive into writing a library, ask yourself:

  • Will this library ever be used by anything else?
  • Am I writing this library for the sake of a library? Will I be better off just adding it as a common code file in my addon without incurring the extra overhead the library call will make?

The Ace philosophy has always been about small, efficient code. About addons that do what they are specced to do and do it well without unnecessary bloat. Memory efficient, CPU efficient code. Always keep that in mind when writing libraries to be used with the Ace Framework.

Creating the library

Libraries are traditionally one lua file in a folder which is self-contained. It may require other libraries as dependencies, but should not require whole addons.

Header

To start off your library, you'd make a lua file. For this tutorial, I'll be calling it Monkey-1.0.

In this file (Monkey-1.0.lua), you would start off with the Library Header.

--[[
Name: Monkey-1.0
Revision: $Rev: 1 $
Author(s): John Doe ([email protected])
Website: http://www.wowace.com/
Documentation: http://wiki.wowace.com/index.php/Monkey-1.0_API_Documentation
SVN: http://svn.wowace.com/root/trunk/Monkey/Monkey-1.0/
Description: Library that can act an awful lot like a monkey
Dependencies: AceLibrary

]]

The SVN part can point to your own SVN server if you want. If you don't have an SVN server or an SVN account with wowace. For more info, see What is the SVN?

For libraries, you should have some sort of documentation. Feel free to get a wiki account and add a page for your project.

Notice how the Revision is wrapped in the funny "$Rev: 1 $" thing. That is an svn:keyword. It will automatically update every time you commit your file to an SVN.

Version

local MAJOR_VERSION = "Monkey-1.0"
local MINOR_VERSION = "$Revision: 1 $"

This will set you up so you can easily refer to these variables later when registering and such.

Your major version is the name of your library, in the form of "Name-#.#". Although it can technically be any string you want, this is the standard form. Your major version should match the name of the file and folder your library is in.

Your minor version will increase with every release. The simplest thing to do is have it be a revision string, that increases with every commit. This can either be a revision string in the form of "$Revision: ### $" or just an integer.

Check for AceLibrary

if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end

This makes sure that AceLibrary is properly loaded and ready to go. If not, it raises an error that makes sense.

Check for obscelesence

if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end

This checks to see if the current major and minor versions are either already out-of-date or already registered. If this is the case it returns out of the file, saving valuable CPU cycles and memory.

Localization/Internationalization (optional)

You should (optionally) do your localizations here. By having it as close to the top as possible, you make it easy on your translators. Feel free to use AceLocale-2.0 or use your own method to do your translations.

Here, I will use a haphazard way to do my own localizations:

local TEXT_MONKEY
if GetLocale() == "deDE" then
    TEXT_MONKEY = "Affe"
else -- enUS
    TEXT_MONKEY = "Monkey"
end

Create the library object

local Monkey = {}

Difficult, I know. You'll get the hang of it.

Note: If you were to use AceOO and want this to be a mixin or class or an instance, you'd have no trouble registering that instead of just a simple table.

Methods

Here all your assorted methods for your library can be defined.

Let's define some monkey-esque methods now.

function Monkey:FlingPoop()
    self.poopFlung = self.poopFlung   1
    DEFAULT_CHAT_FRAME:AddMessage(string.format("You have had poop flung at you %d times.", self.poopFlung))
end

function Monkey:GroomOther()
    DEFAULT_CHAT_FRAME:AddMessage("You see one monkey groom another monkey")
end

function Monkey:EatBanana()
    local totalBananas = (time() - self.startTime) / (60 * 5)
    -- you get 1 banana every 5 minutes
    local currentBananas = totalBananas - self.bananasEaten

    if currentBananas >= 1 then
        self.bananasEaten = self.bananasEaten   1
        DEFAULT_CHAT_FRAME:AddMessage("The monkey eats a banana. Yum")
        return true
    else
        DEFAULT_CHAT_FRAME:AddMessage("The monkey looks sad, like it wants a banana but has none")
        return false
    end
end

Gotta love them monkeys. They're like little people (unless you're from Kentucky, where they're just monkeys and nothing else).

Activation function

local function activate(self, oldLib, oldDeactivate)
    if oldLib then -- if upgrading
        self.bananasEaten = oldLib.bananasEaten
        self.poopFlung = oldLib.poopFlung
        self.startTime = oldLib.startTime
    end

    -- make sure all the basic values are available
    if not self.bananasEaten then
        self.bananasEaten = 0
    end
    if not self.poopFlung then
        self.poopFlung = 0
    end
    if not self.startTime then
        self.startTime = time()
    end

    if oldDeactivate then -- clean up the old library
        oldDeactivate(oldLib)
    end
end

The activation function is important, as it is called whenever your library updates itself or is initially registered. Your values should go in this place.

Registration

AceLibrary:Register(Monkey, MAJOR_VERSION, MINOR_VERSION, activate)
Monkey = nil

This registers your Monkey table with AceLibrary, to be accessed through MAJOR_VERSION, which is "Monkey-1.0". This will also cause activate to be called, with the appropriate arguments fed into it.
Note: you could also supply a deactivate function, but it's typically not needed.

Registration with AceLibrary destroys the contents of the table registered as a safety measure, thus it is in your best interest to just nil out the table, letting garbage collection take its course. Alternatively, you could do Monkey = AceLibrary (MAJOR_VERSION) to get the proper library into the Monkey reference, also causing the garbage collection to occur.

Done

Feel free to run your tests, commit to SVN, talk about it on IRC, release it on the wowace forums, and/or release to your favorite Addon site.

Using the library

Firstly, you should copy the library folder to your addon's folder. Alternatively, you could set up an svn:external to the folder, which would automatically update whenever the author updates his library.

In the TOC

In your TOC, be sure to add the following lines to the top of your files:

AceLibrary\AceLibrary.lua
Monkey-1.0\Monkey-1.0.lua

In the LUA

In your .lua file, somewhere near the top, put

local monkey = AceLibrary("Monkey-1.0")

Now you have a reference to the Monkey-1.0 library, all fine and dandy.
Note: you do not need to call it monkey, you can call it whatever you want (even baboon).

Using the library's methods

if not monkey:EatBanana() then
    monkey:FlingPoop()
end

Hooray, now when that code is run, if the monkey eats a banana, it'll be content, if not, it'll do its admirable duty and fling poop.