Merge commit '656622cc88bbd5e30bff3980611b9e7c26059719' as 'locator'
This commit is contained in:
commit
c3a14a6392
3
locator/.gitignore
vendored
Normal file
3
locator/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
*.lua
|
||||||
|
!pull.lua
|
||||||
|
!add.lua
|
21
locator/LICENSE
Normal file
21
locator/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Paul Liverman III
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
104
locator/ReadMe.md
Normal file
104
locator/ReadMe.md
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# locator
|
||||||
|
|
||||||
|
A <strike>service</strike> module locator for use with Lapis.
|
||||||
|
(Which doesn't use anythng Lapis-specific, so you could use it elsewhere.)
|
||||||
|
|
||||||
|
Installation:
|
||||||
|
|
||||||
|
1. subtree this project into the root of your project.
|
||||||
|
2. `echo 'return require((...) .. ".init")' > locator.lua` (and make sure it is
|
||||||
|
committed!)
|
||||||
|
3. Make `locator_config.moon` using the configuration format specified below.
|
||||||
|
4. `echo 'return require("locator").models' > models.moon` (to replace the
|
||||||
|
models autoloader provided with Lapis with our autoloader)
|
||||||
|
|
||||||
|
## Usages
|
||||||
|
|
||||||
|
1. `autoload`: A function to allow automatically loading modules when accessing
|
||||||
|
a table.
|
||||||
|
2. Implicit autoloaders: The module itself is an autoloader, and can be called
|
||||||
|
to create a specialized autoloader that checks directories local to where
|
||||||
|
your code is before other paths.
|
||||||
|
3. `make_migrations`: A function to make integrating migrations tables from
|
||||||
|
multiple Lapis projects into one to streamline applying migrations.
|
||||||
|
|
||||||
|
### `autoload` function
|
||||||
|
|
||||||
|
The core functionality is based on the `autoload` function, which returns a
|
||||||
|
table with `__call` and `__index` metamethods, allowing things like:
|
||||||
|
|
||||||
|
```
|
||||||
|
locator = require "locator"
|
||||||
|
|
||||||
|
-- the module itself is an autoloader, so you can grab anything in a
|
||||||
|
-- sub-directory like so:
|
||||||
|
import Users, Posts from locator.models -- looks in 'models' directory first
|
||||||
|
|
||||||
|
-- you can also use it (locator.models) as a function to require from models
|
||||||
|
Model = locator.models "Model" -- will look for 'models.Model' (& config paths)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can create autoloaders yourself: `models = locator.autoload "models"`
|
||||||
|
|
||||||
|
### Implicit autoloaders
|
||||||
|
|
||||||
|
In sub-applications, make requiring things easier by acting like everything is
|
||||||
|
in your repository's root:
|
||||||
|
|
||||||
|
```
|
||||||
|
-- returns an autoloader that will try to require from wherever your
|
||||||
|
-- sub-application is installed before other paths
|
||||||
|
locate = require("locator")(...)
|
||||||
|
|
||||||
|
util = locate "util" -- will grab *our* utility functions
|
||||||
|
```
|
||||||
|
|
||||||
|
Grab a table of all views: `views = locator.views` (will create an autoloader
|
||||||
|
when you try to access it)
|
||||||
|
|
||||||
|
### `make_migrations` function
|
||||||
|
|
||||||
|
In your migrations, do something like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
import make_migrations from require "locator"
|
||||||
|
import create_table, types from require "lapis.db.schema"
|
||||||
|
|
||||||
|
return make_migrations {
|
||||||
|
[1518418142]: =>
|
||||||
|
create_table "articles", {
|
||||||
|
{"id", types.serial primary_key: true}
|
||||||
|
{"title", types.text unique: true}
|
||||||
|
{"content", types.text}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Whatever migrations you have defined will be combined with migrations specified
|
||||||
|
in sub-applications added to `locator_config`, subject to conditions in the
|
||||||
|
config file.
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
{
|
||||||
|
path: "applications.users" -- there is a sub-application at this location
|
||||||
|
migrations: {after: 1518414112} -- don't run this migration or any before it
|
||||||
|
}
|
||||||
|
{
|
||||||
|
path: "utility" -- another sub-application is here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The only required value per item in the configuration is `path`, formatted with
|
||||||
|
periods as a directory-separator. Currently, the only point of specifying a
|
||||||
|
migrations table is to specify a migration ID to ignore it and every migration
|
||||||
|
before it within that sub-application.
|
||||||
|
|
||||||
|
Without the configuration file, locator will crash with an error, and without
|
||||||
|
a path specified, locator doesn't know where else to look for files to require,
|
||||||
|
so it is very essential.
|
45
locator/add.lua
Normal file
45
locator/add.lua
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
local config = require("locator_config")
|
||||||
|
local execute
|
||||||
|
execute = function(cmd, capture_exit_code)
|
||||||
|
if capture_exit_code == nil then
|
||||||
|
capture_exit_code = true
|
||||||
|
end
|
||||||
|
local handle
|
||||||
|
if capture_exit_code then
|
||||||
|
handle = io.popen(tostring(cmd) .. "\necho $?")
|
||||||
|
else
|
||||||
|
handle = io.popen(cmd)
|
||||||
|
end
|
||||||
|
local result = handle:read("*a")
|
||||||
|
handle:close()
|
||||||
|
local exit_start, exit_end = result:find("(%d*)[%c]$")
|
||||||
|
local exit_code = tonumber(result:sub(exit_start, exit_end):sub(1, -2))
|
||||||
|
local output = result:sub(1, exit_start - 1)
|
||||||
|
if exit_code == 0 then
|
||||||
|
return output
|
||||||
|
else
|
||||||
|
return error("sub-process '" .. tostring(cmd) .. "' returned status " .. tostring(exit_code) .. ".\n\n" .. tostring(output))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local list = execute("git remote")
|
||||||
|
local remotes = { }
|
||||||
|
for line in list:gmatch("[^\n]+") do
|
||||||
|
remotes[line] = true
|
||||||
|
end
|
||||||
|
for _index_0 = 1, #config do
|
||||||
|
local item = config[_index_0]
|
||||||
|
if item.remote and item.remote.fetch then
|
||||||
|
if not (item.remote.branch) then
|
||||||
|
item.remote.branch = "master"
|
||||||
|
end
|
||||||
|
if item.remote.name then
|
||||||
|
if not (remotes[item.remote.name]) then
|
||||||
|
execute("git remote add -f " .. tostring(item.remote.name) .. " " .. tostring(item.remote.fetch))
|
||||||
|
if item.remote.push and not ("boolean" == type(item.remote.push)) then
|
||||||
|
execute("git remote set-url --push " .. tostring(item.remote.name) .. " " .. tostring(item.remote.push))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
execute("git subtree add --prefix " .. tostring(item.path:gsub("%.", "/")) .. " " .. tostring(item.remote.fetch) .. " " .. tostring(item.remote.branch) .. " --squash")
|
||||||
|
end
|
||||||
|
end
|
38
locator/add.moon
Normal file
38
locator/add.moon
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
config = require "locator_config"
|
||||||
|
|
||||||
|
execute = (cmd, capture_exit_code=true) ->
|
||||||
|
local handle
|
||||||
|
if capture_exit_code
|
||||||
|
handle = io.popen "#{cmd}\necho $?"
|
||||||
|
else
|
||||||
|
handle = io.popen cmd
|
||||||
|
result = handle\read "*a"
|
||||||
|
handle\close!
|
||||||
|
|
||||||
|
exit_start, exit_end = result\find "(%d*)[%c]$"
|
||||||
|
exit_code = tonumber result\sub(exit_start, exit_end)\sub 1, -2
|
||||||
|
output = result\sub 1, exit_start - 1
|
||||||
|
|
||||||
|
if exit_code == 0
|
||||||
|
return output
|
||||||
|
else
|
||||||
|
error "sub-process '#{cmd}' returned status #{exit_code}.\n\n#{output}"
|
||||||
|
|
||||||
|
list = execute "git remote"
|
||||||
|
remotes = {}
|
||||||
|
for line in list\gmatch "[^\n]+"
|
||||||
|
remotes[line] = true
|
||||||
|
|
||||||
|
for item in *config
|
||||||
|
if item.remote and item.remote.fetch -- if configured to pull
|
||||||
|
unless item.remote.branch
|
||||||
|
item.remote.branch = "master"
|
||||||
|
|
||||||
|
if item.remote.name -- if we want a named remote
|
||||||
|
unless remotes[item.remote.name] -- add it if needed
|
||||||
|
execute "git remote add -f #{item.remote.name} #{item.remote.fetch}"
|
||||||
|
if item.remote.push and not ("boolean" == type item.remote.push)
|
||||||
|
execute "git remote set-url --push #{item.remote.name} #{item.remote.push}"
|
||||||
|
|
||||||
|
-- we actually ignore names with in-script usage..
|
||||||
|
execute "git subtree add --prefix #{item.path\gsub "%.", "/"} #{item.remote.fetch} #{item.remote.branch} --squash"
|
9
locator/example.locator_config.moon
Normal file
9
locator/example.locator_config.moon
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
{
|
||||||
|
path: "applications.users" -- there is a sub-application at this location
|
||||||
|
migrations: {after: 1518414112} -- don't run this migration or any before it
|
||||||
|
}
|
||||||
|
{
|
||||||
|
path: "utility" -- another sub-application is here
|
||||||
|
}
|
||||||
|
}
|
120
locator/init.moon
Normal file
120
locator/init.moon
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
config = require "locator_config" -- TODO combine w values from Lapis's config, those overwriting these (this will be a legacy option)
|
||||||
|
|
||||||
|
import insert, sort from table
|
||||||
|
|
||||||
|
-- require, but only errors when a module errors during loading
|
||||||
|
check_require = (path) ->
|
||||||
|
ok, value = pcall -> require path
|
||||||
|
if ok or ("string" == type(value) and value\find "module '#{path}' not found")
|
||||||
|
return ok, value
|
||||||
|
else
|
||||||
|
error value
|
||||||
|
|
||||||
|
-- locates and returns a module
|
||||||
|
-- if a path is specified, it will be checked before other paths
|
||||||
|
-- checks the project root, then each path specified in locator_config
|
||||||
|
locate = (name, path) ->
|
||||||
|
print "locate ->"
|
||||||
|
if path
|
||||||
|
print " try '#{path}.#{name}'"
|
||||||
|
ok, value = check_require "#{path}.#{name}"
|
||||||
|
return value if ok
|
||||||
|
|
||||||
|
print " try '#{name}'"
|
||||||
|
ok, value = check_require name
|
||||||
|
return value if ok
|
||||||
|
|
||||||
|
for item in *config
|
||||||
|
if path
|
||||||
|
print " try '#{item.path}.#{path}.#{name}'"
|
||||||
|
ok, value = check_require "#{item.path}.#{path}.#{name}"
|
||||||
|
else
|
||||||
|
print " try '#{item.path}.#{name}'"
|
||||||
|
ok, value = check_require "#{item.path}.#{name}"
|
||||||
|
return value if ok
|
||||||
|
|
||||||
|
if path
|
||||||
|
error "locator could not find '#{path}.#{name}'"
|
||||||
|
else
|
||||||
|
error "locator could not find '#{name}'"
|
||||||
|
|
||||||
|
-- works like Lapis's autoload, but
|
||||||
|
-- includes trying sub-application paths & can be called to access a value
|
||||||
|
autoload = (path, tab={}) ->
|
||||||
|
return setmetatable tab, {
|
||||||
|
__call: (t, name) ->
|
||||||
|
t[name] = locate name, path
|
||||||
|
return t[name]
|
||||||
|
__index: (t, name) ->
|
||||||
|
t[name] = locate name, path
|
||||||
|
return t[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
-- pass your migrations, it returns them + all sub-application migrations
|
||||||
|
-- (legacy) see example config for how to specify to not include early migrations
|
||||||
|
make_migrations = (app_migrations={}) ->
|
||||||
|
for item in *config
|
||||||
|
ok, migrations = check_require "#{item.path}.migrations"
|
||||||
|
if ok
|
||||||
|
sorted = {}
|
||||||
|
for m in pairs migrations
|
||||||
|
insert sorted, m
|
||||||
|
sort sorted
|
||||||
|
for i in *sorted
|
||||||
|
-- only allow migrations after specified config value, or if no 'after' is specified
|
||||||
|
if (item.migrations and ((item.migrations.after and i > item.migrations.after) or not item.migrations.after)) or not item.migrations
|
||||||
|
-- if your migrations and theirs share a value, combine them
|
||||||
|
if app_fn = app_migrations[i]
|
||||||
|
app_migrations[i] = (...) ->
|
||||||
|
app_fn(...)
|
||||||
|
migrations[i](...)
|
||||||
|
-- else just add them
|
||||||
|
else
|
||||||
|
app_migrations[i] = migrations[i]
|
||||||
|
|
||||||
|
return app_migrations
|
||||||
|
|
||||||
|
-- sub-applications can define custom functions in a `locator_config` file in
|
||||||
|
-- their root directory. These functions are aggregated by name and called in
|
||||||
|
-- the order defined by the paths in the root locator_config (with the root
|
||||||
|
-- being called first)
|
||||||
|
registry = setmetatable {}, {
|
||||||
|
__index: (t, name) ->
|
||||||
|
registered_functions = {}
|
||||||
|
|
||||||
|
if config[name]
|
||||||
|
insert registered_functions, config[name]
|
||||||
|
|
||||||
|
for item in *config
|
||||||
|
ok, register = check_require "#{item.path}.locator_config"
|
||||||
|
if ok and register[name]
|
||||||
|
insert registered_functions, register[name]
|
||||||
|
|
||||||
|
if #registered_functions > 0
|
||||||
|
t[name] = (...) ->
|
||||||
|
for i=1, #registered_functions-1
|
||||||
|
registered_functions[i](...)
|
||||||
|
return registered_functions[#registered_functions](...)
|
||||||
|
else
|
||||||
|
t[name] = ->
|
||||||
|
|
||||||
|
return t[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
-- public interface:
|
||||||
|
-- functions: autoload, make_migrations
|
||||||
|
-- tables: locate (locator alias), registry
|
||||||
|
return setmetatable {
|
||||||
|
:locate, :autoload, :make_migrations, :registry
|
||||||
|
}, {
|
||||||
|
__call: (t, here="") ->
|
||||||
|
if "init" == here\sub -4
|
||||||
|
here = here\sub 1, -6
|
||||||
|
unless here\len! > 0
|
||||||
|
here = ""
|
||||||
|
return autoload here
|
||||||
|
|
||||||
|
__index: (t, name) ->
|
||||||
|
t[name] = autoload name
|
||||||
|
return t[name]
|
||||||
|
}
|
45
locator/pull.lua
Normal file
45
locator/pull.lua
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
local config = require("locator_config")
|
||||||
|
local execute
|
||||||
|
execute = function(cmd, capture_exit_code)
|
||||||
|
if capture_exit_code == nil then
|
||||||
|
capture_exit_code = true
|
||||||
|
end
|
||||||
|
local handle
|
||||||
|
if capture_exit_code then
|
||||||
|
handle = io.popen(tostring(cmd) .. "\necho $?")
|
||||||
|
else
|
||||||
|
handle = io.popen(cmd)
|
||||||
|
end
|
||||||
|
local result = handle:read("*a")
|
||||||
|
handle:close()
|
||||||
|
local exit_start, exit_end = result:find("(%d*)[%c]$")
|
||||||
|
local exit_code = tonumber(result:sub(exit_start, exit_end):sub(1, -2))
|
||||||
|
local output = result:sub(1, exit_start - 1)
|
||||||
|
if exit_code == 0 then
|
||||||
|
return output
|
||||||
|
else
|
||||||
|
return error("sub-process '" .. tostring(cmd) .. "' returned status " .. tostring(exit_code) .. ".\n\n" .. tostring(output))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local list = execute("git remote")
|
||||||
|
local remotes = { }
|
||||||
|
for line in list:gmatch("[^\n]+") do
|
||||||
|
remotes[line] = true
|
||||||
|
end
|
||||||
|
for _index_0 = 1, #config do
|
||||||
|
local item = config[_index_0]
|
||||||
|
if item.remote and item.remote.fetch then
|
||||||
|
if not (item.remote.branch) then
|
||||||
|
item.remote.branch = "master"
|
||||||
|
end
|
||||||
|
if item.remote.name then
|
||||||
|
if not (remotes[item.remote.name]) then
|
||||||
|
execute("git remote add -f " .. tostring(item.remote.name) .. " " .. tostring(item.remote.fetch))
|
||||||
|
if item.remote.push and not ("boolean" == type(item.remote.push)) then
|
||||||
|
execute("git remote set-url --push " .. tostring(item.remote.name) .. " " .. tostring(item.remote.push))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
execute("git subtree pull --prefix " .. tostring(item.path:gsub("%.", "/")) .. " " .. tostring(item.remote.fetch) .. " " .. tostring(item.remote.branch) .. " --squash")
|
||||||
|
end
|
||||||
|
end
|
38
locator/pull.moon
Normal file
38
locator/pull.moon
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
config = require "locator_config"
|
||||||
|
|
||||||
|
execute = (cmd, capture_exit_code=true) ->
|
||||||
|
local handle
|
||||||
|
if capture_exit_code
|
||||||
|
handle = io.popen "#{cmd}\necho $?"
|
||||||
|
else
|
||||||
|
handle = io.popen cmd
|
||||||
|
result = handle\read "*a"
|
||||||
|
handle\close!
|
||||||
|
|
||||||
|
exit_start, exit_end = result\find "(%d*)[%c]$"
|
||||||
|
exit_code = tonumber result\sub(exit_start, exit_end)\sub 1, -2
|
||||||
|
output = result\sub 1, exit_start - 1
|
||||||
|
|
||||||
|
if exit_code == 0
|
||||||
|
return output
|
||||||
|
else
|
||||||
|
error "sub-process '#{cmd}' returned status #{exit_code}.\n\n#{output}"
|
||||||
|
|
||||||
|
list = execute "git remote"
|
||||||
|
remotes = {}
|
||||||
|
for line in list\gmatch "[^\n]+"
|
||||||
|
remotes[line] = true
|
||||||
|
|
||||||
|
for item in *config
|
||||||
|
if item.remote and item.remote.fetch -- if configured to pull
|
||||||
|
unless item.remote.branch
|
||||||
|
item.remote.branch = "master"
|
||||||
|
|
||||||
|
if item.remote.name -- if we want a named remote
|
||||||
|
unless remotes[item.remote.name] -- add it if needed
|
||||||
|
execute "git remote add -f #{item.remote.name} #{item.remote.fetch}"
|
||||||
|
if item.remote.push and not ("boolean" == type item.remote.push)
|
||||||
|
execute "git remote set-url --push #{item.remote.name} #{item.remote.push}"
|
||||||
|
|
||||||
|
-- we actually ignore names with in-script usage..
|
||||||
|
execute "git subtree pull --prefix #{item.path\gsub "%.", "/"} #{item.remote.fetch} #{item.remote.branch} --squash"
|
Loading…
Reference in New Issue
Block a user