Skip to content

Commit

Permalink
initial
Browse files Browse the repository at this point in the history
  • Loading branch information
niquola committed Jul 14, 2015
0 parents commit 7bb3241
Show file tree
Hide file tree
Showing 15 changed files with 360 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*sw?
node_modules
dist
lib
npm-debug.log
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
sample

```sh
nvm install 0.12
nvm use 0.12
npm install
source config.sh && npm start
```
## PLPL
5 changes: 5 additions & 0 deletions bin/plpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env coffee
path = require('path')
fs = require('fs')
lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib')
require(lib + '/cli.js').run()
30 changes: 30 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "plpl",
"description": "node + plv8 + pg",
"version": "0.0.1",
"dependencies": {
"cli": "latest",
"cli-table": "latest"
},
"repository": {
"type": "git",
"url": "https://github.com/niquola/plpl"
},
"devDependencies": {
"coffee": "latest",
"jasmine-node": "latest",
"pg-native":"latest"
},
"author": "niquola",
"engines": { "node": ">=0.10" },
"homepage": "https://github.com/niquola/plpl",
"files": ["bin", "lib", "src"],
"scripts": {
"publish": "coffee -o lib/ -c src/ && npm publish",
"watch": "webpack --progress --colors --watch",
"build": "`npm bin`/coffee --output lib --compile src && ls lib -R",
"test": "",
"test-watch": "",
"watch": "coffee -w --output lib --compile src"
}
}
2 changes: 2 additions & 0 deletions sample/env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export DATABASE_URL=postgres://localhost:5432/test
export MIGRATIONS_DIR=./migrations
7 changes: 7 additions & 0 deletions sample/migrations/2015-07-14T21:24:47.026Z__init.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
exports.up = (plv8)->
# plv8.execute ""
throw new Error('Not implemented')

exports.down = (plv8)->
# plv8.execute ""
throw new Error('Not implemented')
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
exports.up = (plv8)->
# plv8.execute ""
throw new Error('Not implemented')

exports.down = (plv8)->
# plv8.execute ""
throw new Error('Not implemented')
Empty file added sample/package.json
Empty file.
13 changes: 13 additions & 0 deletions scratch.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
plv8 = require('./src/plv8')
loader = require('./src/loader')

console.log plv8.execute 'select 1'

loader.scan('./scratch_code.coffee')

mod = require('./scratch_code.coffee')

mod.generate_table(plv8, 'users')

console.log plv8.execute 'select plv8_add(1, 3)'
console.log plv8.execute 'select generate_table($1)', ['musers']
19 changes: 19 additions & 0 deletions scratch_code.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
plv8_add = (a, b)-> a + b

module.exports.plv8_add = plv8_add
plv8_add.plv8 = 'plv8_add(a int, b int) RETURNS int'

generate_table = (plv8, nm)->
tbl = plv8.quote_ident(nm)
plv8.execute """
DROP TABLE IF EXISTS #{tbl};
CREATE TABLE #{tbl} (
id serial PRIMARY KEY,
data jsonb
);
"""
plv8.elog(INFO, "table #{tbl} generated")
"table #{tbl} generated"

module.exports.generate_table = generate_table
generate_table.plv8 = 'generate_table(nm text) RETURNS text'
57 changes: 57 additions & 0 deletions src/cli.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
mig = require './migrations'
load = require './loader'

migrate = (args)->
mig.up()

generate_migration = (args)->
mig.generate(args[0])


reload = (args)->

commands =
reload:
default:
fn: reload
args: ''
desc: 'reload procedures'
watch:
fn: reload
args: ''
desc: 'watch src directory and update procs'
migrate:
default:
fn: migrate
args: ''
desc: 'migrate up'
new:
fn: generate_migration
args: '<migration_name>'
desc: 'generate new migration'

print_usage = (commands)->
for k,v of commands
for sk, vv of v
sk = '' if sk == 'default'
console.log "\nplpl #{k} #{sk} #{vv.args}"
console.log " #{vv.desc}"
console.log ""

exports.run = ()->
cmd_nm = process.argv[2]
if not cmd_nm
print_usage(commands)

subcmd_nm = process.argv[3] || 'default'
args = process.argv[4..-1]

cmd = commands[cmd_nm]
unless cmd
console.log("Unknown command #{cmd_nm}")
return
subcmd = cmd[subcmd_nm]
unless subcmd
console.log("Unknown sub-command #{subcmd_nm}")
return
subcmd.fn(args)
77 changes: 77 additions & 0 deletions src/loader.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require('coffee-script/register')
path = require('path')
plv8 = require('./plv8')
Module = require("module")

currentModule = null
modules_idx = {}
plv8_exports = {}

oldrequire = Module::require

Module::require = (fl) ->
currentModule = fl
oldrequire.apply this, arguments

oldcompile = Module::_compile

Module::_compile = (answer, filename) ->
modules_idx[currentModule] ={ filename: filename, code: answer}
res = oldcompile.apply(this, arguments)
for k,v of @exports when v.plv8?
plv8_exports[k] ={fn: v, filename: filename}
res

_isAbsolute = (pth)->
path.resolve(pth) == path.normalize(pth)

scan = (pth) ->
unless _isAbsolute(pth)
pth = path.normalize(path.join(path.dirname(module.parent.filename), pth))

currentModule = null
Module._cache = {}
modules_idx = {}
plv8_exports = {}

delete require.cache

#TODO: calculate path from calling module
file = require(pth)

modules_js = generate_modules(modules_idx)
plv8.execute "CREATE EXTENSION IF NOT EXISTS plv8"
for k,v of plv8_exports
sql = generate_plv8_fn(pth, k, modules_js, v.fn)
console.log('-- Load ', v.fn.plv8)
#console.log(sql)
plv8.execute(sql)

generate_modules = (modules_idx)->
mods = []
for m,v of modules_idx
console.log("dep: #{m}")
mods.push "deps['#{m}'] = function(module, exports, require){#{v.code}};"
mods.join("\n")

generate_plv8_fn = (mod, k, modules_js, fn)->
def_fn = fn.plv8
def_call = fn.toString().split("{")[0].split("function")[1].trim()

"""
CREATE OR REPLACE FUNCTION #{def_fn} AS $$
var deps = {}
var cache = {}
#{modules_js}
var require = function(dep){
if(!cache[dep]) {
var module = {exports: {}};
deps[dep](module, module.exports, require);
cache[dep] = module.exports;
}
return cache[dep]
}
return require('#{mod}').#{k}#{def_call};
$$ LANGUAGE plv8 IMMUTABLE STRICT;
"""
exports.scan = scan
99 changes: 99 additions & 0 deletions src/migrations.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
plv8 = require('./plv8')
fs = require('fs')

MIGRATION_REG = /^\d.*__.*/
parse_name = (x)->
parts = x.split('__')
{ts: parts[0].replace(/_/g,' '), name: parts[1].split('.')[0]}

ensure_migrations_table = ()->
unless plv8.execute("select to_regclass('public._migrations')")[0]
plv8.execute """
create table if not exists _migrations (
name text PRIMARY KEY,
ts timestamp
)
"""

past_migrations = ()->
res = plv8.execute('SELECT * from _migrations')
res.reduce ((a,x)-> a[x.name] = x; a), {}

existing_migrations = (dir)->
files = fs.readdirSync(dir)
for x in files.sort() when x.match(MIGRATION_REG)
m = parse_name(x)
m.file = "#{dir}/#{x}"
m

save_migration = (m)->
plv8.execute 'INSERT INTO _migrations (name, ts) VALUES ($1,$2)', [m.name, m.ts]

rm_migration = (m)->
plv8.execute 'DELETE FROM _migrations WHERE name = $1', [m.name]

clear_migrations = ()->
plv8.execute 'TRUNCATE _migrations'

migrate_up = (m)->
mod = require(m.file)
console.log("migrating #{m.ts} #{m.name}...")
if mod.up
mod.up(plv8)
else
throw new Error("No exports.up for #{m.file}")
save_migration(m)

migrate_down = (m)->
mod = require(m.file)
console.log("rolling back #{m.ts} #{m.name}...")
if mod.down
mod.down(plv8)
else
throw new Error("No exports.down for #{m.file}")
save_migration(m)

pending = (dir)->
past = past_migrations()
existing_migrations(dir).filter (x)->
not past[x.name]

ensure_migrations_dir = (dir)->
dir = dir || process.env.MIGRATIONS_DIR
unless dir
throw new Error("Please export MIGRATIONS_DIR=???")
dir

up = (dir)->
dir = ensure_migrations_dir(dir)
ensure_migrations_table()
pnd = pending(dir)
for m in pending(dir)
migrate_up(m)
if pnd.length == 0
console.log "No pending migrations"
else
console.log "All migrations done!"

spit = (pth, cnt)->
fd = fs.openSync(pth, 'a')
fs.writeSync(fd, cnt)
fs.closeSync(fd)

generate = (name, dir)->
unless name
throw new Error('name is required')
dir = ensure_migrations_dir(dir)
nm = "#{(new Date()).toISOString()}__#{name}.coffee"
spit "#{dir}/#{nm}", """
exports.up = (plv8)->
# plv8.execute ""
throw new Error('Not implemented')
exports.down = (plv8)->
# plv8.execute ""
throw new Error('Not implemented')
"""

module.exports.up = up
module.exports.generate = generate
29 changes: 29 additions & 0 deletions src/plv8.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Client = require('pg-native')
global.INFO="INFO"
global.ERROR="ERROR"
global.DEBUG="DEBUG"

conn_string = process.env.DATABASE_URL

unless conn_string
throw new Error("set connection string \n export DATABASE_URL=postgres://user:password@localhost:5432/test")

client = new Client
client.connectSync(conn_string)
module.exports =
execute: ->
client.querySync.apply(client, arguments).map (x) ->
obj = {}
for k of x
if typeof x[k] == 'object'
obj[k] = JSON.stringify(x[k])
else
obj[k] = x[k]
obj
elog: (x, msg) ->
console.log "#{x}:", msg
return

quote_literal: (str)-> str && client.pq.escapeLiteral(str)
nullable: (str)->
quote_ident: (str)-> str && client.pq.escapeIdentifier(str)

0 comments on commit 7bb3241

Please sign in to comment.