jrun-couchrest 0.2.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/LICENSE +176 -0
- data/README.md +93 -0
- data/Rakefile +69 -0
- data/THANKS.md +18 -0
- data/examples/model/example.rb +144 -0
- data/examples/word_count/markov +38 -0
- data/examples/word_count/views/books/chunked-map.js +3 -0
- data/examples/word_count/views/books/united-map.js +1 -0
- data/examples/word_count/views/markov/chain-map.js +6 -0
- data/examples/word_count/views/markov/chain-reduce.js +7 -0
- data/examples/word_count/views/word_count/count-map.js +6 -0
- data/examples/word_count/views/word_count/count-reduce.js +3 -0
- data/examples/word_count/word_count.rb +46 -0
- data/examples/word_count/word_count_query.rb +40 -0
- data/examples/word_count/word_count_views.rb +26 -0
- data/lib/couchrest/commands/generate.rb +71 -0
- data/lib/couchrest/commands/push.rb +103 -0
- data/lib/couchrest/core/database.rb +313 -0
- data/lib/couchrest/core/design.rb +89 -0
- data/lib/couchrest/core/document.rb +96 -0
- data/lib/couchrest/core/response.rb +16 -0
- data/lib/couchrest/core/server.rb +88 -0
- data/lib/couchrest/core/view.rb +4 -0
- data/lib/couchrest/helper/pager.rb +103 -0
- data/lib/couchrest/helper/streamer.rb +44 -0
- data/lib/couchrest/mixins/attachments.rb +31 -0
- data/lib/couchrest/mixins/callbacks.rb +483 -0
- data/lib/couchrest/mixins/design_doc.rb +64 -0
- data/lib/couchrest/mixins/document_queries.rb +48 -0
- data/lib/couchrest/mixins/extended_attachments.rb +68 -0
- data/lib/couchrest/mixins/extended_document_mixins.rb +6 -0
- data/lib/couchrest/mixins/properties.rb +125 -0
- data/lib/couchrest/mixins/validation.rb +234 -0
- data/lib/couchrest/mixins/views.rb +168 -0
- data/lib/couchrest/mixins.rb +4 -0
- data/lib/couchrest/monkeypatches.rb +119 -0
- data/lib/couchrest/more/casted_model.rb +28 -0
- data/lib/couchrest/more/extended_document.rb +217 -0
- data/lib/couchrest/more/property.rb +40 -0
- data/lib/couchrest/support/blank.rb +42 -0
- data/lib/couchrest/support/class.rb +191 -0
- data/lib/couchrest/validation/auto_validate.rb +163 -0
- data/lib/couchrest/validation/contextual_validators.rb +78 -0
- data/lib/couchrest/validation/validation_errors.rb +118 -0
- data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
- data/lib/couchrest/validation/validators/confirmation_validator.rb +99 -0
- data/lib/couchrest/validation/validators/format_validator.rb +117 -0
- data/lib/couchrest/validation/validators/formats/email.rb +66 -0
- data/lib/couchrest/validation/validators/formats/url.rb +43 -0
- data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
- data/lib/couchrest/validation/validators/length_validator.rb +134 -0
- data/lib/couchrest/validation/validators/method_validator.rb +89 -0
- data/lib/couchrest/validation/validators/numeric_validator.rb +104 -0
- data/lib/couchrest/validation/validators/required_field_validator.rb +109 -0
- data/lib/couchrest.rb +189 -0
- data/spec/couchrest/core/couchrest_spec.rb +201 -0
- data/spec/couchrest/core/database_spec.rb +750 -0
- data/spec/couchrest/core/design_spec.rb +131 -0
- data/spec/couchrest/core/document_spec.rb +311 -0
- data/spec/couchrest/core/server_spec.rb +35 -0
- data/spec/couchrest/helpers/pager_spec.rb +122 -0
- data/spec/couchrest/helpers/streamer_spec.rb +23 -0
- data/spec/couchrest/more/casted_extended_doc_spec.rb +40 -0
- data/spec/couchrest/more/casted_model_spec.rb +97 -0
- data/spec/couchrest/more/extended_doc_attachment_spec.rb +129 -0
- data/spec/couchrest/more/extended_doc_spec.rb +509 -0
- data/spec/couchrest/more/extended_doc_view_spec.rb +206 -0
- data/spec/couchrest/more/property_spec.rb +129 -0
- data/spec/couchrest/support/class_spec.rb +59 -0
- data/spec/fixtures/attachments/README +3 -0
- data/spec/fixtures/attachments/couchdb.png +0 -0
- data/spec/fixtures/attachments/test.html +11 -0
- data/spec/fixtures/more/article.rb +34 -0
- data/spec/fixtures/more/card.rb +20 -0
- data/spec/fixtures/more/course.rb +14 -0
- data/spec/fixtures/more/event.rb +6 -0
- data/spec/fixtures/more/invoice.rb +17 -0
- data/spec/fixtures/more/person.rb +8 -0
- data/spec/fixtures/more/question.rb +6 -0
- data/spec/fixtures/more/service.rb +12 -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 +26 -0
- data/utils/remap.rb +27 -0
- data/utils/subset.rb +30 -0
- metadata +219 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module CouchRest
|
|
2
|
+
class Server
|
|
3
|
+
attr_accessor :uri, :uuid_batch_count, :available_databases
|
|
4
|
+
def initialize(server = 'http://127.0.0.1:5984', uuid_batch_count = 1000)
|
|
5
|
+
@uri = server
|
|
6
|
+
@uuid_batch_count = uuid_batch_count
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Lists all "available" databases.
|
|
10
|
+
# An available database, is a database that was specified
|
|
11
|
+
# as avaiable by your code.
|
|
12
|
+
# It allows to define common databases to use and reuse in your code
|
|
13
|
+
def available_databases
|
|
14
|
+
@available_databases ||= {}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Adds a new available database and create it unless it already exists
|
|
18
|
+
#
|
|
19
|
+
# Example:
|
|
20
|
+
#
|
|
21
|
+
# @couch = CouchRest::Server.new
|
|
22
|
+
# @couch.define_available_database(:default, "tech-blog")
|
|
23
|
+
#
|
|
24
|
+
def define_available_database(reference, db_name, create_unless_exists = true)
|
|
25
|
+
available_databases[reference.to_sym] = create_unless_exists ? database!(db_name) : database(db_name)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Checks that a database is set as available
|
|
29
|
+
#
|
|
30
|
+
# Example:
|
|
31
|
+
#
|
|
32
|
+
# @couch.available_database?(:default)
|
|
33
|
+
#
|
|
34
|
+
def available_database?(ref_or_name)
|
|
35
|
+
ref_or_name.is_a?(Symbol) ? available_databases.keys.include?(ref_or_name) : available_databases.values.map{|db| db.name}.include?(ref_or_name)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def default_database=(name, create_unless_exists = true)
|
|
39
|
+
define_available_database(:default, name, create_unless_exists = true)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def default_database
|
|
43
|
+
available_databases[:default]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Lists all databases on the server
|
|
47
|
+
def databases
|
|
48
|
+
CouchRest.get "#{@uri}/_all_dbs"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Returns a CouchRest::Database for the given name
|
|
52
|
+
def database(name)
|
|
53
|
+
CouchRest::Database.new(self, name)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Creates the database if it doesn't exist
|
|
57
|
+
def database!(name)
|
|
58
|
+
create_db(name) rescue nil
|
|
59
|
+
database(name)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# GET the welcome message
|
|
63
|
+
def info
|
|
64
|
+
CouchRest.get "#{@uri}/"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Create a database
|
|
68
|
+
def create_db(name)
|
|
69
|
+
CouchRest.put "#{@uri}/#{CGI.escape(name)}"
|
|
70
|
+
database(name)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Restart the CouchDB instance
|
|
74
|
+
def restart!
|
|
75
|
+
CouchRest.post "#{@uri}/_restart"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Retrive an unused UUID from CouchDB. Server instances manage caching a list of unused UUIDs.
|
|
79
|
+
def next_uuid(count = @uuid_batch_count)
|
|
80
|
+
@uuids ||= []
|
|
81
|
+
if @uuids.empty?
|
|
82
|
+
@uuids = CouchRest.get("#{@uri}/_uuids?count=#{count}")["uuids"]
|
|
83
|
+
end
|
|
84
|
+
@uuids.pop
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
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,44 @@
|
|
|
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 = /^_/.match(name) ? "#{@db.root}/#{name}" : "#{@db.root}/_view/#{name}"
|
|
11
|
+
url = CouchRest.paramify_url urlst, params
|
|
12
|
+
# puts "stream #{url}"
|
|
13
|
+
first = nil
|
|
14
|
+
IO.popen("curl --silent #{url}") do |view|
|
|
15
|
+
first = view.gets # discard header
|
|
16
|
+
while line = view.gets
|
|
17
|
+
row = parse_line(line)
|
|
18
|
+
block.call row
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
parse_first(first)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def parse_line line
|
|
27
|
+
return nil unless line
|
|
28
|
+
if /(\{.*\}),?/.match(line.chomp)
|
|
29
|
+
JSON.parse($1)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def parse_first first
|
|
34
|
+
return nil unless first
|
|
35
|
+
parts = first.split(',')
|
|
36
|
+
parts.pop
|
|
37
|
+
line = parts.join(',')
|
|
38
|
+
JSON.parse("#{line}}")
|
|
39
|
+
rescue
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module CouchRest
|
|
2
|
+
module Mixins
|
|
3
|
+
module Attachments
|
|
4
|
+
|
|
5
|
+
# saves an attachment directly to couchdb
|
|
6
|
+
def put_attachment(name, file, options={})
|
|
7
|
+
raise ArgumentError, "doc must be saved" unless self.rev
|
|
8
|
+
raise ArgumentError, "doc.database required to put_attachment" unless database
|
|
9
|
+
result = database.put_attachment(self, name, file, options)
|
|
10
|
+
self['_rev'] = result['rev']
|
|
11
|
+
result['ok']
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# returns an attachment's data
|
|
15
|
+
def fetch_attachment(name)
|
|
16
|
+
raise ArgumentError, "doc must be saved" unless self.rev
|
|
17
|
+
raise ArgumentError, "doc.database required to put_attachment" unless database
|
|
18
|
+
database.fetch_attachment(self, name)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# deletes an attachment directly from couchdb
|
|
22
|
+
def delete_attachment(name)
|
|
23
|
+
raise ArgumentError, "doc.database required to delete_attachment" unless database
|
|
24
|
+
result = database.delete_attachment(self, name)
|
|
25
|
+
self['_rev'] = result['rev']
|
|
26
|
+
result['ok']
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|