Use optparse for couchview. Implemented view generation option.
This commit is contained in:
parent
021121f22f
commit
c5397a9b6b
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.DS_Store
|
142
bin/couchview
142
bin/couchview
|
@ -1,111 +1,53 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
commands = %w{push generate}
|
require 'optparse'
|
||||||
|
require File.dirname(__FILE__) + "/../lib/couch_rest/commands"
|
||||||
|
|
||||||
command = ARGV[0]
|
# Set defaults
|
||||||
|
options = {
|
||||||
|
:loud => true,
|
||||||
|
}
|
||||||
|
|
||||||
if !commands.include?(command)
|
opts = OptionParser.new do |opts|
|
||||||
puts <<-USAGE
|
opts.banner = "Usage: #$0 [options] (push|generate) directory database"
|
||||||
Couchview has two modes: push and generate. Run couchview push or couchview generate for usage documentation.
|
opts.on('-q', '--quiet', "Omit extra debug info") do
|
||||||
USAGE
|
options[:loud] = false
|
||||||
exit
|
end
|
||||||
end
|
opts.on_tail('-h', '--help [push|generate]', "Display detailed help and exit") do |help_command|
|
||||||
|
puts opts
|
||||||
if ARGV.length == 1
|
case help_command
|
||||||
case command
|
|
||||||
when "generate"
|
|
||||||
puts <<-GEN
|
|
||||||
Usage: couchview generate directory design1 design2 design3 ...
|
|
||||||
|
|
||||||
Couchview will create directories and example views for the design documents you specify.
|
|
||||||
|
|
||||||
GEN
|
|
||||||
when "push"
|
when "push"
|
||||||
puts <<-PUSH
|
puts CouchRest::Commands::Push.help
|
||||||
== Pushing views with Couchview ==
|
when "generate"
|
||||||
|
puts CouchRest::Commands::Generate.help
|
||||||
Usage: couchview push directory dbname
|
|
||||||
|
|
||||||
Couchview expects a specific filesystem layout for your CouchDB views (see
|
|
||||||
example below). It also supports advanced features like inlining of library
|
|
||||||
code (so you can keep DRY) as well as avoiding unnecessary document
|
|
||||||
modification.
|
|
||||||
|
|
||||||
Couchview also solves a problem with CouchDB's view API, which only provides
|
|
||||||
access to the final reduce side of any views which have both a map and a
|
|
||||||
reduce function defined. The intermediate map results are often useful for
|
|
||||||
development and production. CouchDB is smart enough to reuse map indexes for
|
|
||||||
functions duplicated across views within the same design document.
|
|
||||||
|
|
||||||
For views with a reduce function defined, Couchview creates both a reduce view
|
|
||||||
and a map-only view, so that you can browse and query the map side as well as
|
|
||||||
the reduction, with no performance penalty.
|
|
||||||
|
|
||||||
== Example ==
|
|
||||||
|
|
||||||
couchview push foo-project/bar-views baz-database
|
|
||||||
|
|
||||||
This will push the views defined in foo-project/bar-views into a database
|
|
||||||
called baz-database. Couchview expects the views to be defined in files with
|
|
||||||
names like:
|
|
||||||
|
|
||||||
foo-project/bar-views/my-design/viewname-map.js
|
|
||||||
foo-project/bar-views/my-design/viewname-reduce.js
|
|
||||||
foo-project/bar-views/my-design/noreduce-map.js
|
|
||||||
|
|
||||||
Pushed to => http://localhost:5984/baz-database/_design/my-design
|
|
||||||
|
|
||||||
And the design document:
|
|
||||||
{
|
|
||||||
"views" : {
|
|
||||||
"viewname-map" : {
|
|
||||||
"map" : "### contents of view-name-map.js ###"
|
|
||||||
},
|
|
||||||
"viewname-reduce" : {
|
|
||||||
"map" : "### contents of view-name-map.js ###",
|
|
||||||
"reduce" : "### contents of view-name-reduce.js ###"
|
|
||||||
},
|
|
||||||
"noreduce-map" : {
|
|
||||||
"map" : "### contents of noreduce-map.js ###"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Couchview will create a design document for each subdirectory of the views
|
|
||||||
directory specified on the command line.
|
|
||||||
|
|
||||||
== Library Inlining ==
|
|
||||||
|
|
||||||
Couchview can optionally inline library code into your views so you only have
|
|
||||||
to maintain it in one place. It looks for any files named lib.* in your
|
|
||||||
design-doc directory (for doc specific libs) and in the parent views directory
|
|
||||||
(for project global libs). These libraries are only inserted into views which
|
|
||||||
include the text
|
|
||||||
|
|
||||||
//include-lib
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
#include-lib
|
|
||||||
|
|
||||||
Couchview is a result of scratching my own itch. I'd be happy to make it more
|
|
||||||
general, so please contact me at jchris@grabb.it if you'd like to see anything
|
|
||||||
added or changed.
|
|
||||||
PUSH
|
|
||||||
end
|
end
|
||||||
exit
|
exit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
opts.parse!(ARGV)
|
||||||
|
|
||||||
|
options[:command] = ARGV.shift
|
||||||
|
options[:directory] = ARGV.shift
|
||||||
|
options[:trailing_args] = ARGV
|
||||||
|
|
||||||
|
# There must be a better way to check for extra required args
|
||||||
|
unless (["push", "generate"].include?(options[:command]) && options[:directory] && options[:trailing_args])
|
||||||
|
puts(opts)
|
||||||
|
exit
|
||||||
end
|
end
|
||||||
|
|
||||||
require 'rubygems'
|
# DEBUG
|
||||||
require 'couchrest'
|
puts options.inspect
|
||||||
|
|
||||||
if command == 'push'
|
# The options hash now contains the resolved defaults
|
||||||
dirname = ARGV[1]
|
# and the overrides from the command line.
|
||||||
dbname = ARGV[2]
|
|
||||||
fm = CouchRest::FileManager.new(dbname)
|
# Call your class and send it the options here
|
||||||
fm.loud = true
|
# cr = CouchRest::FileManager.new(options[:database_name])
|
||||||
puts "Pushing views from directory #{dirname} to database #{fm.db}"
|
|
||||||
fm.push_views(dirname)
|
case options[:command]
|
||||||
elsif command == 'generate'
|
when "push"
|
||||||
puts "Under construction ;)"
|
CouchRest::Commands::Push.run(options)
|
||||||
|
when "generate"
|
||||||
|
CouchRest::Commands::Generate.run(options)
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,7 +20,6 @@ Gem::Specification.new do |s|
|
||||||
s.bindir = 'bin'
|
s.bindir = 'bin'
|
||||||
s.executables << 'couchview'
|
s.executables << 'couchview'
|
||||||
s.executables << 'couchdir'
|
s.executables << 'couchdir'
|
||||||
s.executables << 'couchcontrol'
|
|
||||||
s.add_dependency("json", [">= 1.1.2"])
|
s.add_dependency("json", [">= 1.1.2"])
|
||||||
s.add_dependency("rest-client", [">= 0.5"])
|
s.add_dependency("rest-client", [">= 0.5"])
|
||||||
end
|
end
|
||||||
|
|
5
lib/couch_rest/commands.rb
Normal file
5
lib/couch_rest/commands.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
require File.join(File.dirname(__FILE__), "..", "couchrest")
|
||||||
|
|
||||||
|
%w(push generate).each do |filename|
|
||||||
|
require File.join(File.dirname(__FILE__), "commands", filename)
|
||||||
|
end
|
71
lib/couch_rest/commands/generate.rb
Normal file
71
lib/couch_rest/commands/generate.rb
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
require 'fileutils'
|
||||||
|
|
||||||
|
class CouchRest
|
||||||
|
module Commands
|
||||||
|
module Generate
|
||||||
|
|
||||||
|
def self.run(options)
|
||||||
|
directory = options[:directory]
|
||||||
|
design_names = options[:trailing_args]
|
||||||
|
|
||||||
|
FileUtils.mkdir_p(directory)
|
||||||
|
filename = File.join(directory, "lib.js")
|
||||||
|
self.write(filename, <<-FUNC)
|
||||||
|
// Put global functions here.
|
||||||
|
// Include in your views with
|
||||||
|
//
|
||||||
|
// //include-lib
|
||||||
|
FUNC
|
||||||
|
|
||||||
|
design_names.each do |design_name|
|
||||||
|
subdirectory = File.join(directory, design_name)
|
||||||
|
FileUtils.mkdir_p(subdirectory)
|
||||||
|
filename = File.join(subdirectory, "sample-map.js")
|
||||||
|
self.write(filename, <<-FUNC)
|
||||||
|
function(doc) {
|
||||||
|
// Keys is first letter of _id
|
||||||
|
emit(doc._id[0], doc);
|
||||||
|
}
|
||||||
|
FUNC
|
||||||
|
|
||||||
|
filename = File.join(subdirectory, "sample-reduce.js")
|
||||||
|
self.write(filename, <<-FUNC)
|
||||||
|
function(keys, values) {
|
||||||
|
// Count the number of keys starting with this letter
|
||||||
|
return values.length;
|
||||||
|
}
|
||||||
|
FUNC
|
||||||
|
|
||||||
|
filename = File.join(subdirectory, "lib.js")
|
||||||
|
self.write(filename, <<-FUNC)
|
||||||
|
// Put functions specific to '#{design_name}' here.
|
||||||
|
// Include in your views with
|
||||||
|
//
|
||||||
|
// //include-lib
|
||||||
|
FUNC
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.help
|
||||||
|
helpstring = <<-GEN
|
||||||
|
|
||||||
|
Usage: couchview generate directory design1 design2 design3 ...
|
||||||
|
|
||||||
|
Couchview will create directories and example views for the design documents you specify.
|
||||||
|
|
||||||
|
GEN
|
||||||
|
helpstring.gsub(/^ /, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.write(filename, contents)
|
||||||
|
puts "Writing #{filename}"
|
||||||
|
File.open(filename, "w") do |f|
|
||||||
|
# Remove leading spaces
|
||||||
|
contents.gsub!(/^ ( )?/, '')
|
||||||
|
f.write contents
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
99
lib/couch_rest/commands/push.rb
Normal file
99
lib/couch_rest/commands/push.rb
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
class CouchRest
|
||||||
|
|
||||||
|
module Commands
|
||||||
|
|
||||||
|
module Push
|
||||||
|
|
||||||
|
def self.run(options)
|
||||||
|
directory = options[:directory]
|
||||||
|
database = options[:trailing_args].first
|
||||||
|
|
||||||
|
fm = CouchRest::FileManager.new(database)
|
||||||
|
fm.loud = options[:loud]
|
||||||
|
puts "Pushing views from directory #{directory} to database #{fm.db}"
|
||||||
|
fm.push_views(directory)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.help
|
||||||
|
helpstring = <<-GEN
|
||||||
|
|
||||||
|
== Pushing views with Couchview ==
|
||||||
|
|
||||||
|
Usage: couchview push directory dbname
|
||||||
|
|
||||||
|
Couchview expects a specific filesystem layout for your CouchDB views (see
|
||||||
|
example below). It also supports advanced features like inlining of library
|
||||||
|
code (so you can keep DRY) as well as avoiding unnecessary document
|
||||||
|
modification.
|
||||||
|
|
||||||
|
Couchview also solves a problem with CouchDB's view API, which only provides
|
||||||
|
access to the final reduce side of any views which have both a map and a
|
||||||
|
reduce function defined. The intermediate map results are often useful for
|
||||||
|
development and production. CouchDB is smart enough to reuse map indexes for
|
||||||
|
functions duplicated across views within the same design document.
|
||||||
|
|
||||||
|
For views with a reduce function defined, Couchview creates both a reduce view
|
||||||
|
and a map-only view, so that you can browse and query the map side as well as
|
||||||
|
the reduction, with no performance penalty.
|
||||||
|
|
||||||
|
== Example ==
|
||||||
|
|
||||||
|
couchview push foo-project/bar-views baz-database
|
||||||
|
|
||||||
|
This will push the views defined in foo-project/bar-views into a database
|
||||||
|
called baz-database. Couchview expects the views to be defined in files with
|
||||||
|
names like:
|
||||||
|
|
||||||
|
foo-project/bar-views/my-design/viewname-map.js
|
||||||
|
foo-project/bar-views/my-design/viewname-reduce.js
|
||||||
|
foo-project/bar-views/my-design/noreduce-map.js
|
||||||
|
|
||||||
|
Pushed to => http://localhost:5984/baz-database/_design/my-design
|
||||||
|
|
||||||
|
And the design document:
|
||||||
|
{
|
||||||
|
"views" : {
|
||||||
|
"viewname-map" : {
|
||||||
|
"map" : "### contents of view-name-map.js ###"
|
||||||
|
},
|
||||||
|
"viewname-reduce" : {
|
||||||
|
"map" : "### contents of view-name-map.js ###",
|
||||||
|
"reduce" : "### contents of view-name-reduce.js ###"
|
||||||
|
},
|
||||||
|
"noreduce-map" : {
|
||||||
|
"map" : "### contents of noreduce-map.js ###"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Couchview will create a design document for each subdirectory of the views
|
||||||
|
directory specified on the command line.
|
||||||
|
|
||||||
|
== Library Inlining ==
|
||||||
|
|
||||||
|
Couchview can optionally inline library code into your views so you only have
|
||||||
|
to maintain it in one place. It looks for any files named lib.* in your
|
||||||
|
design-doc directory (for doc specific libs) and in the parent views directory
|
||||||
|
(for project global libs). These libraries are only inserted into views which
|
||||||
|
include the text
|
||||||
|
|
||||||
|
//include-lib
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
#include-lib
|
||||||
|
|
||||||
|
Couchview is a result of scratching my own itch. I'd be happy to make it more
|
||||||
|
general, so please contact me at jchris@grabb.it if you'd like to see anything
|
||||||
|
added or changed.
|
||||||
|
|
||||||
|
GEN
|
||||||
|
helpstring.gsub(/^ /, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in a new issue