couch_docs 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,13 @@
1
+ == 1.1.0 / 2010-03-13
2
+
3
+ * Better command line experience.
4
+ * Default to current directory.
5
+ * Print help without args / better format (optparse).
6
+ * Support the !code macro from couchapp.
7
+ * Support a flag (-d) to only work on design docs.
8
+ * Can create the DB if it doesn't already exist
9
+ * Command line can be used to watch for local changes to be pushed immediately to the CouchDB server.
10
+
1
11
  == 1.0.0 / 2009-08-09
2
12
 
3
13
  * Update the couch-docs script to be able to dump a CouchDB database
data/README.rdoc CHANGED
@@ -16,11 +16,15 @@ Manage CouchDB views and documents.
16
16
  (e.g. <tt>_design/recipes/count_by_month/map.js</tt>) into CouchDB
17
17
  design documents.
18
18
 
19
+ * Support for the <tt>!code</tt> macro from couchapp (useful for
20
+ DRYing up map/reduce views as well as list/show documents.
21
+
19
22
  * Dump documents stored in CouchDB to the filesystem.
20
23
 
21
24
  * Script (couch-docs) to restore / backup CouchDB database.
22
25
 
23
- * The couch-docs scipts does not work with design documents.
26
+ * Multiple options including a directory watcher for uploading
27
+ directory changes
24
28
 
25
29
  * A progress bar would be helpful.
26
30
 
@@ -34,7 +38,7 @@ Manage CouchDB views and documents.
34
38
  couch-docs dump "http://localhost:5984/db" path/to/dump_dir/
35
39
 
36
40
  # For loading documents from the filesystem into CouchDB
37
- couch-docs load path/to/dump_dir/ "http://localhost:5984/db"
41
+ couch-docs load "http://localhost:5984/db" path/to/dump_dir/
38
42
 
39
43
 
40
44
  In code:
@@ -62,16 +66,17 @@ Manage CouchDB views and documents.
62
66
  * CouchDB
63
67
  * JSON
64
68
  * RestClient
69
+ * DirectoryWatcher
65
70
 
66
71
  == INSTALL:
67
72
 
68
- * sudo gem install eee-c-couch_docs
73
+ * sudo gem install couch_docs
69
74
 
70
75
  == LICENSE:
71
76
 
72
77
  (The MIT License)
73
78
 
74
- Copyright (c) 2009
79
+ Copyright (c) 2010
75
80
 
76
81
  Permission is hereby granted, free of charge, to any person obtaining
77
82
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -27,11 +27,13 @@ PROJ.rubyforge.name = 'couch_docs'
27
27
 
28
28
  PROJ.spec.opts << '--color'
29
29
 
30
- PROJ.gem.dependencies = %w{json rest-client}
30
+ #PROJ.gem.dependencies = %w{json rest-client}
31
+ PROJ.gem.development_dependencies << 'rspec'
31
32
 
32
33
  PROJ.readme_file = 'README.rdoc'
33
34
 
34
35
  depend_on 'rest-client'
35
36
  depend_on 'json'
37
+ depend_on 'directory_watcher'
36
38
 
37
39
  # EOF
data/bin/couch-docs CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'rubygems'
4
+
3
5
  require File.expand_path(
4
6
  File.join(File.dirname(__FILE__), %w[.. lib couch_docs]))
5
7
 
data/couch_docs.gemspec CHANGED
@@ -2,48 +2,47 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{couch_docs}
5
- s.version = "1.0.0"
5
+ s.version = "1.1.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Chris Strom"]
9
- s.date = %q{2009-08-09}
9
+ s.date = %q{2010-03-11}
10
10
  s.default_executable = %q{couch-docs}
11
11
  s.description = %q{Manage CouchDB views and documents.}
12
12
  s.email = %q{chris@eeecooks.com}
13
13
  s.executables = ["couch-docs"]
14
14
  s.extra_rdoc_files = ["History.txt", "README.rdoc", "bin/couch-docs"]
15
- s.files = ["History.txt", "README.rdoc", "Rakefile", "bin/couch-docs", "couch_docs.gemspec", "fixtures/_design/a/b/c.js", "fixtures/_design/a/b/d.js", "fixtures/bar.json", "fixtures/foo.json", "lib/couch_docs.rb", "lib/couch_docs/command_line.rb", "lib/couch_docs/design_directory.rb", "lib/couch_docs/document_directory.rb", "lib/couch_docs/store.rb", "spec/couch_docs_spec.rb", "spec/spec_helper.rb", "test/test_couch_docs.rb"]
16
- s.has_rdoc = true
15
+ s.files = ["History.txt", "README.rdoc", "Rakefile", "bin/couch-docs", "couch_docs.gemspec", "fixtures/_design/__lib/foo.js", "fixtures/_design/a/b/c.js", "fixtures/_design/a/b/d.js", "fixtures/_design/x/z.js", "fixtures/bar.json", "fixtures/foo.json", "lib/couch_docs.rb", "lib/couch_docs/command_line.rb", "lib/couch_docs/design_directory.rb", "lib/couch_docs/document_directory.rb", "lib/couch_docs/store.rb", "spec/couch_docs_spec.rb", "spec/spec_helper.rb", "test/test_couch_docs.rb"]
17
16
  s.homepage = %q{http://github.com/eee-c/couch_docs}
18
17
  s.rdoc_options = ["--main", "README.rdoc"]
19
18
  s.require_paths = ["lib"]
20
19
  s.rubyforge_project = %q{couch_docs}
21
- s.rubygems_version = %q{1.3.1}
20
+ s.rubygems_version = %q{1.3.5}
22
21
  s.summary = %q{Manage CouchDB views and documents}
23
22
  s.test_files = ["test/test_couch_docs.rb"]
24
23
 
25
24
  if s.respond_to? :specification_version then
26
25
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
27
- s.specification_version = 2
26
+ s.specification_version = 3
28
27
 
29
28
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
30
- s.add_runtime_dependency(%q<json>, [">= 0"])
31
- s.add_runtime_dependency(%q<rest-client>, [">= 0"])
32
- s.add_runtime_dependency(%q<rest-client>, [">= 1.0.3"])
33
- s.add_runtime_dependency(%q<json>, [">= 1.1.6"])
34
- s.add_development_dependency(%q<bones>, [">= 2.5.1"])
29
+ s.add_runtime_dependency(%q<rest-client>, [">= 1.1.0"])
30
+ s.add_runtime_dependency(%q<json>, [">= 1.2.0"])
31
+ s.add_runtime_dependency(%q<directory_watcher>, [">= 1.3.1"])
32
+ s.add_development_dependency(%q<bones>, [">= 2.5.0"])
33
+ s.add_development_dependency(%q<rspec>, [">= 0"])
35
34
  else
36
- s.add_dependency(%q<json>, [">= 0"])
37
- s.add_dependency(%q<rest-client>, [">= 0"])
38
- s.add_dependency(%q<rest-client>, [">= 1.0.3"])
39
- s.add_dependency(%q<json>, [">= 1.1.6"])
40
- s.add_dependency(%q<bones>, [">= 2.5.1"])
35
+ s.add_dependency(%q<rest-client>, [">= 1.1.0"])
36
+ s.add_dependency(%q<json>, [">= 1.2.0"])
37
+ s.add_dependency(%q<directory_watcher>, [">= 1.3.1"])
38
+ s.add_dependency(%q<bones>, [">= 2.5.0"])
39
+ s.add_dependency(%q<rspec>, [">= 0"])
41
40
  end
42
41
  else
43
- s.add_dependency(%q<json>, [">= 0"])
44
- s.add_dependency(%q<rest-client>, [">= 0"])
45
- s.add_dependency(%q<rest-client>, [">= 1.0.3"])
46
- s.add_dependency(%q<json>, [">= 1.1.6"])
47
- s.add_dependency(%q<bones>, [">= 2.5.1"])
42
+ s.add_dependency(%q<rest-client>, [">= 1.1.0"])
43
+ s.add_dependency(%q<json>, [">= 1.2.0"])
44
+ s.add_dependency(%q<directory_watcher>, [">= 1.3.1"])
45
+ s.add_dependency(%q<bones>, [">= 2.5.0"])
46
+ s.add_dependency(%q<rspec>, [">= 0"])
48
47
  end
49
48
  end
@@ -0,0 +1 @@
1
+ function foo () { return "foo"; }
@@ -0,0 +1,2 @@
1
+ // !code foo.js
2
+ function bar () { return "bar"; }
@@ -1,27 +1,134 @@
1
+ require 'optparse'
2
+ require 'pp'
3
+ require 'directory_watcher'
4
+
1
5
  module CouchDocs
2
6
  class CommandLine
7
+ COMMANDS = %w{push dump}
8
+ DEPRECATED_COMMANDS = %w{load}
9
+
3
10
  def self.run(*args)
4
11
  CommandLine.new(*args).run
5
12
  end
6
13
 
7
- attr_accessor :command, :options
14
+ attr_reader :command, :options
8
15
 
9
16
  def initialize(args)
10
- @command = args.shift
11
- @options = args
17
+ parse_options(args)
12
18
  end
13
19
 
14
20
  def run
15
21
  case command
16
22
  when "dump"
17
- CouchDocs.dump(*options)
18
- when "load"
19
- CouchDocs.put_document_dir(*options.reverse)
20
- when "help", "--help", "-h"
21
- puts "#{$0} load dir couchdb_uri"
22
- puts "#{$0} dump couchdb_uri dir"
23
- else
24
- raise ArgumentError.new("Unknown command #{command}")
23
+ CouchDocs.dump(@options[:couchdb_url],
24
+ @options[:target_dir],
25
+ @options[:dump])
26
+ when "push"
27
+ if @options[:destructive]
28
+ CouchDocs.destructive_database_create(options[:couchdb_url])
29
+ end
30
+
31
+ dw = DirectoryWatcher.new @options[:target_dir]
32
+ dw.glob = '**/*'
33
+ dw.interval = 2.0
34
+
35
+ dw.add_observer do |*args|
36
+ puts "Updating documents on CouchDB Server..."
37
+ CouchDocs.put_dir(@options[:couchdb_url],
38
+ @options[:target_dir])
39
+ end
40
+
41
+ if @options[:watch]
42
+ dw.start
43
+
44
+ begin
45
+ sleep 30 while true
46
+ rescue Interrupt
47
+ dw.stop
48
+ puts
49
+ end
50
+ else
51
+ dw.run_once
52
+ end
53
+ when "load" # DEPRECATED
54
+ CouchDocs.put_dir(@options[:target_dir],
55
+ @options[:couchdb_url])
56
+ end
57
+ end
58
+
59
+ def parse_options(args)
60
+ @options = { :target_dir => "." }
61
+
62
+ options_parser = OptionParser.new do |opts|
63
+ opts.banner = "Usage: couch-docs push|dump [OPTIONS] couchdb_url [target_dir]"
64
+
65
+ opts.separator ""
66
+ opts.separator "If a target_dir is not specified, the current working directory will be used."
67
+
68
+ opts.separator ""
69
+ opts.separator "Push options:"
70
+
71
+ opts.on("-R", "--destructive",
72
+ "Drop the couchdb_uri (if it exists) and create a new database") do
73
+ @options[:destructive] = true
74
+ end
75
+
76
+ # TODO: bulk_docs in 1.2
77
+ # opts.on("-b", "--bulk [BATCH_SIZE=1000]", Integer,
78
+ # "Use bulk insert when pushing new documents") do |batch_size|
79
+ # @options[:bulk] = true
80
+ # @options[:batch_size] = batch_size || 1000
81
+ # end
82
+
83
+ opts.on("-w", "--watch", "Watch the directory for changes, uploading when detected") do
84
+ @options[:watch] = true
85
+ end
86
+
87
+ opts.separator ""
88
+ opts.separator "Dump options:"
89
+
90
+ opts.on("-d", "--design", "Only dump design documents") do
91
+ @options[:dump] = :design
92
+ end
93
+ opts.on("-D", "--data", "Only dump data documents") do
94
+ @options[:dump] = :doc
95
+ end
96
+
97
+ opts.separator ""
98
+ opts.separator "Common options:"
99
+
100
+ opts.on_tail("-v", "--version", "Show version") do
101
+ puts File.basename($0) + " " + CouchDocs::VERSION
102
+ exit
103
+ end
104
+
105
+ # No argument, shows at tail. This will print an options summary.
106
+ # Try it and see!
107
+ opts.on_tail("-h", "--help", "Show this message") do
108
+ puts opts
109
+ exit
110
+ end
111
+ end
112
+
113
+ begin
114
+ options_parser.parse!(args)
115
+ additional_help = "#{options_parser.banner}\n\nTry --help for more options."
116
+ unless (COMMANDS+DEPRECATED_COMMANDS).include? args.first
117
+ puts "invalid command: \"#{args.first}\". Must be one of #{COMMANDS.join(', ')}.\n\n"
118
+ puts additional_help
119
+ exit
120
+ end
121
+ @command = args.shift
122
+ unless args.first
123
+ puts "Missing required couchdb_uri argument.\n\n"
124
+ puts additional_help
125
+ exit
126
+ end
127
+ @options[:couchdb_url] = args.shift
128
+ @options[:target_dir] = args.shift if (args.size >= 1)
129
+
130
+ rescue OptionParser::InvalidOption => e
131
+ raise e
25
132
  end
26
133
  end
27
134
  end
@@ -25,6 +25,8 @@ module CouchDocs
25
25
  @couch_view_dir = path
26
26
  end
27
27
 
28
+ # Load
29
+
28
30
  def to_hash
29
31
  Dir["#{couch_view_dir}/**/*.js"].inject({}) do |memo, filename|
30
32
  DesignDirectory.
@@ -38,9 +40,64 @@ module CouchDocs
38
40
  gsub(/#{couch_view_dir}\/?/, '').
39
41
  split(/\//) +
40
42
  [
41
- File.basename(filename, '.js'),
42
- File.new(filename).read
43
+ File.basename(filename, '.js').gsub(/%2F/, '/'),
44
+ read_value(filename)
43
45
  ]
44
46
  end
47
+
48
+ def read_value(filename)
49
+ File.
50
+ readlines(filename).
51
+ map { |line| process_code_macro(line) }.
52
+ join
53
+ end
54
+
55
+ def process_code_macro(line)
56
+ if line =~ %r{\s*//\s*!code\s*(\S+)\s*}
57
+ "// !begin code #{$1}\n" +
58
+ read_from_lib($1) +
59
+ "// !end code #{$1}\n"
60
+ else
61
+ line
62
+ end
63
+ end
64
+
65
+ def read_from_lib(path)
66
+ File.read("#{couch_view_dir}/__lib/#{path}")
67
+ end
68
+
69
+ # Store
70
+
71
+ def store_document(doc)
72
+ id = doc['_id']
73
+ self.save_js(nil, id, doc)
74
+ end
75
+
76
+ def save_js(rel_path, key, value)
77
+ if value.is_a? Hash
78
+ value.each_pair do |k, v|
79
+ next if k == '_id'
80
+ self.save_js([rel_path, key].compact.join('/'), k, v)
81
+
82
+ end
83
+ else
84
+ path = couch_view_dir + '/' + rel_path
85
+ FileUtils.mkdir_p(path)
86
+
87
+ file = File.new("#{path}/#{key.gsub(/\//, '%2F')}.js", "w+")
88
+ file.write(remove_code_macros(value))
89
+ file.close
90
+ end
91
+ end
92
+
93
+ def remove_code_macros(js)
94
+ js =~ %r{// !begin code ([.\w]+)$}m
95
+ lib = $1
96
+ if lib and js =~ %r{// !end code #{lib}$}m
97
+ remove_code_macros(js.sub(%r{// !begin code #{lib}.+// !end code #{lib}}m, "// !code #{lib}"))
98
+ else
99
+ js
100
+ end
101
+ end
45
102
  end
46
103
  end
@@ -6,13 +6,14 @@ module CouchDocs
6
6
  class Store
7
7
  include Enumerable
8
8
 
9
- attr_accessor :url
9
+ attr_accessor :url, :design_docs_only
10
10
 
11
11
  # Initialize a CouchDB store object. Requires a URL for the
12
12
  # target CouchDB database.
13
13
  #
14
- def initialize(url)
14
+ def initialize(url, options={})
15
15
  @url = url
16
+ @design_docs_only = (options[:only] == :design)
16
17
  end
17
18
 
18
19
  # Loads all supplied design documents in the current store.
@@ -45,6 +46,12 @@ module CouchDocs
45
46
  :content_type => 'application/json'
46
47
  end
47
48
 
49
+ def self.post(path, doc)
50
+ RestClient.post path,
51
+ doc.to_json,
52
+ :content_type => 'application/json'
53
+ end
54
+
48
55
  def self.delete(path)
49
56
  # retrieve existing to obtain the revision
50
57
  old = self.get(path)
@@ -56,7 +63,9 @@ module CouchDocs
56
63
  end
57
64
 
58
65
  def each
59
- Store.get("#{url}/_all_docs")['rows'].each do |rec|
66
+ all_url = "#{url}/_all_docs" +
67
+ (design_docs_only ? '?startkey=%22_design%22&endkey=%22_design0%22' : "")
68
+ Store.get(all_url)['rows'].each do |rec|
60
69
  yield Store.get("#{url}/#{rec['id']}?attachments=true")
61
70
  end
62
71
  end
data/lib/couch_docs.rb CHANGED
@@ -1,7 +1,9 @@
1
+ require 'ostruct'
2
+
1
3
  module CouchDocs
2
4
 
3
5
  # :stopdoc:
4
- VERSION = '1.0.0'
6
+ VERSION = '1.1.0'
5
7
  LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
8
  PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
9
  # :startdoc:
@@ -49,13 +51,25 @@ module CouchDocs
49
51
  # Dump all documents located at <tt>db_uri</tt> into the directory
50
52
  # <tt>dir</tt>
51
53
  #
52
- def self.dump(db_uri, dir)
53
- store = Store.new(db_uri)
54
- dir = DocumentDirectory.new(dir)
55
- store.
56
- map.
57
- reject { |doc| doc['_id'] =~ /^_design/ }.
58
- each { |doc| doc.delete('_rev'); dir.store_document(doc) }
54
+ def self.dump(db_uri, dir, only=nil)
55
+ null_dir = OpenStruct.new(:store_document => nil)
56
+
57
+ doc_dir = (only == :design) ?
58
+ null_dir : DocumentDirectory.new(dir)
59
+ design_dir = (only == :doc) ?
60
+ null_dir : DesignDirectory.new(dir)
61
+
62
+ store = Store.new(db_uri, :only => only)
63
+ store.map.each do |doc|
64
+ doc.delete('_rev')
65
+ (doc['_id'] =~ /^_design/ ? design_dir : doc_dir).
66
+ store_document(doc)
67
+ end
68
+ end
69
+
70
+ # Create or recreate the database located at <tt>db_uri</tt>
71
+ def self.destructive_database_create(db_uri)
72
+ Store.put!(db_uri, "")
59
73
  end
60
74
 
61
75
  # Returns the library path for the module. If any arguments are given,
@@ -1,6 +1,14 @@
1
1
  require File.join(File.dirname(__FILE__), %w[spec_helper])
2
2
 
3
3
  describe CouchDocs do
4
+ it "should be able to create (or delete/create) a DB" do
5
+ Store.
6
+ should_receive(:put!).
7
+ with("couchdb_url", anything())
8
+
9
+ CouchDocs.destructive_database_create("couchdb_url")
10
+ end
11
+
4
12
  it "should be able to load design and normal documents" do
5
13
  CouchDocs.
6
14
  should_receive(:put_design_dir).
@@ -51,7 +59,10 @@ describe CouchDocs do
51
59
  @store = mock("Store")
52
60
  Store.stub!(:new).and_return(@store)
53
61
 
54
- @dir = mock("Document Directory")
62
+ @des_dir = mock("Design Directory").as_null_object
63
+ DesignDirectory.stub!(:new).and_return(@des_dir)
64
+
65
+ @dir = mock("Document Directory").as_null_object
55
66
  DocumentDirectory.stub!(:new).and_return(@dir)
56
67
  end
57
68
  it "should be able to store all CouchDB documents on the filesystem" do
@@ -78,6 +89,24 @@ describe CouchDocs do
78
89
 
79
90
  CouchDocs.dump("uri", "fixtures")
80
91
  end
92
+ it "should not dump regular docs when asked for only design docs" do
93
+ @store.stub!(:map).
94
+ and_return([{'foo' => 'bar'}])
95
+
96
+ @dir.
97
+ should_not_receive(:store_document)
98
+
99
+ CouchDocs.dump("uri", "fixtures", :design)
100
+ end
101
+ it "should not dump design docs when asked for only regular docs" do
102
+ @store.stub!(:map).
103
+ and_return([{'_id' => '_design/foo'}])
104
+
105
+ @des_dir.
106
+ should_not_receive(:store_document)
107
+
108
+ CouchDocs.dump("uri", "fixtures", :doc)
109
+ end
81
110
  end
82
111
  end
83
112
 
@@ -262,17 +291,126 @@ describe DesignDirectory do
262
291
  end
263
292
 
264
293
  it "should assemble all documents into a single docs structure" do
265
- @it.to_hash.
294
+ @it.to_hash['a'].
266
295
  should == {
267
- 'a' => {
268
296
  'b' => {
269
297
  'c' => 'function(doc) { return true; }',
270
298
  'd' => 'function(doc) { return true; }'
271
299
  }
272
300
  }
301
+ end
273
302
 
303
+ it "should process code macros when assembling" do
304
+ @it.to_hash['x'].
305
+ should == {
306
+ 'z' =>
307
+ "// !begin code foo.js\n" +
308
+ "function foo () { return \"foo\"; }\n" +
309
+ "// !end code foo.js\n" +
310
+ "function bar () { return \"bar\"; }\n"
274
311
  }
275
312
  end
313
+
314
+ it "should work with absolute !code paths"
315
+
316
+ it "should replace !code macros with the contents of the referenced file in lib" do
317
+ @it.stub!(:read_from_lib).and_return("awesome javascript")
318
+
319
+ @it.
320
+ process_code_macro(" // !code foo/bar.js ").
321
+ should =~ /awesome javascript/
322
+ end
323
+
324
+ it "should not affect normal lines when processing macros" do
325
+ @it.
326
+ process_code_macro(" var foo = 'bar'; ").
327
+ should == " var foo = 'bar'; "
328
+ end
329
+
330
+ it "should find files with relative paths in __lib" do
331
+ File.
332
+ should_receive(:read).
333
+ with("fixtures/_design/__lib/foo.js")
334
+
335
+ @it.read_from_lib("foo.js")
336
+ end
337
+
338
+ end
339
+
340
+ context "saving a JS attribute" do
341
+ before(:each) do
342
+ @it = DesignDirectory.new("/tmp")
343
+
344
+ FileUtils.stub!(:mkdir_p)
345
+ @file = mock("File").as_null_object
346
+ File.stub!(:new).and_return(@file)
347
+ end
348
+
349
+ it "should not store _id" do
350
+ File.
351
+ should_not_receive(:new).
352
+ with("/tmp/_design/foo/_id.js", "w+")
353
+
354
+ @it.save_js(nil, "_design/foo", { "_id" => "_design/foo"})
355
+ end
356
+
357
+ it "should create map the design document attribute to the filesystem" do
358
+ FileUtils.
359
+ should_receive(:mkdir_p).
360
+ with("/tmp/_design/foo")
361
+
362
+ @it.save_js("_design/foo", "bar", "json")
363
+ end
364
+
365
+ it "should store the attribute to the filesystem" do
366
+ File.
367
+ should_receive(:new).
368
+ with("/tmp/_design/foo/bar.js", "w+")
369
+
370
+ @it.save_js("_design/foo", "bar", "json")
371
+ end
372
+
373
+ it "should store hash values to the filesystem" do
374
+ File.
375
+ should_receive(:new).
376
+ with("/tmp/_design/foo/bar/baz.js", "w+")
377
+
378
+ @it.save_js("_design/foo", "bar", { "baz" => "json" })
379
+ end
380
+
381
+ it "should store the attribute to the filesystem" do
382
+ @file.
383
+ should_receive(:write).
384
+ with("json")
385
+
386
+ @it.save_js("_design/foo", "bar", "json")
387
+ end
388
+
389
+ it "should store the attributes with slashes to the filesystem" do
390
+ File.
391
+ should_receive(:new).
392
+ with("/tmp/_design/foo/bar%2Fbaz.js", "w+")
393
+
394
+ @it.save_js("_design/foo", "bar/baz", "json")
395
+ end
396
+
397
+ it "should strip lib code when dumping" do
398
+ js = <<_JS
399
+ // !begin code foo.js
400
+ function foo () { return 'foo'; }
401
+ // !end code foo.js
402
+ // !begin code bar.js
403
+ function bar () { return 'bar'; }
404
+ // !end code bar.js
405
+ function baz () { return 'baz'; }
406
+ _JS
407
+
408
+ @it.
409
+ remove_code_macros(js).
410
+ should == "// !code foo.js\n" +
411
+ "// !code bar.js\n" +
412
+ "function baz () { return 'baz'; }\n"
413
+ end
276
414
  end
277
415
  end
278
416
 
@@ -297,15 +435,61 @@ describe CommandLine do
297
435
  end
298
436
 
299
437
  context "an instance that dumps a CouchDB database" do
300
- before(:each) do
438
+ it "should dump CouchDB documents from uri to dir when run" do
301
439
  @it = CommandLine.new(['dump', 'uri', 'dir'])
440
+
441
+ CouchDocs.
442
+ should_receive(:dump).
443
+ with("uri", "dir", nil)
444
+
445
+ @it.run
302
446
  end
303
447
 
304
- it "should dump CouchDB documents from uri to dir when run" do
448
+ it "should be able to dump only design documents" do
449
+ @it = CommandLine.new(['dump', 'uri', 'dir', '-d'])
450
+
305
451
  CouchDocs.
306
452
  should_receive(:dump).
307
- with("uri", "dir")
453
+ with("uri", "dir", :design)
454
+
455
+ @it.run
456
+ end
457
+
458
+ it "should be able to dump only regular documents" do
459
+ @it = CommandLine.new(['dump', 'uri', 'dir', '-D'])
460
+
461
+ CouchDocs.
462
+ should_receive(:dump).
463
+ with("uri", "dir", :doc)
464
+
465
+ @it.run
466
+ end
467
+ end
308
468
 
469
+ context "pushing" do
470
+ before(:each) do
471
+ CouchDocs.stub!(:put_dir)
472
+
473
+ @dw = mock("Directory Watcher").as_null_object
474
+ DirectoryWatcher.stub!(:new).and_return(@dw)
475
+ end
476
+
477
+ it "should know watch" do
478
+ @it = CommandLine.new(%w(push uri dir -w))
479
+ @it.options[:watch].should be_true
480
+ end
481
+
482
+ it "should run once normally" do
483
+ @dw.should_receive(:run_once)
484
+
485
+ @it = CommandLine.new(%w(push uri dir))
486
+ @it.run
487
+ end
488
+
489
+ it "should start a watcher with -w" do
490
+ @dw.should_receive(:start)
491
+
492
+ @it = CommandLine.new(%w(push uri dir -w))
309
493
  @it.run
310
494
  end
311
495
  end
@@ -315,9 +499,9 @@ describe CommandLine do
315
499
  @it = CommandLine.new(['load', 'dir', 'uri'])
316
500
  end
317
501
 
318
- it "should dump CouchDB documents from uri to dir when run" do
502
+ it "should load CouchDB documents from dir to uri when run" do
319
503
  CouchDocs.
320
- should_receive(:put_document_dir).
504
+ should_receive(:put_dir).
321
505
  with("uri", "dir")
322
506
 
323
507
  @it.run
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couch_docs
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Strom
@@ -9,58 +9,58 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-08-09 00:00:00 -04:00
12
+ date: 2010-03-11 00:00:00 -05:00
13
13
  default_executable: couch-docs
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
- name: json
16
+ name: rest-client
17
17
  type: :runtime
18
18
  version_requirement:
19
19
  version_requirements: !ruby/object:Gem::Requirement
20
20
  requirements:
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: "0"
23
+ version: 1.1.0
24
24
  version:
25
25
  - !ruby/object:Gem::Dependency
26
- name: rest-client
26
+ name: json
27
27
  type: :runtime
28
28
  version_requirement:
29
29
  version_requirements: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: "0"
33
+ version: 1.2.0
34
34
  version:
35
35
  - !ruby/object:Gem::Dependency
36
- name: rest-client
36
+ name: directory_watcher
37
37
  type: :runtime
38
38
  version_requirement:
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
- version: 1.0.3
43
+ version: 1.3.1
44
44
  version:
45
45
  - !ruby/object:Gem::Dependency
46
- name: json
47
- type: :runtime
46
+ name: bones
47
+ type: :development
48
48
  version_requirement:
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: 1.1.6
53
+ version: 2.5.0
54
54
  version:
55
55
  - !ruby/object:Gem::Dependency
56
- name: bones
56
+ name: rspec
57
57
  type: :development
58
58
  version_requirement:
59
59
  version_requirements: !ruby/object:Gem::Requirement
60
60
  requirements:
61
61
  - - ">="
62
62
  - !ruby/object:Gem::Version
63
- version: 2.5.1
63
+ version: "0"
64
64
  version:
65
65
  description: Manage CouchDB views and documents.
66
66
  email: chris@eeecooks.com
@@ -78,8 +78,10 @@ files:
78
78
  - Rakefile
79
79
  - bin/couch-docs
80
80
  - couch_docs.gemspec
81
+ - fixtures/_design/__lib/foo.js
81
82
  - fixtures/_design/a/b/c.js
82
83
  - fixtures/_design/a/b/d.js
84
+ - fixtures/_design/x/z.js
83
85
  - fixtures/bar.json
84
86
  - fixtures/foo.json
85
87
  - lib/couch_docs.rb
@@ -117,7 +119,7 @@ requirements: []
117
119
  rubyforge_project: couch_docs
118
120
  rubygems_version: 1.3.5
119
121
  signing_key:
120
- specification_version: 2
122
+ specification_version: 3
121
123
  summary: Manage CouchDB views and documents
122
124
  test_files:
123
125
  - test/test_couch_docs.rb