summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorazul <azul@riseup.net>2013-12-22 06:33:56 -0800
committerazul <azul@riseup.net>2013-12-22 06:33:56 -0800
commit12dbeefe693e5432416ec8019368e591c526736b (patch)
tree8b93ddc237aef26f4a68557259f5f63250b1a694
parent41dee6ee7242a731763bb1b75e12cc97403aa4a4 (diff)
parentc1e6eea1eb2a9fbdb92e806929806acef32ed8dc (diff)
Merge pull request #6 from azul/deploy-design-docs
Deploy design docs
-rwxr-xr-xbin/tapicero5
-rw-r--r--designs/docs.json12
-rw-r--r--designs/syncs.json11
-rw-r--r--designs/transactions.json12
-rw-r--r--lib/tapicero.rb2
-rw-r--r--lib/tapicero/user_database.rb20
-rw-r--r--lib/tapicero/version.rb2
-rw-r--r--lib/tapicero_daemon.rb1
8 files changed, 61 insertions, 4 deletions
diff --git a/bin/tapicero b/bin/tapicero
index 9a5a7ee..b8b88f1 100755
--- a/bin/tapicero
+++ b/bin/tapicero
@@ -1,10 +1,11 @@
#!/usr/bin/ruby
+require 'pathname'
#
# Tapicero Daemon
#
-BASE_DIR = File.expand_path('../..', File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__)
+BASE_DIR = Pathname.new(__FILE__).realpath + '../..'
begin
#
@@ -17,7 +18,7 @@ rescue LoadError
#
require "#{BASE_DIR}/lib/tapicero/version.rb"
Tapicero::REQUIRE_PATHS.each do |path|
- path = File.expand_path(path, BASE_DIR)
+ path = BASE_DIR + path
$LOAD_PATH.unshift path unless $LOAD_PATH.include?(path)
end
require 'rubygems'
diff --git a/designs/docs.json b/designs/docs.json
new file mode 100644
index 0000000..4aad02a
--- /dev/null
+++ b/designs/docs.json
@@ -0,0 +1,12 @@
+{
+ "views" : {
+ "get" : {
+ "map" : "function(doc) {\n if (doc.u1db_rev) {\n var is_tombstone = true;\n var has_conflicts = false;\n if (doc._attachments) {\n if (doc._attachments.u1db_content)\n is_tombstone = false;\n if (doc._attachments.u1db_conflicts)\n has_conflicts = true;\n }\n emit(doc._id,\n {\n \"couch_rev\": doc._rev,\n \"u1db_rev\": doc.u1db_rev,\n \"is_tombstone\": is_tombstone,\n \"has_conflicts\": has_conflicts,\n }\n );\n }\n}\n"
+ }
+ },
+ "_id" : "_design/docs",
+ "updates" : {
+ "resolve_doc" : "function(doc, req){\n /* we expect to receive the following in `req.body`:\n * {\n * 'couch_rev': '<couch_rev>',\n * 'conflicts': '<base64 encoded conflicts>',\n * }\n */\n var body = JSON.parse(req.body);\n\n // fail if no document was given\n if (!doc) {\n return [null, 'document does not exist']\n } \n\n // fail if couch revisions do not match\n if (body['couch_rev'] != null\n && doc['_rev'] != body['couch_rev']) {\n return [null, 'revision conflict']\n }\n\n // fail if conflicts were not sent\n if (body['conflicts'] == null)\n return [null, 'missing conflicts']\n\n // save conflicts as attachment if they were sent\n if (body['conflicts'] != null) {\n if (!doc._attachments)\n doc._attachments = {};\n doc._attachments.u1db_conflicts = {\n content_type: \"application/octet-stream\",\n data: body['conflicts'] // should be base64 encoded\n }\n }\n // or delete attachment if there are no conflicts\n else if (doc._attachments && doc._attachments.u1db_conflicts)\n delete doc._attachments.u1db_conflicts;\n\n return [doc, 'ok'];\n}\n",
+ "put" : "function(doc, req){\n /* we expect to receive the following in `req.body`:\n * {\n * 'couch_rev': '<couch_rev>',\n * 'u1db_rev': '<u1db_rev>',\n * 'content': '<base64 encoded content>',\n * 'trans_id': '<reansaction_id>'\n * 'conflicts': '<base64 encoded conflicts>',\n * 'update_conflicts': <boolean>\n * }\n */\n var body = JSON.parse(req.body);\n\n // create a new document document\n if (!doc) {\n doc = {}\n doc['_id'] = req['id'];\n }\n // or fail if couch revisions do not match\n else if (doc['_rev'] != body['couch_rev']) {\n // of fail if revisions do not match\n return [null, 'revision conflict']\n }\n\n // store u1db rev\n doc.u1db_rev = body['u1db_rev'];\n\n // save content as attachment\n if (body['content'] != null) {\n // save u1db content as attachment\n if (!doc._attachments)\n doc._attachments = {};\n doc._attachments.u1db_content = {\n content_type: \"application/octet-stream\",\n data: body['content'] // should be base64 encoded\n };\n }\n // or delete the attachment if document is tombstone\n else if (doc._attachments &&\n doc._attachments.u1db_content)\n delete doc._attachments.u1db_content;\n\n // store the transaction id\n if (!doc.u1db_transactions)\n doc.u1db_transactions = [];\n var d = new Date();\n doc.u1db_transactions.push([d.getTime(), body['trans_id']]);\n\n // save conflicts as attachment if they were sent\n if (body['update_conflicts'])\n if (body['conflicts'] != null) {\n if (!doc._attachments)\n doc._attachments = {};\n doc._attachments.u1db_conflicts = {\n content_type: \"application/octet-stream\",\n data: body['conflicts'] // should be base64 encoded\n }\n } else {\n if(doc._attachments && doc._attachments.u1db_conflicts)\n delete doc._attachments.u1db_conflicts\n }\n\n return [doc, 'ok'];\n}\n"
+ }
+}
diff --git a/designs/syncs.json b/designs/syncs.json
new file mode 100644
index 0000000..0df5ff7
--- /dev/null
+++ b/designs/syncs.json
@@ -0,0 +1,11 @@
+{
+ "views" : {
+ "log" : {
+ "map" : "function(doc) {\n if (doc._id == 'u1db_sync_log') {\n if (doc.syncs)\n doc.syncs.forEach(function (entry) {\n emit(entry[0],\n {\n 'known_generation': entry[1],\n 'known_transaction_id': entry[2]\n });\n });\n }\n}\n"
+ }
+ },
+ "_id" : "_design/syncs",
+ "updates" : {
+ "put" : "function(doc, req){\n if (!doc) {\n doc = {}\n doc['_id'] = 'u1db_sync_log';\n doc['syncs'] = [];\n }\n body = JSON.parse(req.body);\n // remove outdated info\n doc['syncs'] = doc['syncs'].filter(\n function (entry) {\n return entry[0] != body['other_replica_uid'];\n }\n );\n // store u1db rev\n doc['syncs'].push([\n body['other_replica_uid'],\n body['other_generation'],\n body['other_transaction_id']\n ]);\n return [doc, 'ok'];\n}\n\n"
+ }
+}
diff --git a/designs/transactions.json b/designs/transactions.json
new file mode 100644
index 0000000..8fcb84d
--- /dev/null
+++ b/designs/transactions.json
@@ -0,0 +1,12 @@
+{
+ "lists" : {
+ "generation" : "function(head, req) {\n var row;\n var rows=[];\n // fetch all rows\n while(row = getRow()) {\n rows.push(row);\n }\n if (rows.length > 0)\n send(JSON.stringify({\n \"generation\": rows.length,\n \"doc_id\": rows[rows.length-1]['id'],\n \"transaction_id\": rows[rows.length-1]['value']\n }));\n else\n send(JSON.stringify({\n \"generation\": 0,\n \"doc_id\": \"\",\n \"transaction_id\": \"\",\n }));\n}\n",
+ "whats_changed" : "function(head, req) {\n var row;\n var gen = 1;\n var old_gen = 0;\n if (req.query.old_gen)\n old_gen = parseInt(req.query['old_gen']);\n send('{\"transactions\":[\\n');\n // fetch all rows\n while(row = getRow()) {\n if (gen > old_gen) {\n if (gen > old_gen+1)\n send(',\\n');\n send(JSON.stringify({\n \"generation\": gen,\n \"doc_id\": row[\"id\"],\n \"transaction_id\": row[\"value\"]\n }));\n }\n gen++;\n }\n send('\\n]}');\n}\n",
+ "trans_id_for_gen" : "function(head, req) {\n var row;\n var rows=[];\n var i = 1;\n var gen = 1;\n if (req.query.gen)\n gen = parseInt(req.query['gen']);\n // fetch all rows\n while(row = getRow())\n rows.push(row);\n if (gen <= rows.length)\n send(JSON.stringify({\n \"generation\": gen,\n \"doc_id\": rows[gen-1]['id'],\n \"transaction_id\": rows[gen-1]['value'],\n }));\n else\n send('{}');\n}\n"
+ },
+ "views" : {
+ "log" : {
+ "map" : "function(doc) {\n if (doc.u1db_transactions)\n doc.u1db_transactions.forEach(function(t) {\n emit(t[0], // use timestamp as key so the results are ordered\n t[1]); // value is the transaction_id\n });\n}\n"
+ }
+ }
+}
diff --git a/lib/tapicero.rb b/lib/tapicero.rb
index aba9fad..3923c65 100644
--- a/lib/tapicero.rb
+++ b/lib/tapicero.rb
@@ -1,5 +1,5 @@
unless defined? BASE_DIR
- BASE_DIR = File.expand_path('../..', __FILE__)
+ BASE_DIR = Pathname.new(__FILE__) + '../..'
end
unless defined? TAPICERO_CONFIG
TAPICERO_CONFIG = '/etc/leap/tapicero.yaml'
diff --git a/lib/tapicero/user_database.rb b/lib/tapicero/user_database.rb
index 8f461ef..efd723a 100644
--- a/lib/tapicero/user_database.rb
+++ b/lib/tapicero/user_database.rb
@@ -23,6 +23,22 @@ module Tapicero
CouchRest.put security_url, security
end
+ def add_design_docs
+ pattern = BASE_DIR + 'designs' + '*.json'
+ Tapicero.logger.debug "looking for design docs in #{pattern}"
+ Pathname.glob(pattern).each do |file|
+ upload_design_doc(file)
+ end
+ end
+
+ def upload_design_doc(file)
+ url = design_url(file.basename('.json'))
+ CouchRest.put url, JSON.parse(file.read)
+ Tapicero.logger.debug "uploaded design doc #{file.basename} to #{url}"
+ rescue RestClient::Conflict
+ end
+
+
def destroy
db = CouchRest.new(host).database(name)
db.delete! if db
@@ -40,6 +56,10 @@ module Tapicero
"#{host}/#{name}/_security"
end
+ def design_url(doc_name)
+ "#{host}/#{name}/_design/#{doc_name}"
+ end
+
attr_reader :host, :name
end
end
diff --git a/lib/tapicero/version.rb b/lib/tapicero/version.rb
index 3688f62..2c4bc00 100644
--- a/lib/tapicero/version.rb
+++ b/lib/tapicero/version.rb
@@ -1,4 +1,4 @@
module Tapicero
- VERSION = "0.2.0"
+ VERSION = "0.3.0"
REQUIRE_PATHS = ['lib']
end
diff --git a/lib/tapicero_daemon.rb b/lib/tapicero_daemon.rb
index 9020fa2..67eee88 100644
--- a/lib/tapicero_daemon.rb
+++ b/lib/tapicero_daemon.rb
@@ -15,6 +15,7 @@ module Tapicero
db = user_database(hash['id'])
db.create
db.secure(config.options[:security])
+ db.add_design_docs
end
users.deleted do |hash|