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.
@@ -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
+