couch_docs 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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