add show-view command twisty
authorMark Hammond <mhammond@skippinet.com.au>
Fri, 03 Apr 2009 16:02:31 +1100
branchtwisty
changeset 152 2eb31d62489c13339c85b9d2d0129c1dce6c25d0
parent 151 684089de8f1a1da42f26c95c1262332c3c4ffaf5
child 153 c109e37a2770ba8cdb51b5cb59334a705848e2dc
push id1
push userroot
push dateWed, 08 Apr 2009 01:46:05 +0000
add show-view command
server/python/run-raindrop.py
--- a/server/python/run-raindrop.py
+++ b/server/python/run-raindrop.py
@@ -1,33 +1,39 @@
 """The raindrop server
 """
 import sys
 import optparse
 import logging
 
-from twisted.internet import reactor, defer
+from twisted.internet import reactor, defer, task
 
 from junius import model
 from junius import bootstrap
 from junius import pipeline
 from junius.sync import get_conductor
 
 logger = logging.getLogger('raindrop')
 
 class HelpFormatter(optparse.IndentedHelpFormatter):
     def format_description(self, description):
         return description
 
-# a decorator for our global functions, so they can insist they complete
-# before the next command executes;
+# decorators for our global functions:
+#  so they can insist they complete before the next command executes
 def asynch_command(f):
     f.asynch = True
     return f
 
+#  so they can consume the rest of the args
+def allargs_command(f):
+    f.allargs = True
+    return f
+
+
 # NOTE: All global functions with docstrings are 'commands'
 # They must all return a deferred.
 def nuke_db_and_delete_everything_forever(result, parser, options):
     """Nuke the database AND ALL MESSAGES FOREVER"""
     return model.nuke_db(
                 ).addCallback(model.fab_db
                 ).addCallback(bootstrap.install_accounts)
 
@@ -51,16 +57,70 @@ def sync_messages(result, parser, option
 @asynch_command
 def process(result, parser, options):
     """Process all messages to see if any extensions need running"""
     def done(result):
         print "Message pipeline has finished..."
     p = pipeline.Pipeline(model.get_doc_model())
     return p.start().addCallback(done)
 
+@allargs_command
+def show_view(result, parser, options, args):
+    """Pretty-print the result of executing a view.
+
+    All arguments after this command are URLs to be executed as the view.  If
+    the view name starts with '/', the URL will be used as-is.  This is
+    suitable for builtin views - eg:
+
+        show-view /_all_docs?limit=5
+    
+    will fetch the first 5 results from the URL:
+
+        http://[dbhost]/[dbname]/_all_docs?limit=5"
+
+    whereas
+
+        show-view my_design_doc/my_view?limit=5
+
+    will fetch the first 5 results from the URL
+
+        http://[dbhost]/[dbname]/_view/my_design_doc/my_view?limit=5
+
+    (XXX - todo - couch 0.9 changes the above URL - adjust this docstring
+    accordingly...)
+    """
+    from pprint import pprint
+    def print_view(result, view_name):
+        print "** view %r **" % view_name
+        pprint(result)
+
+    def gen_views():
+        for arg in args:
+            # don't use open_view as then we'd need to parse the query portion!
+            # grrr - just to get the dbname :()
+            from junius.config import get_config
+            dbinfo = get_config().couches['local']
+            dbname = dbinfo['name']
+            if arg.startswith("/"):
+                uri = "/%s/%s" % (dbname, arg)
+            else:
+                try:
+                    doc, rest = arg.split("/")
+                except ValueError:
+                    parser.error("View name must be in the form design_doc_name/view")
+                uri = "/%s/_view/%s/%s" % (dbname, doc, rest)
+            db = model.get_db()
+            yield db.get(uri
+                ).addCallback(db.parseResult
+                ).addCallback(print_view, arg)
+
+    coop = task.Cooperator()
+    return coop.coiterate(gen_views())
+
+
 def unprocess(result, parser, options):
     """Delete all documents which can be re-generated by the 'process' command
     """
     def done(result):
         print "unprocess has finished..."
     # XXX - pipeline should probably be a singleton?
     p = pipeline.Pipeline(model.get_doc_model())
     return p.unprocess().addCallback(done)
@@ -71,17 +131,17 @@ def delete_docs(result, parser, options)
     """
     # NOTE: This is for development only, until we get a way to say
     # 'reprocess stuff you've already done' - in the meantime deleting those
     # intermediate docs has the same result...
     def _del_docs(to_del):
         docs = []
         for id, rev in to_del:
             docs.append({'_id': id, '_rev': rev, '_deleted': True})
-        return db.updateDocuments(docs)
+        return model.get_db().updateDocuments(docs)
 
     def _got_docs(result, dt):
         to_del = [(row['id'], row['value']) for row in result['rows']]
         logger.info("Deleting %d documents of type %r", len(to_del), dt)
         return to_del
 
     if not options.doctypes:
         parser.error("You must specify one or more --doctype")
@@ -202,27 +262,32 @@ def main():
     d.addCallback(model.fab_db
         ).addCallback(maybe_install_accounts
         )
     # Check if the files on the filesystem need updating.
     d.addCallback(bootstrap.install_client_files, options)
     d.addCallback(bootstrap.install_views, options)
 
     # Now process the args specified.
-    for arg in args:
+    for i, arg in enumerate(args):
         try:
             func = all_args[arg]
         except KeyError:
             parser.error("Invalid command: " + arg)
 
         asynch = getattr(func, 'asynch', False)
         if asynch:
             asynch_tasks.append(func)
         else:
-            d.addCallback(func, parser, options)
+            consumes_args = getattr(func, 'allargs', False)
+            if consumes_args:
+                d.addCallback(func, parser, options, args[i+1:])
+                break
+            else:
+                d.addCallback(func, parser, options)
 
     # and some final deferreds to control the process itself.
     def done(whateva):
         print "Apparently everything is finished - terminating."
         reactor.stop()
 
     def start(whateva):
         if not asynch_tasks: