sr-couchy 0.0.2
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/README.textile +77 -0
- data/Rakefile +46 -0
- data/bin/couchy +80 -0
- data/couchy.gemspec +58 -0
- data/lib/couchy.rb +21 -0
- data/lib/couchy/database.rb +129 -0
- data/lib/couchy/server.rb +89 -0
- data/spec/couchy_spec.rb +71 -0
- data/spec/database_spec.rb +417 -0
- data/spec/fixtures/attachments/test.html +11 -0
- data/spec/fixtures/views/lib.js +3 -0
- data/spec/fixtures/views/test_view/lib.js +3 -0
- data/spec/fixtures/views/test_view/only-map.js +4 -0
- data/spec/fixtures/views/test_view/test-map.js +3 -0
- data/spec/fixtures/views/test_view/test-reduce.js +3 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +5 -0
- data/test/couchy_test.rb +13 -0
- data/test/database_test.rb +193 -0
- data/test/server_test.rb +211 -0
- data/test/test_helper.rb +10 -0
- data/vendor/addressable/.gitignore +7 -0
- data/vendor/addressable/CHANGELOG +51 -0
- data/vendor/addressable/LICENSE +20 -0
- data/vendor/addressable/README +24 -0
- data/vendor/addressable/Rakefile +51 -0
- data/vendor/addressable/lib/addressable/idna.rb +4867 -0
- data/vendor/addressable/lib/addressable/uri.rb +2212 -0
- data/vendor/addressable/lib/addressable/version.rb +35 -0
- data/vendor/addressable/spec/addressable/idna_spec.rb +196 -0
- data/vendor/addressable/spec/addressable/uri_spec.rb +3827 -0
- data/vendor/addressable/spec/data/rfc3986.txt +3419 -0
- data/vendor/addressable/tasks/clobber.rake +2 -0
- data/vendor/addressable/tasks/gem.rake +62 -0
- data/vendor/addressable/tasks/git.rake +40 -0
- data/vendor/addressable/tasks/metrics.rake +22 -0
- data/vendor/addressable/tasks/rdoc.rake +29 -0
- data/vendor/addressable/tasks/rubyforge.rake +89 -0
- data/vendor/addressable/tasks/spec.rake +107 -0
- data/vendor/addressable/website/index.html +107 -0
- metadata +113 -0
data/README.textile
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
h1. Couchy: There Are Many Like It but This One is Mine
|
2
|
+
|
3
|
+
Couchy is a simple, no-frills, Ruby wrapper around the nice
|
4
|
+
"CouchDB HTTP API":http://wiki.apache.org/couchdb/HttpRestApt.
|
5
|
+
|
6
|
+
h2. Quick overview
|
7
|
+
|
8
|
+
<pre>
|
9
|
+
server = CouchRest.new
|
10
|
+
database = server.database('articles')
|
11
|
+
response = database.save(:title => 'Atom-Powered Robots Run Amok', :content => 'Some text.')
|
12
|
+
document = database.get(response['id'])
|
13
|
+
database.delete(document)
|
14
|
+
puts document.inspect
|
15
|
+
</pre>
|
16
|
+
|
17
|
+
|
18
|
+
h2. Requirements
|
19
|
+
|
20
|
+
The fellowing gems are required:
|
21
|
+
|
22
|
+
* @rest-client@
|
23
|
+
* @addressable@
|
24
|
+
|
25
|
+
To run the test and generate the code coverage report, you also need :
|
26
|
+
|
27
|
+
* @test/spec@
|
28
|
+
* @mocha@
|
29
|
+
* @rcov@
|
30
|
+
* @rspec@ (for the legacy tests)
|
31
|
+
|
32
|
+
and @yard@ to generate the documentation.
|
33
|
+
|
34
|
+
h2. Genesis
|
35
|
+
|
36
|
+
It started as an Hardcore Forking Action of "jchris's CouchRest":original
|
37
|
+
I ended up:
|
38
|
+
|
39
|
+
* Writing more tests. The legacy tests are rather integration tests while those
|
40
|
+
I wrote are unit tests.
|
41
|
+
* DRY-ing it up
|
42
|
+
* Writing some doc using "YARD":yard
|
43
|
+
|
44
|
+
Unfortunately, jchris didn't merge the changes for some reasons.
|
45
|
+
|
46
|
+
Thank a lot to him for having written CouchRest in the first place and having
|
47
|
+
allowed me to relicense my fork under another license.
|
48
|
+
|
49
|
+
h2. License
|
50
|
+
|
51
|
+
(The MIT License)
|
52
|
+
|
53
|
+
Copyright (c) 2008 "Simon Rozet":sr <simon@rozet.name>
|
54
|
+
|
55
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
56
|
+
a copy of this software and associated documentation files (the
|
57
|
+
'Software'), to deal in the Software without restriction, including
|
58
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
59
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
60
|
+
permit persons to whom the Software is furnished to do so, subject to
|
61
|
+
the following conditions:
|
62
|
+
|
63
|
+
The above copyright notice and this permission notice shall be
|
64
|
+
included in all copies or substantial portions of the Software.
|
65
|
+
|
66
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
67
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
68
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
69
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
70
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
71
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
72
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
73
|
+
|
74
|
+
[yard]http://yard.soen.ca/
|
75
|
+
[couchdb]http://couchdb.org
|
76
|
+
[original]http://github.com/jchris/couchrest
|
77
|
+
[sr]http://purl.org/net/sr/
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
require 'rcov/rcovtask'
|
4
|
+
require 'yard'
|
5
|
+
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
task :test => :"test:all"
|
9
|
+
|
10
|
+
namespace :test do
|
11
|
+
desc 'Run all tests'
|
12
|
+
task :all => [:unit, :integration]
|
13
|
+
|
14
|
+
desc 'Run unit tests'
|
15
|
+
task :unit do
|
16
|
+
sh 'testrb test/*.rb'
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "Run integration tests"
|
20
|
+
Spec::Rake::SpecTask.new('integration') do |t|
|
21
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Rcov::RcovTask.new do |t|
|
26
|
+
t.test_files = FileList['test/*_test.rb']
|
27
|
+
t.rcov_opts << '-Ilib'
|
28
|
+
t.rcov_opts << '-x"home"'
|
29
|
+
t.verbose = true
|
30
|
+
end
|
31
|
+
|
32
|
+
YARD::Rake::YardocTask.new
|
33
|
+
|
34
|
+
task :publish => [:"publish:doc", :"publish:coverage"]
|
35
|
+
|
36
|
+
namespace :publish do
|
37
|
+
task :doc => :yardoc do
|
38
|
+
sh 'cp -r doc ~/web/atonie.org/2008/couchy'
|
39
|
+
sh 'cd ~/web/atonie.org && git add 2008/couchy/doc && git commit -m "update couchy doc"'
|
40
|
+
end
|
41
|
+
|
42
|
+
task :coverage => :rcov do
|
43
|
+
sh 'cp -r coverage ~/web/atonie.org/2008/couchy'
|
44
|
+
sh 'cd ~/web/atonie.org && git add 2008/couchy/coverage && git commit -m "update couchy coverage"'
|
45
|
+
end
|
46
|
+
end
|
data/bin/couchy
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'thor'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
require File.dirname(__FILE__) + '/../lib/couchy'
|
8
|
+
|
9
|
+
module Couchy
|
10
|
+
class ViewManager < Thor
|
11
|
+
desc 'generate DESIGN VIEW',
|
12
|
+
'Generate directory structure for DESIGN/VIEW in PATH suitable for the `push` task.'
|
13
|
+
def generate(path, design, view='all')
|
14
|
+
path = File.join(Dir.pwd, path, design)
|
15
|
+
FileUtils.mkdir_p path
|
16
|
+
create_sample_view(path, view)
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'push PATH DATABASE', 'Push views avalaible in PATH to DATABASE.'
|
20
|
+
method_options :server => :optional,
|
21
|
+
:force => :boolean
|
22
|
+
def push(path, database_name)
|
23
|
+
@database_name = database_name
|
24
|
+
designs = find_designs(path)
|
25
|
+
documents = designs.map { |design| document_for(*design) }
|
26
|
+
documents.each do |document|
|
27
|
+
if document_exists?(document) && options[:force]
|
28
|
+
puts "Deleting `#{document['_id']}'"
|
29
|
+
database.delete database.get(document['_id'])
|
30
|
+
end
|
31
|
+
puts "Saving `#{document['_id']}' to `#{database.name}' on `#{server.uri}'"
|
32
|
+
database.save(document)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def find_designs(path)
|
38
|
+
views = Dir[File.join(path, '**/*-*.js')].inject({}) do |memo, path|
|
39
|
+
path = Pathname.new(path)
|
40
|
+
parts = path.split
|
41
|
+
design = parts.first.split.last.to_s
|
42
|
+
view = parts.last.to_s
|
43
|
+
view_type = view[/\-(\w+)\.js/] && $1
|
44
|
+
view_body = File.read(path)
|
45
|
+
memo.tap do |memo|
|
46
|
+
memo[design] = {} unless memo[design]
|
47
|
+
memo[design][view_type] = view_body
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def document_for(name, views)
|
53
|
+
{ '_id' => "_design/#{name}",
|
54
|
+
'language' => 'javascript',
|
55
|
+
'views' => views }
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_sample_view(path, view)
|
59
|
+
File.open(File.join(path, "#{view}-map.js"), 'w') do |file|
|
60
|
+
file.puts 'function(doc) {'
|
61
|
+
file.puts ' emit(null, doc);'
|
62
|
+
file.puts '}'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def document_exists?(document)
|
67
|
+
database.documents['rows'].collect { |row| row['id'] }.include?(document['_id'])
|
68
|
+
end
|
69
|
+
|
70
|
+
def database
|
71
|
+
@database ||= server.database(@database_name)
|
72
|
+
end
|
73
|
+
|
74
|
+
def server
|
75
|
+
@server ||= options[:server] ? Couchy::Server.new(options[:server]) : Couchy.new
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
Couchy::ViewManager.start
|
data/couchy.gemspec
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'couchy'
|
3
|
+
s.version = '0.0.2'
|
4
|
+
s.date = '2008-10-07'
|
5
|
+
s.summary = 'Simple, no-frills, Ruby wrapper around the nice CouchDB HTTP API'
|
6
|
+
s.description = 'Simple, no-frills, Ruby wrapper around the nice CouchDB HTTP API'
|
7
|
+
s.homepage = 'http://github.com/sr/couchy'
|
8
|
+
s.email = 'simon@rozet.name'
|
9
|
+
s.authors = ['Simon Rozet']
|
10
|
+
s.has_rdoc = false
|
11
|
+
s.executables = ['couchy']
|
12
|
+
|
13
|
+
s.files = %w(
|
14
|
+
README.textile
|
15
|
+
Rakefile
|
16
|
+
bin/couchy
|
17
|
+
couchy.gemspec
|
18
|
+
lib/couchy.rb
|
19
|
+
lib/couchy/database.rb
|
20
|
+
lib/couchy/server.rb
|
21
|
+
spec/couchy_spec.rb
|
22
|
+
spec/database_spec.rb
|
23
|
+
spec/fixtures/attachments/test.html
|
24
|
+
spec/fixtures/views/lib.js
|
25
|
+
spec/fixtures/views/test_view/lib.js
|
26
|
+
spec/fixtures/views/test_view/only-map.js
|
27
|
+
spec/fixtures/views/test_view/test-map.js
|
28
|
+
spec/fixtures/views/test_view/test-reduce.js
|
29
|
+
spec/spec.opts
|
30
|
+
spec/spec_helper.rb
|
31
|
+
test/couchy_test.rb
|
32
|
+
test/database_test.rb
|
33
|
+
test/server_test.rb
|
34
|
+
test/test_helper.rb
|
35
|
+
vendor/addressable/.gitignore
|
36
|
+
vendor/addressable/CHANGELOG
|
37
|
+
vendor/addressable/LICENSE
|
38
|
+
vendor/addressable/README
|
39
|
+
vendor/addressable/Rakefile
|
40
|
+
vendor/addressable/lib/addressable/idna.rb
|
41
|
+
vendor/addressable/lib/addressable/uri.rb
|
42
|
+
vendor/addressable/lib/addressable/version.rb
|
43
|
+
vendor/addressable/spec/addressable/idna_spec.rb
|
44
|
+
vendor/addressable/spec/addressable/uri_spec.rb
|
45
|
+
vendor/addressable/spec/data/rfc3986.txt
|
46
|
+
vendor/addressable/tasks/clobber.rake
|
47
|
+
vendor/addressable/tasks/gem.rake
|
48
|
+
vendor/addressable/tasks/git.rake
|
49
|
+
vendor/addressable/tasks/metrics.rake
|
50
|
+
vendor/addressable/tasks/rdoc.rake
|
51
|
+
vendor/addressable/tasks/rubyforge.rake
|
52
|
+
vendor/addressable/tasks/spec.rake
|
53
|
+
vendor/addressable/website/index.html
|
54
|
+
)
|
55
|
+
|
56
|
+
s.test_files = s.files.select { |path| path =~ /^(test|spec)/ }
|
57
|
+
s.add_dependency('rest-client', ['> 0.0.0'])
|
58
|
+
end
|
data/lib/couchy.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'json'
|
3
|
+
require 'rest_client'
|
4
|
+
|
5
|
+
$:.unshift File.dirname(__FILE__) + '/../vendor/addressable/lib'
|
6
|
+
require 'addressable/uri'
|
7
|
+
|
8
|
+
$:.unshift File.dirname(__FILE__) + '/couchy'
|
9
|
+
|
10
|
+
module Couchy
|
11
|
+
autoload :Server, 'server'
|
12
|
+
autoload :Database, 'database'
|
13
|
+
|
14
|
+
# Shortcut for Couchy::Server.new
|
15
|
+
#
|
16
|
+
# @param [String] uri The URI of the CouchDB server. defaults to "http://localhost:5984/"
|
17
|
+
# @return Couchy::Server
|
18
|
+
def self.new(server_uri='http://localhost:5984/')
|
19
|
+
Server.new(server_uri)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module Couchy
|
5
|
+
class Database
|
6
|
+
attr_accessor :server, :name
|
7
|
+
|
8
|
+
def initialize(server, database_name)
|
9
|
+
@name = database_name
|
10
|
+
@server = server
|
11
|
+
end
|
12
|
+
|
13
|
+
# Gets a list of all the documents available in the database
|
14
|
+
#
|
15
|
+
# @param [Hash] params
|
16
|
+
#
|
17
|
+
# @return [Hash] Parsed server response
|
18
|
+
def documents(params={})
|
19
|
+
server.get("#{name}/_all_docs", params)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Creates a temporary view
|
23
|
+
#
|
24
|
+
# @param [String] function The view function
|
25
|
+
# @param [Hash] params
|
26
|
+
#
|
27
|
+
# @return [Hash] Parsed server response
|
28
|
+
def temp_view(function, params={})
|
29
|
+
server.post("#{name}/_temp_view", function,
|
30
|
+
params.merge!(:headers => {'Content-Type' => 'application/json'}))
|
31
|
+
end
|
32
|
+
|
33
|
+
# Query a view
|
34
|
+
#
|
35
|
+
# @param [String] view_name The name of the view
|
36
|
+
# @param [Hash] params
|
37
|
+
#
|
38
|
+
# @return [Hash] Parsed server response
|
39
|
+
def view(view_name, params={})
|
40
|
+
server.get("#{name}/_view/#{view_name}", params)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Retrieves a document by its ID
|
44
|
+
#
|
45
|
+
# @param [String] id The ID of the document
|
46
|
+
#
|
47
|
+
# @return [Hash] Parsed server response
|
48
|
+
def get(id)
|
49
|
+
server.get("#{name}/#{CGI.escape(id)}")
|
50
|
+
end
|
51
|
+
|
52
|
+
# Retrieves an attachment from a document
|
53
|
+
#
|
54
|
+
# @param [String] document_id The ID of the document
|
55
|
+
# @param [String] attachment_name The name of the attachment
|
56
|
+
#
|
57
|
+
# @return [Hash] Parsed server response
|
58
|
+
def fetch_attachment(document_id, attachment_name)
|
59
|
+
server.get("#{name}/#{CGI.escape(document_id)}/#{CGI.escape(attachment_name)}", :no_json => true)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Saves or updates a document
|
63
|
+
#
|
64
|
+
# @param [Hash] doc The document
|
65
|
+
#
|
66
|
+
# @return [Hash] Parsed server response
|
67
|
+
def save(doc)
|
68
|
+
doc = encode_attachments_of(doc)
|
69
|
+
|
70
|
+
if doc['_id']
|
71
|
+
server.put("#{name}/#{CGI.escape(doc['_id'])}", doc)
|
72
|
+
else
|
73
|
+
server.post("#{name}", doc)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Saves or updates a bunch of documents
|
78
|
+
#
|
79
|
+
# @param [Array] docs The documents to save
|
80
|
+
#
|
81
|
+
# @return [Hash] Parsed server response
|
82
|
+
def bulk_save(docs)
|
83
|
+
server.post("#{name}/_bulk_docs", {:docs => docs})
|
84
|
+
end
|
85
|
+
|
86
|
+
# Deletes a document
|
87
|
+
#
|
88
|
+
# @param [String, Hash] document Document ID or an Hash representing the document
|
89
|
+
# @param [String] revision Document's revision
|
90
|
+
#
|
91
|
+
# @raise ArgumentError When the Hash representing the document neither has
|
92
|
+
# an ID nor a revision
|
93
|
+
#
|
94
|
+
# @raise ArgumentError When document is neither an ID nor an Hash
|
95
|
+
# representing a document
|
96
|
+
#
|
97
|
+
# @return [Hash] Parsed server response
|
98
|
+
def delete(document, revision=nil)
|
99
|
+
case document
|
100
|
+
when String
|
101
|
+
raise ArgumentError, 'Document revision must be specified' unless revision
|
102
|
+
server.delete "#{name}/#{CGI.escape(document)}", :rev => revision
|
103
|
+
when Hash
|
104
|
+
raise ArgumentError, 'Document ID and revision must be specified' unless
|
105
|
+
document['_id'] && document['_rev']
|
106
|
+
server.delete("#{name}/#{CGI.escape(document['_id'])}", :rev => document['_rev'])
|
107
|
+
else
|
108
|
+
raise ArgumentError, 'Document must be an Hash representing the document or its ID'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Deletes the current database
|
113
|
+
#
|
114
|
+
# @return [Hash] Parsed server response
|
115
|
+
def delete!
|
116
|
+
server.delete(name)
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
def encode_attachments_of(doc)
|
121
|
+
return doc unless doc['_attachments']
|
122
|
+
doc['_attachments'].each { |_, v| v.update('data' => base64(v['data'])) } and doc
|
123
|
+
end
|
124
|
+
|
125
|
+
def base64(data)
|
126
|
+
Base64.encode64(data.to_s).gsub(/\s/,'')
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Couchy
|
2
|
+
class Server
|
3
|
+
attr_accessor :uri
|
4
|
+
|
5
|
+
def initialize(uri)
|
6
|
+
@uri = Addressable::URI.parse(uri)
|
7
|
+
end
|
8
|
+
|
9
|
+
# Gets information about the server
|
10
|
+
#
|
11
|
+
# @return [Hash] Parsed server response
|
12
|
+
def info
|
13
|
+
get '/'
|
14
|
+
end
|
15
|
+
|
16
|
+
# Restarts the server
|
17
|
+
#
|
18
|
+
# @return [Hash] Parsed server response
|
19
|
+
def restart!
|
20
|
+
post '_restart'
|
21
|
+
end
|
22
|
+
|
23
|
+
# Gets a list of all the databases available on the server
|
24
|
+
#
|
25
|
+
# @return [Array] Parsed server response
|
26
|
+
def databases
|
27
|
+
get '_all_dbs'
|
28
|
+
end
|
29
|
+
|
30
|
+
# Gets a new [Couchy::Database] for the database
|
31
|
+
#
|
32
|
+
# @param [String] name The name of the database
|
33
|
+
#
|
34
|
+
# @return [Couchy::Database] The database
|
35
|
+
def database(name)
|
36
|
+
Database.new(self, name)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Creates a database
|
40
|
+
#
|
41
|
+
# @param [String] name Database's name
|
42
|
+
#
|
43
|
+
# @return [Couchy::Database] The newly created database
|
44
|
+
def create_db(name)
|
45
|
+
put(name)
|
46
|
+
database(name)
|
47
|
+
end
|
48
|
+
|
49
|
+
def get(path, params={})
|
50
|
+
need_json = !params.delete(:no_json)
|
51
|
+
response = RestClient.get(uri_for(path, params))
|
52
|
+
need_json ? json(response, :max_nesting => false) : response
|
53
|
+
end
|
54
|
+
|
55
|
+
def post(path, doc=nil, params={})
|
56
|
+
headers = params.delete(:headers)
|
57
|
+
payload = doc.to_json if doc
|
58
|
+
json RestClient.post(uri_for(path, params), payload, headers)
|
59
|
+
end
|
60
|
+
|
61
|
+
def put(path, doc=nil)
|
62
|
+
payload = doc.to_json if doc
|
63
|
+
json RestClient.put(uri_for(path), payload)
|
64
|
+
end
|
65
|
+
|
66
|
+
def delete(path, params={})
|
67
|
+
json RestClient.delete(uri_for(path, params))
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def uri_for(path, params={})
|
72
|
+
u = uri.join(path)
|
73
|
+
u.query_values = stringify_keys_and_jsonify_values(params) if params.any?
|
74
|
+
u.to_s
|
75
|
+
end
|
76
|
+
|
77
|
+
def json(json_string, options={})
|
78
|
+
JSON.parse(json_string, options)
|
79
|
+
end
|
80
|
+
|
81
|
+
def stringify_keys_and_jsonify_values(hash)
|
82
|
+
hash.inject({}) do |memo, (key, value)|
|
83
|
+
value = value.to_json if %w(key startkey endkey).include?(key.to_s)
|
84
|
+
memo[key.to_s] = value.to_s
|
85
|
+
memo
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|