careo-makura 0.1
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/COPYING +18 -0
- data/README.md +41 -0
- data/bin/makura +12 -0
- data/example/blog.rb +53 -0
- data/example/couch/map/author_all.js +5 -0
- data/example/couch/map/author_posts.js +5 -0
- data/example/couch/map/post_all.js +5 -0
- data/example/couch/map/post_comments.js +5 -0
- data/example/couch/reduce/sum_length.js +7 -0
- data/lib/makura.rb +62 -0
- data/lib/makura/database.rb +216 -0
- data/lib/makura/design.rb +37 -0
- data/lib/makura/error.rb +14 -0
- data/lib/makura/http_methods.rb +19 -0
- data/lib/makura/layout.rb +62 -0
- data/lib/makura/model.rb +372 -0
- data/lib/makura/plugin/pager.rb +56 -0
- data/lib/makura/server.rb +203 -0
- data/lib/makura/uuid_cache.rb +23 -0
- data/makura.gemspec +41 -0
- metadata +91 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
module Makura
|
2
|
+
module Plugin
|
3
|
+
module Pager
|
4
|
+
module SingletonMethods
|
5
|
+
def pager(page, limit)
|
6
|
+
Makura::Plugin::Pager::Pagination.new(self, :pager, page, limit)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Pagination
|
11
|
+
def initialize(model, view, page, limit)
|
12
|
+
@model, @view, @page, @limit = model, view, page, limit
|
13
|
+
end
|
14
|
+
|
15
|
+
# /pager/_all_docs?count=10&group=true
|
16
|
+
# /pager/_all_docs?startkey=%224f9dca1c66121f9320a69553546db07a%22&startkey_docid=4f9dca1c66121f9320a69553546db07a&skip=1&descending=false&count=10&group=true
|
17
|
+
# /pager/_all_docs?startkey=%22_design%2FUser%22&startkey_docid=_design%2FUser&skip=1&descending=false&count=10&group=true
|
18
|
+
# /pager/_all_docs?startkey=%22d850f0801686b85035680bb6f38d5c5c%22&startkey_docid=d850f0801686b85035680bb6f38d5c5c&skip=1&descending=false&count=10&group=true
|
19
|
+
|
20
|
+
# NOTE:
|
21
|
+
# * descending should be true if you page backwards
|
22
|
+
|
23
|
+
include Enumerable
|
24
|
+
|
25
|
+
def each(start_id = nil, descending = false, &block)
|
26
|
+
opts = {
|
27
|
+
:count => @limit,
|
28
|
+
:group => true,
|
29
|
+
:descending => descending,
|
30
|
+
# :include_docs => true,
|
31
|
+
}
|
32
|
+
|
33
|
+
if start_id
|
34
|
+
opts[:skip] = 1
|
35
|
+
opts[:startkey_docid] = start_id
|
36
|
+
opts[:startkey] = start_id
|
37
|
+
end
|
38
|
+
|
39
|
+
@model.view(@view, opts).each(&block)
|
40
|
+
end
|
41
|
+
|
42
|
+
def count
|
43
|
+
end
|
44
|
+
|
45
|
+
def first_page?
|
46
|
+
end
|
47
|
+
|
48
|
+
def last_page?
|
49
|
+
end
|
50
|
+
|
51
|
+
def empty?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
module Makura
|
2
|
+
class Server
|
3
|
+
include HTTPMethods
|
4
|
+
attr_accessor :uri, :cache_ttl, :cache_tries
|
5
|
+
|
6
|
+
COUCHDB_URI = 'http://localhost:5984'
|
7
|
+
CACHE_TTL = 5
|
8
|
+
CACHE_TRIES = 2
|
9
|
+
|
10
|
+
# Usage:
|
11
|
+
# server = Makura::Server.new
|
12
|
+
# #<URI::HTTP:0xb778ce38 URL:http://localhost:5984>
|
13
|
+
# server.info
|
14
|
+
# {"couchdb"=>"Welcome", "version"=>"0.9.0a718650-incubating"}
|
15
|
+
|
16
|
+
def initialize(uri = COUCHDB_URI, cache_ttl = CACHE_TTL, cache_tries = CACHE_TRIES)
|
17
|
+
@uri = URI(uri.to_s)
|
18
|
+
@cache_ttl = cache_ttl
|
19
|
+
@cache_tries = cache_tries
|
20
|
+
@uuids = UUIDCache.new(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
def inspect
|
24
|
+
@uri.inspect
|
25
|
+
end
|
26
|
+
|
27
|
+
# General queries
|
28
|
+
|
29
|
+
# Answers with general couchdb info, looks like:
|
30
|
+
#
|
31
|
+
# Usage:
|
32
|
+
# server.info
|
33
|
+
# # {'couchdb' => 'Welcome', 'version' => '0.9.0a718650-incubating'}
|
34
|
+
def info
|
35
|
+
get('/')
|
36
|
+
end
|
37
|
+
|
38
|
+
# Answers with configuration info.
|
39
|
+
#
|
40
|
+
# Usage:
|
41
|
+
# server.config
|
42
|
+
#
|
43
|
+
def config
|
44
|
+
get('/_config')
|
45
|
+
end
|
46
|
+
|
47
|
+
# Issue restart of the CouchDB daemon.
|
48
|
+
#
|
49
|
+
# Usage:
|
50
|
+
# server.restart
|
51
|
+
# # {'ok' => true}
|
52
|
+
def restart
|
53
|
+
post('/_restart')
|
54
|
+
end
|
55
|
+
|
56
|
+
# Array of names of databases on the server
|
57
|
+
#
|
58
|
+
# Usage:
|
59
|
+
# server.databases
|
60
|
+
# # ["another", "blog", "makura-spec"]
|
61
|
+
def databases
|
62
|
+
get('/_all_dbs')
|
63
|
+
end
|
64
|
+
|
65
|
+
# Return new database instance using this server instance.
|
66
|
+
#
|
67
|
+
# Usage:
|
68
|
+
# foo = server.database('foo')
|
69
|
+
# # #<Makura::Database 'http://localhost:5984/foo'>
|
70
|
+
# server.databases
|
71
|
+
# # ["another", "blog", "foo", "makura-spec"]
|
72
|
+
|
73
|
+
def database(name)
|
74
|
+
Database.new(self, name)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Answers with an uuid from the UUIDCache.
|
78
|
+
#
|
79
|
+
# Usage:
|
80
|
+
# server.next_uuid
|
81
|
+
# # "55fdca746fa5a5b56f5270875477a2cc"
|
82
|
+
|
83
|
+
def next_uuid
|
84
|
+
@uuids.next
|
85
|
+
end
|
86
|
+
|
87
|
+
def start_cache(namespace = 'makura', *servers)
|
88
|
+
servers << 'localhost:11211' if servers.empty?
|
89
|
+
@cache = MemCache.new(servers, :namespace => namespace, :multithread => true)
|
90
|
+
end
|
91
|
+
|
92
|
+
def stop_cache
|
93
|
+
@cache = nil
|
94
|
+
end
|
95
|
+
|
96
|
+
def cached(request, ttl = cache_ttl, tries = cache_tries)
|
97
|
+
key = request[:url]
|
98
|
+
|
99
|
+
unless response = @cache.get(key)
|
100
|
+
response = execute(request)
|
101
|
+
@cache.add(key, response, ttl)
|
102
|
+
end
|
103
|
+
|
104
|
+
return response
|
105
|
+
rescue MemCache::MemCacheError => error
|
106
|
+
servers = @cache.servers.map{|s| "#{s.host}:#{s.port}"}
|
107
|
+
start_cache(@cache.namespace, *servers)
|
108
|
+
tries -= 1
|
109
|
+
retry if tries > 0
|
110
|
+
warn "[makura caching disabled] #{error.message}"
|
111
|
+
@cache = nil
|
112
|
+
execute(request)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Helpers
|
116
|
+
|
117
|
+
def request(method, path, params = {})
|
118
|
+
keep_raw = params.delete(:raw)
|
119
|
+
payload = params.delete(:payload)
|
120
|
+
payload = payload.to_json if payload and not keep_raw
|
121
|
+
headers = {}
|
122
|
+
|
123
|
+
if content_type = params.delete('Content-Type')
|
124
|
+
headers['Content-Type'] = content_type
|
125
|
+
end
|
126
|
+
|
127
|
+
params.delete_if{|k,v| v.nil? }
|
128
|
+
uri = uri(path, params).to_s
|
129
|
+
|
130
|
+
request = {
|
131
|
+
:method => method,
|
132
|
+
:url => uri,
|
133
|
+
:payload => payload,
|
134
|
+
:headers => headers}
|
135
|
+
|
136
|
+
if @cache and request[:method] == :get
|
137
|
+
raw = cached(request)
|
138
|
+
else
|
139
|
+
raw = execute(request)
|
140
|
+
end
|
141
|
+
|
142
|
+
return raw if keep_raw
|
143
|
+
json = JSON.parse(raw)
|
144
|
+
rescue JSON::ParserError
|
145
|
+
return raw
|
146
|
+
rescue RestClient::RequestFailed => ex
|
147
|
+
raise appropriate_error(ex)
|
148
|
+
rescue RestClient::ResourceNotFound => ex
|
149
|
+
raise Error::ResourceNotFound, request[:url], ex.backtrace
|
150
|
+
rescue Errno::ECONNREFUSED
|
151
|
+
raise Error::ConnectionRefused, "Is CouchDB running at #{@uri}?"
|
152
|
+
end
|
153
|
+
|
154
|
+
def execute(request)
|
155
|
+
RestClient::Request.execute(request)
|
156
|
+
end
|
157
|
+
|
158
|
+
def appropriate_error(exception)
|
159
|
+
body = exception.response.body if exception.respond_to?(:response)
|
160
|
+
backtrace = exception.backtrace
|
161
|
+
|
162
|
+
raise(Error::RequestFailed, exception.message, backtrace) unless body
|
163
|
+
raise(Error::RequestFailed, exception.message, backtrace) if body.empty?
|
164
|
+
|
165
|
+
json = JSON.parse(body)
|
166
|
+
error, reason = json['error'], json['reason']
|
167
|
+
|
168
|
+
case error
|
169
|
+
when 'bad_request'
|
170
|
+
raise(Error::BadRequest, reason, backtrace)
|
171
|
+
when 'authorization'
|
172
|
+
raise(Error::Authorization, reason, backtrace)
|
173
|
+
when 'not_found'
|
174
|
+
raise(Error::NotFound, reason, backtrace)
|
175
|
+
when 'file_exists'
|
176
|
+
raise(Error::FileExists, reason, backtrace)
|
177
|
+
when 'missing_rev'
|
178
|
+
raise(Error::MissingRevision, reason, backtrace)
|
179
|
+
when 'conflict'
|
180
|
+
raise(Error::Conflict, reason, backtrace)
|
181
|
+
else
|
182
|
+
raise(Error::RequestFailed, json.inspect, backtrace)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
JSON_PARAMS = %w[key startkey endkey]
|
187
|
+
|
188
|
+
def paramify(hash)
|
189
|
+
hash.map{|k,v|
|
190
|
+
k = k.to_s
|
191
|
+
v = v.to_json if JSON_PARAMS.include?(k)
|
192
|
+
"#{Makura.escape(k)}=#{Makura.escape(v)}"
|
193
|
+
}.join('&')
|
194
|
+
end
|
195
|
+
|
196
|
+
def uri(path = '/', params = {})
|
197
|
+
uri = @uri.dup
|
198
|
+
uri.path = (path[0,1] == '/' ? path : "/#{path}").squeeze('/')
|
199
|
+
uri.query = paramify(params) unless params.empty?
|
200
|
+
uri
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Makura
|
2
|
+
class UUIDCache
|
3
|
+
attr_accessor :min, :max, :server, :pretty
|
4
|
+
|
5
|
+
def initialize(server, min = 500, max = 1500, pretty = true)
|
6
|
+
@server, @min, @max, @pretty = server, min, max, pretty
|
7
|
+
@uuids = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def next
|
11
|
+
fetch if @uuids.size < min
|
12
|
+
@uuids.shift
|
13
|
+
end
|
14
|
+
|
15
|
+
def fetch(count = 0)
|
16
|
+
todo = max - @uuids.size
|
17
|
+
count = [min, todo, max].sort[1]
|
18
|
+
uuids = @server.get('/_uuids', :count => count)['uuids']
|
19
|
+
uuids.map!{|u| Makura.pretty_from_md5(u) } if pretty
|
20
|
+
@uuids.concat(uuids)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/makura.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "makura"
|
3
|
+
s.version = "0.1"
|
4
|
+
|
5
|
+
s.summary = "Ruby wrapper around the CouchDB REST API."
|
6
|
+
s.description = "Ruby wrapper around the CouchDB REST API."
|
7
|
+
s.platform = "ruby"
|
8
|
+
s.has_rdoc = true
|
9
|
+
s.author = "Michael 'manveru' Fellinger"
|
10
|
+
s.email = "m.fellinger@gmail.com"
|
11
|
+
s.homepage = "http://github.com/manveru/makura"
|
12
|
+
s.executables = ['makura']
|
13
|
+
s.bindir = "bin"
|
14
|
+
s.require_path = "lib"
|
15
|
+
|
16
|
+
s.add_dependency('rest-client', '>= 0.8.1')
|
17
|
+
s.add_dependency('json', '>= 1.1.3')
|
18
|
+
|
19
|
+
s.files = [
|
20
|
+
"COPYING",
|
21
|
+
"README.md",
|
22
|
+
"bin/makura",
|
23
|
+
"example/blog.rb",
|
24
|
+
"example/couch/map/author_all.js",
|
25
|
+
"example/couch/map/author_posts.js",
|
26
|
+
"example/couch/map/post_all.js",
|
27
|
+
"example/couch/map/post_comments.js",
|
28
|
+
"example/couch/reduce/sum_length.js",
|
29
|
+
"lib/makura/database.rb",
|
30
|
+
"lib/makura/design.rb",
|
31
|
+
"lib/makura/error.rb",
|
32
|
+
"lib/makura/http_methods.rb",
|
33
|
+
"lib/makura/layout.rb",
|
34
|
+
"lib/makura/model.rb",
|
35
|
+
"lib/makura/plugin/pager.rb",
|
36
|
+
"lib/makura/server.rb",
|
37
|
+
"lib/makura/uuid_cache.rb",
|
38
|
+
"lib/makura.rb",
|
39
|
+
"makura.gemspec"
|
40
|
+
]
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: careo-makura
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael 'manveru' Fellinger
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-13 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rest-client
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.8.1
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: json
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.1.3
|
34
|
+
version:
|
35
|
+
description: Ruby wrapper around the CouchDB REST API.
|
36
|
+
email: m.fellinger@gmail.com
|
37
|
+
executables:
|
38
|
+
- makura
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- COPYING
|
45
|
+
- README.md
|
46
|
+
- bin/makura
|
47
|
+
- example/blog.rb
|
48
|
+
- example/couch/map/author_all.js
|
49
|
+
- example/couch/map/author_posts.js
|
50
|
+
- example/couch/map/post_all.js
|
51
|
+
- example/couch/map/post_comments.js
|
52
|
+
- example/couch/reduce/sum_length.js
|
53
|
+
- lib/makura/database.rb
|
54
|
+
- lib/makura/design.rb
|
55
|
+
- lib/makura/error.rb
|
56
|
+
- lib/makura/http_methods.rb
|
57
|
+
- lib/makura/layout.rb
|
58
|
+
- lib/makura/model.rb
|
59
|
+
- lib/makura/plugin/pager.rb
|
60
|
+
- lib/makura/server.rb
|
61
|
+
- lib/makura/uuid_cache.rb
|
62
|
+
- lib/makura.rb
|
63
|
+
- makura.gemspec
|
64
|
+
has_rdoc: true
|
65
|
+
homepage: http://github.com/manveru/makura
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: "0"
|
76
|
+
version:
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: "0"
|
82
|
+
version:
|
83
|
+
requirements: []
|
84
|
+
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.2.0
|
87
|
+
signing_key:
|
88
|
+
specification_version: 2
|
89
|
+
summary: Ruby wrapper around the CouchDB REST API.
|
90
|
+
test_files: []
|
91
|
+
|