namelessjon-couchrest 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.
Files changed (53) hide show
  1. data/LICENSE +176 -0
  2. data/README.md +46 -0
  3. data/Rakefile +69 -0
  4. data/THANKS.md +21 -0
  5. data/couchrest.gemspec +111 -0
  6. data/examples/word_count/markov +38 -0
  7. data/examples/word_count/views/books/chunked-map.js +3 -0
  8. data/examples/word_count/views/books/united-map.js +1 -0
  9. data/examples/word_count/views/markov/chain-map.js +6 -0
  10. data/examples/word_count/views/markov/chain-reduce.js +7 -0
  11. data/examples/word_count/views/word_count/count-map.js +6 -0
  12. data/examples/word_count/views/word_count/count-reduce.js +3 -0
  13. data/examples/word_count/word_count.rb +46 -0
  14. data/examples/word_count/word_count_query.rb +40 -0
  15. data/examples/word_count/word_count_views.rb +26 -0
  16. data/history.txt +145 -0
  17. data/lib/couchrest/commands/generate.rb +71 -0
  18. data/lib/couchrest/commands/push.rb +103 -0
  19. data/lib/couchrest/database.rb +373 -0
  20. data/lib/couchrest/design.rb +80 -0
  21. data/lib/couchrest/document.rb +89 -0
  22. data/lib/couchrest/helper/attachments.rb +29 -0
  23. data/lib/couchrest/helper/pager.rb +103 -0
  24. data/lib/couchrest/helper/streamer.rb +51 -0
  25. data/lib/couchrest/helper/upgrade.rb +52 -0
  26. data/lib/couchrest/json_response.rb +14 -0
  27. data/lib/couchrest/middlewares/logger.rb +263 -0
  28. data/lib/couchrest/monkeypatches.rb +42 -0
  29. data/lib/couchrest/response.rb +35 -0
  30. data/lib/couchrest/rest_api.rb +62 -0
  31. data/lib/couchrest/server.rb +90 -0
  32. data/lib/couchrest/support/inheritable_attributes.rb +107 -0
  33. data/lib/couchrest.rb +127 -0
  34. data/spec/couchrest/couchrest_spec.rb +202 -0
  35. data/spec/couchrest/database_spec.rb +870 -0
  36. data/spec/couchrest/design_spec.rb +158 -0
  37. data/spec/couchrest/document_spec.rb +279 -0
  38. data/spec/couchrest/helpers/pager_spec.rb +123 -0
  39. data/spec/couchrest/helpers/streamer_spec.rb +52 -0
  40. data/spec/couchrest/server_spec.rb +35 -0
  41. data/spec/fixtures/attachments/README +3 -0
  42. data/spec/fixtures/attachments/couchdb.png +0 -0
  43. data/spec/fixtures/attachments/test.html +11 -0
  44. data/spec/fixtures/views/lib.js +3 -0
  45. data/spec/fixtures/views/test_view/lib.js +3 -0
  46. data/spec/fixtures/views/test_view/only-map.js +4 -0
  47. data/spec/fixtures/views/test_view/test-map.js +3 -0
  48. data/spec/fixtures/views/test_view/test-reduce.js +3 -0
  49. data/spec/spec.opts +5 -0
  50. data/spec/spec_helper.rb +44 -0
  51. data/utils/remap.rb +27 -0
  52. data/utils/subset.rb +30 -0
  53. metadata +179 -0
@@ -0,0 +1,80 @@
1
+ require 'couchrest/document'
2
+ module CouchRest
3
+ class Design < Document
4
+ def view_by *keys
5
+ opts = keys.pop if keys.last.is_a?(Hash)
6
+ opts ||= {}
7
+ self['views'] ||= {}
8
+ method_name = "by_#{keys.join('_and_')}"
9
+
10
+ if opts[:map]
11
+ view = {}
12
+ view['map'] = opts.delete(:map)
13
+ if opts[:reduce]
14
+ view['reduce'] = opts.delete(:reduce)
15
+ opts[:reduce] = false
16
+ end
17
+ self['views'][method_name] = view
18
+ else
19
+ doc_keys = keys.collect{|k|"doc['#{k}']"} # this is where :require => 'doc.x == true' would show up
20
+ key_emit = doc_keys.length == 1 ? "#{doc_keys.first}" : "[#{doc_keys.join(', ')}]"
21
+ guards = opts.delete(:guards) || []
22
+ guards += doc_keys.map{|k| "(#{k} != null)"}
23
+ map_function = <<-JAVASCRIPT
24
+ function(doc) {
25
+ if (#{guards.join(' && ')}) {
26
+ emit(#{key_emit}, null);
27
+ }
28
+ }
29
+ JAVASCRIPT
30
+ self['views'][method_name] = {
31
+ 'map' => map_function
32
+ }
33
+ end
34
+ self['views'][method_name]['couchrest-defaults'] = opts unless opts.empty?
35
+ method_name
36
+ end
37
+
38
+ # Dispatches to any named view.
39
+ # (using the database where this design doc was saved)
40
+ def view view_name, query={}, &block
41
+ view_on database, view_name, query, &block
42
+ end
43
+
44
+ # Dispatches to any named view in a specific database
45
+ def view_on db, view_name, query={}, &block
46
+ view_name = view_name.to_s
47
+ view_slug = "#{name}/#{view_name}"
48
+ defaults = (self['views'][view_name] && self['views'][view_name]["couchrest-defaults"]) || {}
49
+ db.view(view_slug, defaults.merge(query), &block)
50
+ end
51
+
52
+ def name
53
+ id.sub('_design/','') if id
54
+ end
55
+
56
+ def name= newname
57
+ self['_id'] = "_design/#{newname}"
58
+ end
59
+
60
+ def save
61
+ raise ArgumentError, "_design docs require a name" unless name && name.length > 0
62
+ super
63
+ end
64
+
65
+ private
66
+
67
+ # returns stored defaults if the there is a view named this in the design doc
68
+ def has_view?(view)
69
+ view = view.to_s
70
+ self['views'][view] &&
71
+ (self['views'][view]["couchrest-defaults"] || {})
72
+ end
73
+
74
+ def fetch_view view_name, opts, &block
75
+ database.view(view_name, opts, &block)
76
+ end
77
+
78
+ end
79
+
80
+ end
@@ -0,0 +1,89 @@
1
+ require 'couchrest/response'
2
+ require 'couchrest/helper/attachments'
3
+ module CouchRest
4
+ class Document < Response
5
+ include CouchRest::Attachments
6
+ extend CouchRest::InheritableAttributes
7
+
8
+ couchrest_inheritable_accessor :database
9
+ attr_accessor :database
10
+
11
+ # override the CouchRest::Model-wide default_database
12
+ # This is not a thread safe operation, do not change the model
13
+ # database at runtime.
14
+ def self.use_database(db)
15
+ self.database = db
16
+ end
17
+
18
+ def id
19
+ self['_id']
20
+ end
21
+
22
+ def id=(id)
23
+ self['_id'] = id
24
+ end
25
+
26
+ def rev
27
+ self['_rev']
28
+ end
29
+
30
+ # returns true if the document has never been saved
31
+ def new?
32
+ !rev
33
+ end
34
+ alias :new_document? :new?
35
+
36
+ # Saves the document to the db using create or update. Also runs the :save
37
+ # callbacks. Sets the <tt>_id</tt> and <tt>_rev</tt> fields based on
38
+ # CouchDB's response.
39
+ # If <tt>bulk</tt> is <tt>true</tt> (defaults to false) the document is cached for bulk save.
40
+ def save(bulk = false)
41
+ raise ArgumentError, "doc.database required for saving" unless database
42
+ result = database.save_doc self, bulk
43
+ result['ok']
44
+ end
45
+
46
+ # Deletes the document from the database. Runs the :delete callbacks.
47
+ # Removes the <tt>_id</tt> and <tt>_rev</tt> fields, preparing the
48
+ # document to be saved to a new <tt>_id</tt>.
49
+ # If <tt>bulk</tt> is <tt>true</tt> (defaults to false) the document won't
50
+ # actually be deleted from the db until bulk save.
51
+ def destroy(bulk = false)
52
+ raise ArgumentError, "doc.database required to destroy" unless database
53
+ result = database.delete_doc(self, bulk)
54
+ if result['ok']
55
+ self['_rev'] = nil
56
+ self['_id'] = nil
57
+ end
58
+ result['ok']
59
+ end
60
+
61
+ # copies the document to a new id. If the destination id currently exists, a rev must be provided.
62
+ # <tt>dest</tt> can take one of two forms if overwriting: "id_to_overwrite?rev=revision" or the actual doc
63
+ # hash with a '_rev' key
64
+ def copy(dest)
65
+ raise ArgumentError, "doc.database required to copy" unless database
66
+ result = database.copy_doc(self, dest)
67
+ result['ok']
68
+ end
69
+
70
+ # Returns the CouchDB uri for the document
71
+ def uri(append_rev = false)
72
+ return nil if new?
73
+ couch_uri = "#{database.root}/#{CGI.escape(id)}"
74
+ if append_rev == true
75
+ couch_uri << "?rev=#{rev}"
76
+ elsif append_rev.kind_of?(Integer)
77
+ couch_uri << "?rev=#{append_rev}"
78
+ end
79
+ couch_uri
80
+ end
81
+
82
+ # Returns the document's database
83
+ def database
84
+ @database || self.class.database
85
+ end
86
+
87
+ end
88
+
89
+ end
@@ -0,0 +1,29 @@
1
+ module CouchRest
2
+ module Attachments
3
+
4
+ # saves an attachment directly to couchdb
5
+ def put_attachment(name, file, options={})
6
+ raise ArgumentError, "doc must be saved" unless self.rev
7
+ raise ArgumentError, "doc.database required to put_attachment" unless database
8
+ result = database.put_attachment(self, name, file, options)
9
+ self['_rev'] = result['rev']
10
+ result['ok']
11
+ end
12
+
13
+ # returns an attachment's data
14
+ def fetch_attachment(name)
15
+ raise ArgumentError, "doc must be saved" unless self.rev
16
+ raise ArgumentError, "doc.database required to put_attachment" unless database
17
+ database.fetch_attachment(self, name)
18
+ end
19
+
20
+ # deletes an attachment directly from couchdb
21
+ def delete_attachment(name, force=false)
22
+ raise ArgumentError, "doc.database required to delete_attachment" unless database
23
+ result = database.delete_attachment(self, name, force)
24
+ self['_rev'] = result['rev']
25
+ result['ok']
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,103 @@
1
+ module CouchRest
2
+ class Pager
3
+ attr_accessor :db
4
+ def initialize db
5
+ @db = db
6
+ end
7
+
8
+ def all_docs(limit=100, &block)
9
+ startkey = nil
10
+ oldend = nil
11
+
12
+ while docrows = request_all_docs(limit+1, startkey)
13
+ startkey = docrows.last['key']
14
+ docrows.pop if docrows.length > limit
15
+ if oldend == startkey
16
+ break
17
+ end
18
+ yield(docrows)
19
+ oldend = startkey
20
+ end
21
+ end
22
+
23
+ def key_reduce(view, limit=2000, firstkey = nil, lastkey = nil, &block)
24
+ # start with no keys
25
+ startkey = firstkey
26
+ # lastprocessedkey = nil
27
+ keepgoing = true
28
+
29
+ while keepgoing && viewrows = request_view(view, limit, startkey)
30
+ startkey = viewrows.first['key']
31
+ endkey = viewrows.last['key']
32
+
33
+ if (startkey == endkey)
34
+ # we need to rerequest to get a bigger page
35
+ # so we know we have all the rows for that key
36
+ viewrows = @db.view(view, :key => startkey)['rows']
37
+ # we need to do an offset thing to find the next startkey
38
+ # otherwise we just get stuck
39
+ lastdocid = viewrows.last['id']
40
+ fornextloop = @db.view(view, :startkey => startkey, :startkey_docid => lastdocid, :limit => 2)['rows']
41
+
42
+ newendkey = fornextloop.last['key']
43
+ if (newendkey == endkey)
44
+ keepgoing = false
45
+ else
46
+ startkey = newendkey
47
+ end
48
+ rows = viewrows
49
+ else
50
+ rows = []
51
+ for r in viewrows
52
+ if (lastkey && r['key'] == lastkey)
53
+ keepgoing = false
54
+ break
55
+ end
56
+ break if (r['key'] == endkey)
57
+ rows << r
58
+ end
59
+ startkey = endkey
60
+ end
61
+
62
+ key = :begin
63
+ values = []
64
+
65
+ rows.each do |r|
66
+ if key != r['key']
67
+ # we're on a new key, yield the old first and then reset
68
+ yield(key, values) if key != :begin
69
+ key = r['key']
70
+ values = []
71
+ end
72
+ # keep accumulating
73
+ values << r['value']
74
+ end
75
+ yield(key, values)
76
+
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def request_all_docs limit, startkey = nil
83
+ opts = {}
84
+ opts[:limit] = limit if limit
85
+ opts[:startkey] = startkey if startkey
86
+ results = @db.documents(opts)
87
+ rows = results['rows']
88
+ rows unless rows.length == 0
89
+ end
90
+
91
+ def request_view view, limit = nil, startkey = nil, endkey = nil
92
+ opts = {}
93
+ opts[:limit] = limit if limit
94
+ opts[:startkey] = startkey if startkey
95
+ opts[:endkey] = endkey if endkey
96
+
97
+ results = @db.view(view, opts)
98
+ rows = results['rows']
99
+ rows unless rows.length == 0
100
+ end
101
+
102
+ end
103
+ end
@@ -0,0 +1,51 @@
1
+ module CouchRest
2
+ class Streamer
3
+ attr_accessor :db
4
+ def initialize db
5
+ @db = db
6
+ end
7
+
8
+ # Stream a view, yielding one row at a time. Shells out to <tt>curl</tt> to keep RAM usage low when you have millions of rows.
9
+ def view name, params = nil, &block
10
+ urlst = if /^_/.match(name) then
11
+ "#{@db.root}/#{name}"
12
+ else
13
+ name = name.split('/')
14
+ dname = name.shift
15
+ vname = name.join('/')
16
+ "#{@db.root}/_design/#{dname}/_view/#{vname}"
17
+ end
18
+ url = CouchRest.paramify_url urlst, params
19
+ # puts "stream #{url}"
20
+ first = nil
21
+ IO.popen("curl --silent \"#{url}\"") do |view|
22
+ first = view.gets # discard header
23
+ while line = view.gets
24
+ row = parse_line(line)
25
+ block.call row unless row.nil? # last line "}]" discarded
26
+ end
27
+ end
28
+ parse_first(first)
29
+ end
30
+
31
+ private
32
+
33
+ def parse_line line
34
+ return nil unless line
35
+ if /(\{.*\}),?/.match(line.chomp)
36
+ JSON.parse($1)
37
+ end
38
+ end
39
+
40
+ def parse_first first
41
+ return nil unless first
42
+ parts = first.split(',')
43
+ parts.pop
44
+ line = parts.join(',')
45
+ JSON.parse("#{line}}")
46
+ rescue
47
+ nil
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,52 @@
1
+ require 'couchrest/helper/streamer'
2
+ module CouchRest
3
+ class Upgrade
4
+ attr_accessor :olddb, :newdb, :dbname
5
+ def initialize dbname, old_couch, new_couch
6
+ @dbname = dbname
7
+ @olddb = old_couch.database dbname
8
+ @newdb = new_couch.database!(dbname)
9
+ @bulk_docs = []
10
+ end
11
+ def clone!
12
+ puts "#{dbname} - #{olddb.info['doc_count']} docs"
13
+ streamer = CouchRest::Streamer.new(olddb)
14
+ streamer.view("_all_docs_by_seq") do |row|
15
+ load_row_docs(row) if row
16
+ maybe_flush_bulks
17
+ end
18
+ flush_bulks!
19
+ end
20
+
21
+ private
22
+
23
+ def maybe_flush_bulks
24
+ flush_bulks! if (@bulk_docs.length > 99)
25
+ end
26
+
27
+ def flush_bulks!
28
+ url = CouchRest.paramify_url "#{@newdb.uri}/_bulk_docs", {:all_or_nothing => true}
29
+ puts "posting #{@bulk_docs.length} bulk docs to #{url}"
30
+ begin
31
+ CouchRest.post url, {:docs => @bulk_docs}
32
+ @bulk_docs = []
33
+ rescue Exception => e
34
+ puts e.response
35
+ raise e
36
+ end
37
+ end
38
+
39
+ def load_row_docs(row)
40
+ results = @olddb.get(row["id"], {:open_revs => "all", :attachments => true})
41
+ results.select{|r|r["ok"]}.each do |r|
42
+ doc = r["ok"]
43
+ if /^_/.match(doc["_id"]) && !/^_design/.match(doc["_id"])
44
+ puts "invalid docid #{doc["_id"]} -- trimming"
45
+ doc["_id"] = doc["_id"].sub('_','')
46
+ end
47
+ doc.delete('_rev')
48
+ @bulk_docs << doc
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/ruby
2
+ # Jonathan D. Stott <jonathan.stott@gmail.com>
3
+ module CouchRest
4
+ module JsonResponse
5
+ attr_accessor :headers, :raw
6
+ def JsonResponse.create(body)
7
+ result = JSON.parse(body)
8
+ result.extend(JsonResponse)
9
+ result.headers = body.headers
10
+ result.raw = body.to_s
11
+ result
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,263 @@
1
+ ####################################
2
+ # USAGE
3
+ #
4
+ # in your rack.rb file
5
+ # require this file and then:
6
+ #
7
+ # couch = CouchRest.new
8
+ # LOG_DB = couch.database!('couchrest-logger')
9
+ # use CouchRest::Logger, LOG_DB
10
+ #
11
+ # Note:
12
+ # to require just this middleware, if you have the gem installed do:
13
+ # require 'couchrest/middlewares/logger'
14
+ #
15
+ # For log processing examples, see examples at the bottom of this file
16
+
17
+ module CouchRest
18
+ class Logger
19
+
20
+ def self.log
21
+ Thread.current["couchrest.logger"] ||= {:queries => []}
22
+ end
23
+
24
+ def initialize(app, db=nil)
25
+ @app = app
26
+ @db = db
27
+ end
28
+
29
+ def self.record(log_info)
30
+ log[:queries] << log_info
31
+ end
32
+
33
+ def log
34
+ Thread.current["couchrest.logger"] ||= {:queries => []}
35
+ end
36
+
37
+ def reset_log
38
+ Thread.current["couchrest.logger"] = nil
39
+ end
40
+
41
+ def call(env)
42
+ reset_log
43
+ log['started_at'] = Time.now
44
+ log['env'] = env
45
+ log['url'] = 'http://' + env['HTTP_HOST'] + env['REQUEST_URI']
46
+ response = @app.call(env)
47
+ log['ended_at'] = Time.now
48
+ log['duration'] = log['ended_at'] - log['started_at']
49
+ # let's report the log in a different thread so we don't slow down the app
50
+ @db ? Thread.new(@db, log){|db, rlog| db.save_doc(rlog);} : p(log.inspect)
51
+ response
52
+ end
53
+ end
54
+ end
55
+
56
+ # inject our logger into key RestClient methods
57
+ module RestClient
58
+
59
+ def self.get(uri, headers=nil)
60
+ start_query = Time.now
61
+ log = {:method => :get, :uri => uri, :headers => headers}
62
+ response = super(uri, headers=nil)
63
+ end_query = Time.now
64
+ log[:duration] = (end_query - start_query)
65
+ CouchRest::Logger.record(log)
66
+ response
67
+ end
68
+
69
+ def self.post(uri, payload, headers=nil)
70
+ start_query = Time.now
71
+ log = {:method => :post, :uri => uri, :payload => (payload ? (JSON.load(payload) rescue 'parsing error') : nil), :headers => headers}
72
+ response = super(uri, payload, headers=nil)
73
+ end_query = Time.now
74
+ log[:duration] = (end_query - start_query)
75
+ CouchRest::Logger.record(log)
76
+ response
77
+ end
78
+
79
+ def self.put(uri, payload, headers=nil)
80
+ start_query = Time.now
81
+ log = {:method => :put, :uri => uri, :payload => (payload ? (JSON.load(payload) rescue 'parsing error') : nil), :headers => headers}
82
+ response = super(uri, payload, headers=nil)
83
+ end_query = Time.now
84
+ log[:duration] = (end_query - start_query)
85
+ CouchRest::Logger.record(log)
86
+ response
87
+ end
88
+
89
+ def self.delete(uri, headers=nil)
90
+ start_query = Time.now
91
+ log = {:method => :delete, :uri => uri, :headers => headers}
92
+ response = super(uri, headers=nil)
93
+ end_query = Time.now
94
+ log[:duration] = (end_query - start_query)
95
+ CouchRest::Logger.record(log)
96
+ response
97
+ end
98
+
99
+ end
100
+
101
+
102
+ # Advanced usage example
103
+ #
104
+ #
105
+ # # DB VIEWS
106
+ # by_url = {
107
+ # :map =>
108
+ # "function(doc) {
109
+ # if(doc['url']){ emit(doc['url'], 1) };
110
+ # }",
111
+ # :reduce =>
112
+ # 'function (key, values, rereduce) {
113
+ # return(sum(values));
114
+ # };'
115
+ # }
116
+ # req_duration = {
117
+ # :map =>
118
+ # "function(doc) {
119
+ # if(doc['duration']){ emit(doc['url'], doc['duration']) };
120
+ # }",
121
+ # :reduce =>
122
+ # 'function (key, values, rereduce) {
123
+ # return(sum(values)/values.length);
124
+ # };'
125
+ # }
126
+ #
127
+ # query_duration = {
128
+ # :map =>
129
+ # "function(doc) {
130
+ # if(doc['queries']){
131
+ # doc.queries.forEach(function(query){
132
+ # if(query['duration'] && query['method']){
133
+ # emit(query['method'], query['duration'])
134
+ # }
135
+ # });
136
+ # };
137
+ # }" ,
138
+ # :reduce =>
139
+ # 'function (key, values, rereduce) {
140
+ # return(sum(values)/values.length);
141
+ # };'
142
+ # }
143
+ #
144
+ # action_queries = {
145
+ # :map =>
146
+ # "function(doc) {
147
+ # if(doc['queries']){
148
+ # emit(doc['url'], doc['queries'].length)
149
+ # };
150
+ # }",
151
+ # :reduce =>
152
+ # 'function (key, values, rereduce) {
153
+ # return(sum(values)/values.length);
154
+ # };'
155
+ # }
156
+ #
157
+ # action_time_spent_in_db = {
158
+ # :map =>
159
+ # "function(doc) {
160
+ # if(doc['queries']){
161
+ # var totalDuration = 0;
162
+ # doc.queries.forEach(function(query){
163
+ # totalDuration += query['duration']
164
+ # })
165
+ # emit(doc['url'], totalDuration)
166
+ # };
167
+ # }",
168
+ # :reduce =>
169
+ # 'function (key, values, rereduce) {
170
+ # return(sum(values)/values.length);
171
+ # };'
172
+ # }
173
+ #
174
+ # show_queries = %Q~function(doc, req) {
175
+ # var body = ""
176
+ # body += "<h1>" + doc['url'] + "</h1>"
177
+ # body += "<h2>Request duration in seconds: " + doc['duration'] + "</h2>"
178
+ # body += "<h3>" + doc['queries'].length + " queries</h3><ul>"
179
+ # if (doc.queries){
180
+ # doc.queries.forEach(function(query){
181
+ # body += "<li>"+ query['uri'] +"</li>"
182
+ # });
183
+ # };
184
+ # body += "</ul>"
185
+ # if(doc){ return { body: body} }
186
+ # }~
187
+ #
188
+ #
189
+ # couch = CouchRest.new
190
+ # LOG_DB = couch.database!('couchrest-logger')
191
+ # design_doc = LOG_DB.get("_design/stats") rescue nil
192
+ # LOG_DB.delete_doc design_doc rescue nil
193
+ # LOG_DB.save_doc({
194
+ # "_id" => "_design/stats",
195
+ # :views => {
196
+ # :by_url => by_url,
197
+ # :request_duration => req_duration,
198
+ # :query_duration => query_duration,
199
+ # :action_queries => action_queries,
200
+ # :action_time_spent_in_db => action_time_spent_in_db
201
+ # },
202
+ # :shows => {
203
+ # :queries => show_queries
204
+ # }
205
+ # })
206
+ #
207
+ # module CouchRest
208
+ # class Logger
209
+ #
210
+ # def self.roundup(value)
211
+ # begin
212
+ # value = Float(value)
213
+ # (value * 100).round.to_f / 100
214
+ # rescue
215
+ # value
216
+ # end
217
+ # end
218
+ #
219
+ # # Usage example:
220
+ # # CouchRest::Logger.average_request_duration(LOG_DB)['rows'].first['value']
221
+ # def self.average_request_duration(db)
222
+ # raw = db.view('stats/request_duration', :reduce => true)
223
+ # (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
224
+ # end
225
+ #
226
+ # def self.average_query_duration(db)
227
+ # raw = db.view('stats/query_duration', :reduce => true)
228
+ # (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
229
+ # end
230
+ #
231
+ # def self.average_get_query_duration(db)
232
+ # raw = db.view('stats/query_duration', :key => 'get', :reduce => true)
233
+ # (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
234
+ # end
235
+ #
236
+ # def self.average_post_query_duration(db)
237
+ # raw = db.view('stats/query_duration', :key => 'post', :reduce => true)
238
+ # (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
239
+ # end
240
+ #
241
+ # def self.average_queries_per_action(db)
242
+ # raw = db.view('stats/action_queries', :reduce => true)
243
+ # (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
244
+ # end
245
+ #
246
+ # def self.average_db_time_per_action(db)
247
+ # raw = db.view('stats/action_time_spent_in_db', :reduce => true)
248
+ # (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
249
+ # end
250
+ #
251
+ # def self.stats(db)
252
+ # Thread.new(db){|db|
253
+ # puts "=== STATS ===\n"
254
+ # puts "average request duration: #{average_request_duration(db)}\n"
255
+ # puts "average query duration: #{average_query_duration(db)}\n"
256
+ # puts "average queries per action : #{average_queries_per_action(db)}\n"
257
+ # puts "average time spent in DB (per action): #{average_db_time_per_action(db)}\n"
258
+ # puts "===============\n"
259
+ # }
260
+ # end
261
+ #
262
+ # end
263
+ # end