Model.lua

Home   »   Model.lua

declare class Model {
  constructor(model: Record);

  Subscribe(
    subscriber: (methodName: string, ...args: Args) => void,
  ): () => void;

  Destroy(): void;
}
local Model = {}
Model.__index = Model

local warn = warn or print

local function shallow(t)
  local nt = {}
  for k, v in pairs(t) do
    nt[k] = v
  end
  return nt
end

local function handleError(err)
  warn(
    ("Caught an error in a listener:\n%s\n%s")
    :format(err, debug.traceback())
  )
end

local function wrapMethod(method, methodName)
  return function (self, ...)
    method(self, ...)
    self:_FireListeners(methodName, ...)
  end
end

function Model.new(model)
  assert(type(model) == "table", "Model must be a table")
  local self = setmetatable(shallow(model), Model)
  self._subscribers = {}
  for k, v in pairs(model) do
    if type(v) == "function" then
      self[k] = wrapMethod(v, k)
    end
  end
  return self
end

function Model:_FireListeners(...)
  local args = {...}
  for _, subscriber in ipairs(self._subscribers) do
    -- xpcall with arguments will not work in Lua 5.1
    xpcall(function()
      subscriber(unpack(args))
    end, handleError)
  end
end

function Model:Subscribe(subscriber)
  assert(type(subscriber) == "function", "Subscriber must be a function")
  table.insert(self._subscribers, subscriber)
  return function()
    for i, v in ipairs(self._subscribers) do
      if v == subscriber then
        table.remove(self._subscribers, i)
      end
    end
  end
end

function Model:Destroy()
  setmetatable(self, nil)
end

return Model

Leave a Reply

Your email address will not be published.