Skip to content

Instantly share code, notes, and snippets.

@apendley
Last active December 17, 2015 12:29
Show Gist options
  • Save apendley/f45485bcfb9835ce7b5e to your computer and use it in GitHub Desktop.
Save apendley/f45485bcfb9835ce7b5e to your computer and use it in GitHub Desktop.
download each tab (in order) from a codea project at a base url
--# Main
-- Main
function requestDeferred(url, paramTable)
local deferred = Deferred()
local function _success(...) deferred:resolve(...) end
local function _fail(...) deferred:reject(...) end
http.request(url, _success, _fail, paramTable)
return deferred:promise()
end
function requestChained(urls)
local chained = requestDeferred(urls[1])
local promises = {chained}
for i = 2, #urls do
chained = chained:pipe(function()
return requestDeferred(urls[i])
end)
promises[#promises+1] = chained
end
return promises
end
local baseurl =
"https://raw.github.com/apendley/TPCodeaExamples/master/TPCodeaTabExample.codea/"
function setup()
requestDeferred(baseurl .. "Info.plist")
:fail(function(err)
error("Error loading Info.plist: " .. err)
end)
:done(function(data, status, headers)
local plist, _order, order = plistParse(data), {}, {}
-- copy all but "Main"
for i, v in ipairs(plist["Buffer Order"]) do
if v ~= "Main" then
_order[#_order+1] = v
end
end
-- fix up urls
for i, v in ipairs(_order) do
order[i] = baseurl .. v .. ".lua"
end
-- get list of chained request promises
local chained = requestChained(order)
-- add our handlers to them
for i, v in ipairs(chained) do
v:fail(function(err)
error("Error loading " .. _order[i] .. ": " .. err)
end)
:done(function(data, status, headers)
print("saving", _order[i])
saveProjectTab(_order[i], data)
if i == #chained then
print("Finished")
end
end)
end
end)
end
function draw()
background(0)
end
--# Callbacks
-- Callbacks
-- Ported from jQuery.Callbacks (v1.8)
-- Usage is identical to jQuery's with one exception:
--
-- 1: options are specified as a variable argument list of strings,
-- instead of jQuery's space separated string. i.e:
-- jQuery: var cb = Callbacks('once memory')
-- Lua: local cb = Callbacks('once', 'memory')
--
local pairs, ipairs = pairs, ipairs
local select, type, unpack = select, type, unpack
local tinsert, tremove = table.insert, table.remove
local function _has(list, fn)
if list and fn then
for i, cb in ipairs(list) do
if cb == fn then return true, i end
end
end
return false
end
local function _add(list, unique, ...)
local _args = {...}
for idx, arg in ipairs(_args) do
local _type = type(arg)
if _type == 'function' then
if not(unique and _has(list, arg)) then
tinsert(list, arg)
end
elseif _type == 'table' then
_add(list, unique, unpack(arg))
end
end
end
-- options: once, memory, unique, stopOnFalse
local function Callbacks(...)
local opt = {...}
if opt[1] then
for i = 1, #opt do opt[opt[i]] = true end
end
local this, list, stack = {}, {}, (not opt.once) and {} or nil
local firing, fired, firingLength, firingStart, firingIndex, memory
local function fire(...)
memory = opt.memory and {...} or nil
fired, firing, firingIndex = true, true, firingStart or 1
firingStart, firingLength = 1, #list
while list and (firingIndex <= firingLength) do
local ret = list[firingIndex](...)
if opt.stopOnFalse and (ret == false) then
memory = nil
break
end
firingIndex = firingIndex + 1
end
firing = false
if list then
if stack then
if #stack > 0 then
fire(tremove(stack, 1))
end
elseif memory then
list = {}
else
this:disable()
end
end
end
function this:add(...)
if list then
local start = #list + 1
_add(list, opt.unique, ...)
if firing then
firingLength = #list
elseif memory then
firingStart = start
fire(unpack(memory))
end
end
return self
end
function this:remove(...)
if list then
for _, arg in ipairs{...} do
local inList, index = _has(list, arg)
while inList do
tremove(list, index)
if firing then
if index <= firingLength then
firingLength = firingLength - 1
end
if index <= firingIndex then
firingIndex = firingIndex - 1
end
end
inList, index = _has(list, arg)
end
end
end
return self
end
function this:fire(...)
if list and ((not fired) or stack) then
if firing then
tinsert(stack, {...})
else
fire(...)
end
end
return self
end
function this:fired() return fired end
function this:empty()
list = {}
firingLength = 0
return self
end
function this:has(fn)
return fn and _has(list, fn) or (list and #list > 0)
end
function this:disable()
list, stack, memory = nil
return self
end
function this:disabled() return not list end
function this:lock()
stack = nil
if not memory then self:disable() end
return self
end
function this:locked() return not stack end
return this
end
-- global exports
_G.Callbacks = Callbacks
--# Deferred
-- Deferred
-- Ported from jQuery.Deferred (v1.8).
--
-- usage is identical with 3 exceptions:
--
-- 1: Deferred.then() is Deferred.pipe() in Lua due to 'then' being
-- a reserved Lua keyword. The functionality is identical to Deferred.then()
-- as of jQuery 1.8.
--
-- 2: There is no resolveWith/rejectWith/progressWith, because Lua's functions
-- are not "objects" in the way that JavaScript's functions are, and
-- cannot have an instance bound to them (i.e. in Lua we must explicitly bind
-- the object to the method at calling time via obj:func() or obj.func(obj), or
-- write a closure/"thunk", like bind() or caller() )
local pairs, ipairs = pairs, ipairs
local type = type
local unpack = unpack
local select = select
local function _extend(target, ...)
for _, t in ipairs{...} do
for k, v in pairs(t) do
target[k] = v
end
end
return target
end
local _methods = {
{'resolve', 'done'},
{'reject', 'fail'},
{'progress', 'notify'}
}
-- forward declaration for _deferred()'s "self" check
local function _deferred(func)
local _state, deferred, promise = 'pending', {}, {}
local _done = Callbacks('once', 'memory')
local _fail = Callbacks('once', 'memory')
local _progress = Callbacks('memory')
_done:add(function()
_state = 'resolved'
_fail:disable()
_progress:lock()
end)
_fail:add(function()
_state = 'rejected'
_done:disable()
_progress:lock()
end)
function promise:state() return _state end
function promise:always(...)
deferred:done(...):fail(...)
return self
end
--params: fnDone, fnFail, fnProgress
function promise:pipe(...)
local fns = {...}
return Deferred(function(newDefer)
for i, method in ipairs(_methods) do
local fn = (type(fns[i]) == 'function') and fns[i]
deferred[method[2]](deferred, function(...)
local returned = fn and fn(...)
if returned and type(returned.promise) == 'function' then
returned:promise()
:done(function(...) newDefer:resolve(...) end)
:fail(function(...) newDefer:reject(...) end)
:progress(function(...) newDefer:notify(...) end)
else
newDefer[method[1]](newDefer, ...)
end
end)
end
fns = nil
end):promise()
end
function promise:promise(obj)
return (obj) and _extend(obj, promise) or promise
end
function promise:done(...)
_done:add(...)
return self
end
function promise:fail(...)
_fail:add(...)
return self
end
function promise:progress(...)
_progress:add(...)
return self
end
function deferred:resolve(...)
_done:fire(...)
return self
end
function deferred:reject(...)
_fail:fire(...)
return self
end
function deferred:notify(...)
_progress:fire(...)
return self
end
promise:promise(deferred)
if func then func(deferred) end
return deferred
end
local function when(...)
local resolveValues = {...}
local length, first = #resolveValues, resolveValues[1]
local remaining = length
if length == 1 and type(first.promise) ~= 'function' then
remaining = 0
end
local resolveContexts, progressValues, progressContexts
local deferred = (remaining == 1) and first or _deferred()
local function updateFunc(inst, i, contexts, values)
return function(...)
contexts[i] = inst
values[i] = (select('#', ...) > 1) and {...} or select(1, ...)
if values == progressValues then
deferred:notify(unpack(progressValues))
else
remaining = remaining - 1
if remaining == 0 then
deferred:resolve(unpack(resolveValues))
end
end
end
end
if length > 1 then
progressValues, progressContexts, resolveContexts = {}, {}, {}
for i = 1, length do
local rv = resolveValues[i]
if rv and type(rv.promise) == 'function' then
rv:promise()
:progress(updateFunc(rv, i, progressContexts, progressValues))
:done(updateFunc(rv, i, resolveContexts, resolveValues))
:fail(function(...) deferred:reject(...) end)
else
remaining = remaining - 1
end
end
end
if remaining == 0 then
deferred:resolve(unpack(resolveValues))
end
return deferred:promise()
end
-- global exports
_G.Deferred = _deferred
_G.when = when
--# plistParse
-- plistParser
-- version 1.01
--
-- based on an XML parser by Roberto Ierusalimschy at:
-- lua-users.org/wiki/LuaXml
--
-- Takes a string-ified .plist file as input, and outputs
-- a table. Nested dictionaries and arrays are parsed into
-- subtables. Table structure will match the structure of
-- the .plist file
--
-- usage:
-- local plistStr = <string-ified plist file>
-- local plistTable = plistParse(plistStr)
--
local plp = {}
function plp.nextTag(s, i)
return string.find(s, "<(%/?)([%w:]+)(%/?)>", i)
end
function plp.array(s, i)
local arr, nextTag, array, dictionary = {}, plp.nextTag, plp.array, plp.dictionary
local ni, j, c, label, empty
while true do
ni, j, c, label, empty = nextTag(s, i)
assert(ni)
if c == "" then
if empty == "/" then
if label == "dict" or label == "array" then
arr[#arr+1] = {}
else
arr[#arr+1] = (label == "true") and true or false
end
elseif label == "array" then
arr[#arr+1], i, j = array(s, j+1)
elseif label == "dict" then
arr[#arr+1], i, j = dictionary(s, j+1)
else
i = j + 1
ni, j, c, label, empty = nextTag(s, i)
local val = string.sub(s, i, ni-1)
if label == "integer" or label == "real" then
arr[#arr+1] = tonumber(val)
else
arr[#arr+1] = val
end
end
elseif c == "/" then
assert(label == "array")
return arr, j+1, j
end
i = j + 1
end
end
function plp.dictionary(s, i)
local dict, nextTag, array, dictionary = {}, plp.nextTag, plp.array, plp.dictionary
local ni, j, c, label, empty
while true do
ni, j, c, label, empty = nextTag(s, i)
assert(ni)
if c == "" then
if label == "key" then
i = j + 1
ni, j, c, label, empty = nextTag(s, i)
assert(c == "/" and label == "key")
local key = string.sub(s, i, ni-1)
i = j + 1
ni, j, c, label, empty = nextTag(s, i)
if empty == "/" then
if label == "dict" or label == "array" then
dict[key] = {}
else
dict[key] = (label == "true") and true or false
end
else
if label == "dict" then
dict[key], i, j = dictionary(s, j+1)
elseif label == "array" then
dict[key], i, j = array(s, j+1)
else
i = j + 1
ni, j, c, label, empty = nextTag(s, i)
local val = string.sub(s, i, ni-1)
if label == "integer" or label == "real" then
dict[key] = tonumber(val)
else
dict[key] = val
end
end
end
end
elseif c == "/" then
assert(label == "dict")
return dict, j+1, j
end
i = j + 1
end
end
function plistParse(s)
local i, ni, tag, version, empty = 0
while label ~= "plist" do
ni, i, label, version = string.find(s, "<([%w:]+)(.-)>", i+1)
assert(ni)
end
ni, i, _, label, empty = plp.nextTag(s, i)
if empty == "/" then
return {}
elseif label == "dict" then
return plp.dictionary(s, i+1)
elseif label == "array" then
return plp.array(s, i+1)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment