couch_docs 1.0.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 ADDED
@@ -0,0 +1,14 @@
1
+ == 1.0.0 / 2009-08-09
2
+
3
+ * Update the couch-docs script to be able to dump a CouchDB database
4
+ to a local directory as well as uploading a local directory into a
5
+ CouchDB database.
6
+
7
+ * CouchDB revision numbers are stripped when dumping (to prevent
8
+ conflicts when re-loading)
9
+
10
+ * Attachments are dumped as well.
11
+
12
+ == 0.9.0 / 2009-08-08
13
+
14
+ * Import from couch_design_docs (name change to reflect increased functionality)
data/README.rdoc ADDED
@@ -0,0 +1,93 @@
1
+ couch_docs
2
+ by Chris Strom
3
+ http://github.com/eee-c/couch_docs
4
+ (used to be couch_design_docs)
5
+
6
+ == DESCRIPTION:
7
+
8
+ Manage CouchDB views and documents.
9
+
10
+ == FEATURES/PROBLEMS:
11
+
12
+ * Upload JSON documents stored on the filesystem into a CouchDB
13
+ database.
14
+
15
+ * Map <tt>.js</tt> files stored on the filesystem
16
+ (e.g. <tt>_design/recipes/count_by_month/map.js</tt>) into CouchDB
17
+ design documents.
18
+
19
+ * Dump documents stored in CouchDB to the filesystem.
20
+
21
+ * Script (couch-docs) to restore / backup CouchDB database.
22
+
23
+ * The couch-docs scipts does not work with design documents.
24
+
25
+ * A progress bar would be helpful.
26
+
27
+ * Unit testing of view javascript would be very nice.
28
+
29
+ == SYNOPSIS:
30
+
31
+ From the command line:
32
+
33
+ # For dumping the contents of a CouchDB database to the filesystem
34
+ couch-docs dump "http://localhost:5984/db" path/to/dump_dir/
35
+
36
+ # For loading documents from the filesystem into CouchDB
37
+ couch-docs load path/to/dump_dir/ "http://localhost:5984/db"
38
+
39
+
40
+ In code:
41
+
42
+ DB_URL = "http://localhost:5984/db"
43
+ DIRECTORY = "/repos/db/couchdb/"
44
+
45
+ # /repos/db/couchdb/_design/lucene/transform.js
46
+ # /repos/db/couchdb/foo.json
47
+
48
+ CouchDocs.put_dir(DB_URL, DIRECTORY)
49
+
50
+ # => lucene design document with a "transform" function containing
51
+ # the contents of transform.js
52
+ # - AND -
53
+ # a document named "foo" with the JSON contents from the foo.json
54
+ # file
55
+
56
+ CouchDocs.dump(DB_URL, "/repos/db/bak")
57
+
58
+ # => JSON dump of every document at DB_URL
59
+
60
+ == REQUIREMENTS:
61
+
62
+ * CouchDB
63
+ * JSON
64
+ * RestClient
65
+
66
+ == INSTALL:
67
+
68
+ * sudo gem install eee-c-couch_docs
69
+
70
+ == LICENSE:
71
+
72
+ (The MIT License)
73
+
74
+ Copyright (c) 2009
75
+
76
+ Permission is hereby granted, free of charge, to any person obtaining
77
+ a copy of this software and associated documentation files (the
78
+ 'Software'), to deal in the Software without restriction, including
79
+ without limitation the rights to use, copy, modify, merge, publish,
80
+ distribute, sublicense, and/or sell copies of the Software, and to
81
+ permit persons to whom the Software is furnished to do so, subject to
82
+ the following conditions:
83
+
84
+ The above copyright notice and this permission notice shall be
85
+ included in all copies or substantial portions of the Software.
86
+
87
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
88
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
89
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
90
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
91
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
92
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
93
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+
5
+ begin
6
+ require 'bones'
7
+ Bones.setup
8
+ rescue LoadError
9
+ begin
10
+ load 'tasks/setup.rb'
11
+ rescue LoadError
12
+ raise RuntimeError, '### please install the "bones" gem ###'
13
+ end
14
+ end
15
+
16
+ ensure_in_path 'lib'
17
+ require 'couch_docs'
18
+
19
+ task :default => 'spec:run'
20
+
21
+ PROJ.name = 'couch_docs'
22
+ PROJ.authors = 'Chris Strom'
23
+ PROJ.email = 'chris@eeecooks.com'
24
+ PROJ.url = 'http://github.com/eee-c/couch_docs'
25
+ PROJ.version = CouchDocs::VERSION
26
+ PROJ.rubyforge.name = 'couch_docs'
27
+
28
+ PROJ.spec.opts << '--color'
29
+
30
+ PROJ.gem.dependencies = %w{json rest-client}
31
+
32
+ PROJ.readme_file = 'README.rdoc'
33
+
34
+ depend_on 'rest-client'
35
+ depend_on 'json'
36
+
37
+ # EOF
data/bin/couch-docs ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(
4
+ File.join(File.dirname(__FILE__), %w[.. lib couch_docs]))
5
+
6
+ # Put your code here
7
+
8
+ CouchDocs::CommandLine.run ARGV
9
+
10
+ # EOF
@@ -0,0 +1,49 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{couch_docs}
5
+ s.version = "1.0.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Chris Strom"]
9
+ s.date = %q{2009-08-09}
10
+ s.default_executable = %q{couch-docs}
11
+ s.description = %q{Manage CouchDB views and documents.}
12
+ s.email = %q{chris@eeecooks.com}
13
+ s.executables = ["couch-docs"]
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
17
+ s.homepage = %q{http://github.com/eee-c/couch_docs}
18
+ s.rdoc_options = ["--main", "README.rdoc"]
19
+ s.require_paths = ["lib"]
20
+ s.rubyforge_project = %q{couch_docs}
21
+ s.rubygems_version = %q{1.3.1}
22
+ s.summary = %q{Manage CouchDB views and documents}
23
+ s.test_files = ["test/test_couch_docs.rb"]
24
+
25
+ if s.respond_to? :specification_version then
26
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
27
+ s.specification_version = 2
28
+
29
+ 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"])
35
+ 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"])
41
+ end
42
+ 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"])
48
+ end
49
+ end
@@ -0,0 +1 @@
1
+ function(doc) { return true; }
@@ -0,0 +1 @@
1
+ function(doc) { return true; }
data/fixtures/bar.json ADDED
@@ -0,0 +1 @@
1
+ {"bar":"2"}
data/fixtures/foo.json ADDED
@@ -0,0 +1 @@
1
+ {"foo":"1"}
@@ -0,0 +1,28 @@
1
+ module CouchDocs
2
+ class CommandLine
3
+ def self.run(*args)
4
+ CommandLine.new(*args).run
5
+ end
6
+
7
+ attr_accessor :command, :options
8
+
9
+ def initialize(args)
10
+ @command = args.shift
11
+ @options = args
12
+ end
13
+
14
+ def run
15
+ case command
16
+ 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}")
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,46 @@
1
+ class Hash
2
+ def deep_merge(other)
3
+ self.merge(other) do |key, oldval, newval|
4
+ oldval.deep_merge(newval)
5
+ end
6
+ end
7
+ end
8
+
9
+ module CouchDocs
10
+ class DesignDirectory
11
+
12
+ attr_accessor :couch_view_dir
13
+
14
+ def self.a_to_hash(a)
15
+ key = a.first
16
+ if (a.length > 2)
17
+ { key => a_to_hash(a[1,a.length]) }
18
+ else
19
+ { key => a.last }
20
+ end
21
+ end
22
+
23
+ def initialize(path)
24
+ Dir.new(path) # Just checkin'
25
+ @couch_view_dir = path
26
+ end
27
+
28
+ def to_hash
29
+ Dir["#{couch_view_dir}/**/*.js"].inject({}) do |memo, filename|
30
+ DesignDirectory.
31
+ a_to_hash(expand_file(filename)).
32
+ deep_merge(memo)
33
+ end
34
+ end
35
+
36
+ def expand_file(filename)
37
+ File.dirname(filename).
38
+ gsub(/#{couch_view_dir}\/?/, '').
39
+ split(/\//) +
40
+ [
41
+ File.basename(filename, '.js'),
42
+ File.new(filename).read
43
+ ]
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,25 @@
1
+ module CouchDocs
2
+ class DocumentDirectory
3
+
4
+ attr_accessor :couch_doc_dir
5
+
6
+ def initialize(path)
7
+ Dir.new(path)
8
+ @couch_doc_dir = path
9
+ end
10
+
11
+ def each_document
12
+ Dir["#{couch_doc_dir}/*.json"].each do |filename|
13
+ yield [ File.basename(filename, '.json'),
14
+ JSON.parse(File.new(filename).read) ]
15
+
16
+ end
17
+ end
18
+
19
+ def store_document(doc)
20
+ file = File.new("#{couch_doc_dir}/#{doc['_id']}.json", "w+")
21
+ file.write(doc.to_json)
22
+ file.close
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,64 @@
1
+ require 'rubygems'
2
+ require 'restclient'
3
+ require 'json'
4
+
5
+ module CouchDocs
6
+ class Store
7
+ include Enumerable
8
+
9
+ attr_accessor :url
10
+
11
+ # Initialize a CouchDB store object. Requires a URL for the
12
+ # target CouchDB database.
13
+ #
14
+ def initialize(url)
15
+ @url = url
16
+ end
17
+
18
+ # Loads all supplied design documents in the current store.
19
+ # Given a hash <tt>h</tt>, the keys being the CouchDB document
20
+ # name and values of design documents
21
+ #
22
+ def put_design_documents(h)
23
+ h.each_pair do |document_name, doc|
24
+ Store.put!("#{url}/_design/#{document_name}", doc)
25
+ end
26
+ end
27
+
28
+ # Create or replace the document located at <tt>path</tt> with the
29
+ # Hash document <tt>doc</tt>
30
+ #
31
+ def self.put!(path, doc)
32
+ self.put(path, doc)
33
+ rescue RestClient::RequestFailed
34
+ self.delete_and_put(path, doc)
35
+ end
36
+
37
+ def self.delete_and_put(path, doc)
38
+ self.delete(path)
39
+ self.put(path, doc)
40
+ end
41
+
42
+ def self.put(path, doc)
43
+ RestClient.put path,
44
+ doc.to_json,
45
+ :content_type => 'application/json'
46
+ end
47
+
48
+ def self.delete(path)
49
+ # retrieve existing to obtain the revision
50
+ old = self.get(path)
51
+ RestClient.delete(path + "?rev=#{old['_rev']}")
52
+ end
53
+
54
+ def self.get(path)
55
+ JSON.parse(RestClient.get(path))
56
+ end
57
+
58
+ def each
59
+ Store.get("#{url}/_all_docs")['rows'].each do |rec|
60
+ yield Store.get("#{url}/#{rec['id']}?attachments=true")
61
+ end
62
+ end
63
+ end
64
+ end
data/lib/couch_docs.rb ADDED
@@ -0,0 +1,94 @@
1
+ module CouchDocs
2
+
3
+ # :stopdoc:
4
+ VERSION = '1.0.0'
5
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
+ # :startdoc:
8
+
9
+ # Returns the version string for the library.
10
+ #
11
+ def self.version
12
+ VERSION
13
+ end
14
+
15
+ # For a CouchDB database described by <tt>db_uri</tt> and a
16
+ # directory, <tt>dir</tt> containing design documents, creates
17
+ # design documents in the CouchDB database
18
+ #
19
+ def self.put_dir(db_uri, dir)
20
+ self.put_design_dir(db_uri, "#{dir}/_design")
21
+ self.put_document_dir(db_uri, dir)
22
+ end
23
+
24
+ # Alias for <tt>put_dir</tt>
25
+ def self.upload_dir(db_uri, dir)
26
+ self.put_dir(db_uri, dir)
27
+ end
28
+
29
+ # Upload design documents from <tt>dir</tt> to the CouchDB database
30
+ # located at <tt>db_uri</tt>
31
+ #
32
+ def self.put_design_dir(db_uri, dir)
33
+ store = Store.new(db_uri)
34
+ dir = DesignDirectory.new(dir)
35
+ store.put_design_documents(dir.to_hash)
36
+ end
37
+
38
+ # Upload documents from <tt>dir</tt> to the CouchDB database
39
+ # located at <tt>db_uri</tt>
40
+ #
41
+ def self.put_document_dir(db_uri, dir)
42
+ store = Store.new(db_uri)
43
+ dir = DocumentDirectory.new(dir)
44
+ dir.each_document do |name, contents|
45
+ Store.put!("#{db_uri}/#{name}", contents)
46
+ end
47
+ end
48
+
49
+ # Dump all documents located at <tt>db_uri</tt> into the directory
50
+ # <tt>dir</tt>
51
+ #
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) }
59
+ end
60
+
61
+ # Returns the library path for the module. If any arguments are given,
62
+ # they will be joined to the end of the libray path using
63
+ # <tt>File.join</tt>.
64
+ #
65
+ def self.libpath( *args )
66
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
67
+ end
68
+
69
+ # Returns the lpath for the module. If any arguments are given,
70
+ # they will be joined to the end of the path using
71
+ # <tt>File.join</tt>.
72
+ #
73
+ def self.path( *args )
74
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
75
+ end
76
+
77
+ # Utility method used to require all files ending in .rb that lie in the
78
+ # directory below this file that has the same name as the filename passed
79
+ # in. Optionally, a specific _directory_ name can be passed in such that
80
+ # the _filename_ does not have to be equivalent to the directory.
81
+ #
82
+ def self.require_all_libs_relative_to( fname, dir = nil )
83
+ dir ||= ::File.basename(fname, '.*')
84
+ search_me = ::File.expand_path(
85
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
86
+
87
+ Dir.glob(search_me).sort.each {|rb| require rb}
88
+ end
89
+
90
+ end # module CouchDocs
91
+
92
+ CouchDocs.require_all_libs_relative_to(__FILE__)
93
+
94
+ # EOF
@@ -0,0 +1,329 @@
1
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
2
+
3
+ describe CouchDocs do
4
+ it "should be able to load design and normal documents" do
5
+ CouchDocs.
6
+ should_receive(:put_design_dir).
7
+ with("uri", "fixtures/_design")
8
+
9
+ CouchDocs.
10
+ should_receive(:put_document_dir).
11
+ with("uri", "fixtures")
12
+
13
+ CouchDocs.put_dir("uri", "fixtures")
14
+ end
15
+
16
+ it "should be able to load directory/JS files into CouchDB as design docs" do
17
+ store = mock("Store")
18
+ Store.stub!(:new).and_return(store)
19
+
20
+ dir = mock("Design Directory")
21
+ dir.stub!(:to_hash).and_return({ "foo" => "bar" })
22
+ DesignDirectory.stub!(:new).and_return(dir)
23
+
24
+ store.
25
+ should_receive(:put_design_documents).
26
+ with({ "foo" => "bar" })
27
+
28
+ CouchDocs.put_design_dir("uri", "fixtures")
29
+ end
30
+
31
+ it "should be able to load documents into CouchDB" do
32
+ store = mock("Store")
33
+ Store.stub!(:new).and_return(store)
34
+
35
+ dir = mock("Document Directory")
36
+ dir.
37
+ stub!(:each_document).
38
+ and_yield('foo', {"foo" => "1"})
39
+
40
+ DocumentDirectory.stub!(:new).and_return(dir)
41
+
42
+ Store.
43
+ should_receive(:put!).
44
+ with('uri/foo', {"foo" => "1"})
45
+
46
+ CouchDocs.put_document_dir("uri", "fixtures")
47
+ end
48
+
49
+ context "dumping CouchDB documents to a directory" do
50
+ before(:each) do
51
+ @store = mock("Store")
52
+ Store.stub!(:new).and_return(@store)
53
+
54
+ @dir = mock("Document Directory")
55
+ DocumentDirectory.stub!(:new).and_return(@dir)
56
+ end
57
+ it "should be able to store all CouchDB documents on the filesystem" do
58
+ @store.stub!(:map).and_return([{'_id' => 'foo'}])
59
+ @dir.
60
+ should_receive(:store_document).
61
+ with({'_id' => 'foo'})
62
+
63
+ CouchDocs.dump("uri", "fixtures")
64
+ end
65
+ it "should ignore design documents" do
66
+ @store.stub!(:map).and_return([{'_id' => '_design/foo'}])
67
+ @dir.
68
+ should_not_receive(:store_document)
69
+
70
+ CouchDocs.dump("uri", "fixtures")
71
+ end
72
+ it "should strip revision numbers" do
73
+ @store.stub!(:map).
74
+ and_return([{'_id' => 'foo', '_rev' => '1-1234'}])
75
+ @dir.
76
+ should_receive(:store_document).
77
+ with({'_id' => 'foo'})
78
+
79
+ CouchDocs.dump("uri", "fixtures")
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+ describe Store do
86
+ it "should require a CouchDB URL Root for instantiation" do
87
+ lambda { Store.new }.
88
+ should raise_error
89
+
90
+ lambda { Store.new("uri") }.
91
+ should_not raise_error
92
+ end
93
+
94
+ context "a valid store" do
95
+ before(:each) do
96
+ @it = Store.new("uri")
97
+
98
+ @hash = {
99
+ 'a' => {
100
+ 'b' => {
101
+ 'c' => 'function(doc) { return true; }'
102
+ }
103
+ }
104
+ }
105
+ end
106
+
107
+ it "should be able to put a new document" do
108
+ Store.
109
+ should_receive(:put).
110
+ with("uri", { })
111
+
112
+ Store.put!("uri", { })
113
+ end
114
+
115
+ it "should delete existing docs if first put fails" do
116
+ Store.
117
+ stub!(:put).
118
+ and_raise(RestClient::RequestFailed)
119
+
120
+ Store.
121
+ should_receive(:delete_and_put).
122
+ with("uri", { })
123
+
124
+ Store.put!("uri", { })
125
+ end
126
+
127
+ it "should be able to delete and put" do
128
+ Store.
129
+ should_receive(:delete).
130
+ with("uri")
131
+
132
+ Store.
133
+ should_receive(:put).
134
+ with("uri", { })
135
+
136
+ Store.delete_and_put("uri", { })
137
+ end
138
+
139
+ it "should be able to load a hash into design docs" do
140
+ RestClient.
141
+ should_receive(:put).
142
+ with("uri/_design/a",
143
+ '{"b":{"c":"function(doc) { return true; }"}}',
144
+ :content_type => 'application/json')
145
+ @it.put_design_documents(@hash)
146
+ end
147
+
148
+ it "should be able to retrieve an existing document" do
149
+ RestClient.
150
+ stub!(:get).
151
+ and_return('{"_rev":"1234"}')
152
+
153
+ Store.get("uri").should == { '_rev' => "1234" }
154
+ end
155
+
156
+ it "should be able to delete an existing document" do
157
+ Store.stub!(:get).and_return({ '_rev' => '1234' })
158
+
159
+ RestClient.
160
+ should_receive(:delete).
161
+ with("uri?rev=1234")
162
+
163
+ Store.delete("uri")
164
+ end
165
+
166
+ it "should be able to load each document" do
167
+ Store.stub!(:get).
168
+ with("uri/_all_docs").
169
+ and_return({ "total_rows" => 2,
170
+ "offset" => 0,
171
+ "rows" => [{"id"=>"1", "value"=>{}, "key"=>"1"},
172
+ {"id"=>"2", "value"=>{}, "key"=>"2"}]})
173
+
174
+ Store.stub!(:get).with("uri/1?attachments=true")
175
+ Store.should_receive(:get).with("uri/2?attachments=true")
176
+
177
+ @it.each { }
178
+ end
179
+ end
180
+ end
181
+
182
+ describe DocumentDirectory do
183
+ it "should require a root directory for instantiation" do
184
+ lambda { DocumentDirectory.new }.
185
+ should raise_error
186
+
187
+ lambda { DocumentDirectory.new("foo") }.
188
+ should raise_error
189
+
190
+ lambda { DocumentDirectory.new("fixtures")}.
191
+ should_not raise_error
192
+ end
193
+
194
+ context "a valid directory" do
195
+ before(:each) do
196
+ @it = DocumentDirectory.new("fixtures")
197
+ end
198
+
199
+ it "should be able to iterate over the documents" do
200
+ everything = []
201
+ @it.each_document do |name, contents|
202
+ everything << [name, contents]
203
+ end
204
+ everything.
205
+ should == [['bar', {"bar" => "2"}],
206
+ ['foo', {"foo" => "1"}]]
207
+ end
208
+
209
+ it "should be able to store a document" do
210
+ file = mock("File", :write => 42, :close => true)
211
+ File.
212
+ should_receive(:new).
213
+ with("fixtures/foo.json", "w+").
214
+ and_return(file)
215
+
216
+ @it.store_document({'_id' => 'foo'})
217
+ end
218
+
219
+ it "should be able to save a document as JSON" do
220
+ file = mock("File", :close => true)
221
+ File.stub!(:new).and_return(file)
222
+
223
+ file.should_receive(:write).with(%Q|{"_id":"foo"}|)
224
+
225
+ @it.store_document({'_id' => 'foo'})
226
+ end
227
+ end
228
+ end
229
+
230
+ describe DesignDirectory do
231
+ it "should require a root directory for instantiation" do
232
+ lambda { DesignDirectory.new }.
233
+ should raise_error
234
+
235
+ lambda { DesignDirectory.new("foo") }.
236
+ should raise_error
237
+
238
+ lambda { DesignDirectory.new("fixtures/_design")}.
239
+ should_not raise_error
240
+ end
241
+
242
+ it "should convert arrays into deep hashes" do
243
+ DesignDirectory.
244
+ a_to_hash(%w{a b c d}).
245
+ should == {
246
+ 'a' => {
247
+ 'b' => {
248
+ 'c' => 'd'
249
+ }
250
+ }
251
+ }
252
+ end
253
+
254
+ context "a valid directory" do
255
+ before(:each) do
256
+ @it = DesignDirectory.new("fixtures/_design")
257
+ end
258
+
259
+ it "should list dirs, basename and contents of a file" do
260
+ @it.expand_file("fixtures/_design/a/b/c.js").
261
+ should == ['a', 'b', 'c', 'function(doc) { return true; }']
262
+ end
263
+
264
+ it "should assemble all documents into a single docs structure" do
265
+ @it.to_hash.
266
+ should == {
267
+ 'a' => {
268
+ 'b' => {
269
+ 'c' => 'function(doc) { return true; }',
270
+ 'd' => 'function(doc) { return true; }'
271
+ }
272
+ }
273
+
274
+ }
275
+ end
276
+ end
277
+ end
278
+
279
+ describe CommandLine do
280
+ it "should be able to run a single instance of a command line" do
281
+ CommandLine.
282
+ should_receive(:new).
283
+ with('foo', 'bar').
284
+ and_return(mock("Command Line").as_null_object)
285
+
286
+ CommandLine.run('foo', 'bar')
287
+ end
288
+
289
+ it "should run the command line instance" do
290
+ command_line = mock("Command Line").as_null_object
291
+ command_line.
292
+ should_receive(:run)
293
+
294
+ CommandLine.stub!(:new).and_return(command_line)
295
+
296
+ CommandLine.run('foo', 'bar')
297
+ end
298
+
299
+ context "an instance that dumps a CouchDB database" do
300
+ before(:each) do
301
+ @it = CommandLine.new(['dump', 'uri', 'dir'])
302
+ end
303
+
304
+ it "should dump CouchDB documents from uri to dir when run" do
305
+ CouchDocs.
306
+ should_receive(:dump).
307
+ with("uri", "dir")
308
+
309
+ @it.run
310
+ end
311
+ end
312
+
313
+ context "an instance that uploads to a CouchDB database" do
314
+ before(:each) do
315
+ @it = CommandLine.new(['load', 'dir', 'uri'])
316
+ end
317
+
318
+ it "should dump CouchDB documents from uri to dir when run" do
319
+ CouchDocs.
320
+ should_receive(:put_document_dir).
321
+ with("uri", "dir")
322
+
323
+ @it.run
324
+ end
325
+ end
326
+
327
+ end
328
+
329
+ # EOF
@@ -0,0 +1,18 @@
1
+
2
+ require File.expand_path(
3
+ File.join(File.dirname(__FILE__), %w[.. lib couch_docs]))
4
+
5
+ include CouchDocs
6
+
7
+ Spec::Runner.configure do |config|
8
+ # == Mock Framework
9
+ #
10
+ # RSpec uses it's own mocking framework by default. If you prefer to
11
+ # use mocha, flexmock or RR, uncomment the appropriate line:
12
+ #
13
+ # config.mock_with :mocha
14
+ # config.mock_with :flexmock
15
+ # config.mock_with :rr
16
+ end
17
+
18
+ # EOF
File without changes
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: couch_docs
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Strom
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-09 00:00:00 -04:00
13
+ default_executable: couch-docs
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: json
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: rest-client
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: rest-client
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.0.3
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: json
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.1.6
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: bones
57
+ type: :development
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 2.5.1
64
+ version:
65
+ description: Manage CouchDB views and documents.
66
+ email: chris@eeecooks.com
67
+ executables:
68
+ - couch-docs
69
+ extensions: []
70
+
71
+ extra_rdoc_files:
72
+ - History.txt
73
+ - README.rdoc
74
+ - bin/couch-docs
75
+ files:
76
+ - History.txt
77
+ - README.rdoc
78
+ - Rakefile
79
+ - bin/couch-docs
80
+ - couch_docs.gemspec
81
+ - fixtures/_design/a/b/c.js
82
+ - fixtures/_design/a/b/d.js
83
+ - fixtures/bar.json
84
+ - fixtures/foo.json
85
+ - lib/couch_docs.rb
86
+ - lib/couch_docs/command_line.rb
87
+ - lib/couch_docs/design_directory.rb
88
+ - lib/couch_docs/document_directory.rb
89
+ - lib/couch_docs/store.rb
90
+ - spec/couch_docs_spec.rb
91
+ - spec/spec_helper.rb
92
+ - test/test_couch_docs.rb
93
+ has_rdoc: true
94
+ homepage: http://github.com/eee-c/couch_docs
95
+ licenses: []
96
+
97
+ post_install_message:
98
+ rdoc_options:
99
+ - --main
100
+ - README.rdoc
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: "0"
108
+ version:
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: "0"
114
+ version:
115
+ requirements: []
116
+
117
+ rubyforge_project: couch_docs
118
+ rubygems_version: 1.3.5
119
+ signing_key:
120
+ specification_version: 2
121
+ summary: Manage CouchDB views and documents
122
+ test_files:
123
+ - test/test_couch_docs.rb