will-couchrest 0.32.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.
Files changed (98) hide show
  1. data/LICENSE +176 -0
  2. data/README.md +165 -0
  3. data/Rakefile +74 -0
  4. data/THANKS.md +18 -0
  5. data/examples/model/example.rb +144 -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 +65 -0
  17. data/lib/couchrest.rb +199 -0
  18. data/lib/couchrest/commands/generate.rb +71 -0
  19. data/lib/couchrest/commands/push.rb +103 -0
  20. data/lib/couchrest/core/adapters/restclient.rb +35 -0
  21. data/lib/couchrest/core/database.rb +317 -0
  22. data/lib/couchrest/core/design.rb +79 -0
  23. data/lib/couchrest/core/document.rb +83 -0
  24. data/lib/couchrest/core/http_abstraction.rb +48 -0
  25. data/lib/couchrest/core/response.rb +16 -0
  26. data/lib/couchrest/core/server.rb +88 -0
  27. data/lib/couchrest/core/view.rb +4 -0
  28. data/lib/couchrest/helper/pager.rb +103 -0
  29. data/lib/couchrest/helper/streamer.rb +44 -0
  30. data/lib/couchrest/helper/upgrade.rb +51 -0
  31. data/lib/couchrest/mixins.rb +4 -0
  32. data/lib/couchrest/mixins/attachments.rb +31 -0
  33. data/lib/couchrest/mixins/callbacks.rb +483 -0
  34. data/lib/couchrest/mixins/class_proxy.rb +116 -0
  35. data/lib/couchrest/mixins/collection.rb +225 -0
  36. data/lib/couchrest/mixins/design_doc.rb +103 -0
  37. data/lib/couchrest/mixins/document_queries.rb +82 -0
  38. data/lib/couchrest/mixins/extended_attachments.rb +74 -0
  39. data/lib/couchrest/mixins/extended_document_mixins.rb +8 -0
  40. data/lib/couchrest/mixins/properties.rb +158 -0
  41. data/lib/couchrest/mixins/validation.rb +257 -0
  42. data/lib/couchrest/mixins/views.rb +173 -0
  43. data/lib/couchrest/monkeypatches.rb +113 -0
  44. data/lib/couchrest/more/casted_model.rb +29 -0
  45. data/lib/couchrest/more/extended_document.rb +246 -0
  46. data/lib/couchrest/more/property.rb +40 -0
  47. data/lib/couchrest/support/blank.rb +42 -0
  48. data/lib/couchrest/support/class.rb +176 -0
  49. data/lib/couchrest/support/rails.rb +35 -0
  50. data/lib/couchrest/validation/auto_validate.rb +161 -0
  51. data/lib/couchrest/validation/contextual_validators.rb +78 -0
  52. data/lib/couchrest/validation/validation_errors.rb +125 -0
  53. data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
  54. data/lib/couchrest/validation/validators/confirmation_validator.rb +99 -0
  55. data/lib/couchrest/validation/validators/format_validator.rb +117 -0
  56. data/lib/couchrest/validation/validators/formats/email.rb +66 -0
  57. data/lib/couchrest/validation/validators/formats/url.rb +43 -0
  58. data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
  59. data/lib/couchrest/validation/validators/length_validator.rb +134 -0
  60. data/lib/couchrest/validation/validators/method_validator.rb +89 -0
  61. data/lib/couchrest/validation/validators/numeric_validator.rb +104 -0
  62. data/lib/couchrest/validation/validators/required_field_validator.rb +109 -0
  63. data/spec/couchrest/core/couchrest_spec.rb +201 -0
  64. data/spec/couchrest/core/database_spec.rb +700 -0
  65. data/spec/couchrest/core/design_spec.rb +138 -0
  66. data/spec/couchrest/core/document_spec.rb +267 -0
  67. data/spec/couchrest/core/server_spec.rb +35 -0
  68. data/spec/couchrest/helpers/pager_spec.rb +122 -0
  69. data/spec/couchrest/helpers/streamer_spec.rb +23 -0
  70. data/spec/couchrest/more/casted_extended_doc_spec.rb +75 -0
  71. data/spec/couchrest/more/casted_model_spec.rb +177 -0
  72. data/spec/couchrest/more/extended_doc_attachment_spec.rb +135 -0
  73. data/spec/couchrest/more/extended_doc_spec.rb +588 -0
  74. data/spec/couchrest/more/extended_doc_subclass_spec.rb +98 -0
  75. data/spec/couchrest/more/extended_doc_view_spec.rb +426 -0
  76. data/spec/couchrest/more/property_spec.rb +169 -0
  77. data/spec/fixtures/attachments/README +3 -0
  78. data/spec/fixtures/attachments/couchdb.png +0 -0
  79. data/spec/fixtures/attachments/test.html +11 -0
  80. data/spec/fixtures/more/article.rb +34 -0
  81. data/spec/fixtures/more/card.rb +22 -0
  82. data/spec/fixtures/more/cat.rb +18 -0
  83. data/spec/fixtures/more/course.rb +14 -0
  84. data/spec/fixtures/more/event.rb +6 -0
  85. data/spec/fixtures/more/invoice.rb +17 -0
  86. data/spec/fixtures/more/person.rb +8 -0
  87. data/spec/fixtures/more/question.rb +6 -0
  88. data/spec/fixtures/more/service.rb +12 -0
  89. data/spec/fixtures/views/lib.js +3 -0
  90. data/spec/fixtures/views/test_view/lib.js +3 -0
  91. data/spec/fixtures/views/test_view/only-map.js +4 -0
  92. data/spec/fixtures/views/test_view/test-map.js +3 -0
  93. data/spec/fixtures/views/test_view/test-reduce.js +3 -0
  94. data/spec/spec.opts +6 -0
  95. data/spec/spec_helper.rb +37 -0
  96. data/utils/remap.rb +27 -0
  97. data/utils/subset.rb +30 -0
  98. metadata +198 -0
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.dirname(__FILE__)) + '/../../couchrest'
4
+
5
+ cr = CouchRest.new("http://127.0.0.1:5984")
6
+ @db = cr.database('word-count-example')
7
+ @word_memoizer = {}
8
+
9
+ def probable_follower_for(word)
10
+ @word_memoizer[word] ||= @db.view('markov/chain-reduce', :startkey => [word,nil], :endkey => [word,{}],:group_level => 2)
11
+
12
+ # puts
13
+ # puts "search #{word} #{wprobs[word]['rows'].length}"
14
+ # @word_memoizer[word]['rows'].sort_by{|r|r['value']}.each{|r|puts [r['value'],r['key']].inspect}
15
+
16
+ rows = @word_memoizer[word]['rows'].select{|r|(r['key'][1]!='')}.sort_by{|r|r['value']}
17
+ row = rows[(-1*[rows.length,5].min)..-1].sort_by{rand}[0]
18
+ row ? row['key'][1] : nil
19
+ end
20
+
21
+
22
+ word = ARGV[0]
23
+ words = [word]
24
+
25
+ while word
26
+ $stdout.print ' ' if words.length > 1
27
+ $stdout.print word
28
+ $stdout.flush
29
+ word = probable_follower_for(word)
30
+ words << word
31
+ end
32
+
33
+ $stdout.print '.'
34
+ $stdout.flush
35
+ puts
36
+
37
+ # `say #{words.join(' ')}`
38
+
@@ -0,0 +1,3 @@
1
+ function(doc) {
2
+ doc.title && doc.chunk && emit([doc.title, doc.chunk],null);
3
+ }
@@ -0,0 +1 @@
1
+ function(doc){if(doc.text && doc.text.match(/united/)) emit([doc.title, doc.chunk],null)}
@@ -0,0 +1,6 @@
1
+ function(doc){
2
+ var words = doc.text.split(/\W/).filter(function(w) {return w.length > 0}).map(function(w){return w.toLowerCase()});
3
+ for (var i = 0, l = words.length; i < l; i++) {
4
+ emit(words.slice(i,4),doc.title);
5
+ }
6
+ }
@@ -0,0 +1,7 @@
1
+ function(key,vs,c){
2
+ if (c) {
3
+ return sum(vs);
4
+ } else {
5
+ return vs.length;
6
+ }
7
+ }
@@ -0,0 +1,6 @@
1
+ function(doc){
2
+ var words = doc.text.split(/\W/).map(function(w){return w.toLowerCase()});
3
+ words.forEach(function(word){
4
+ if (word.length > 0) emit([word,doc.title],1);
5
+ });
6
+ }
@@ -0,0 +1,3 @@
1
+ function(key,values){
2
+ return sum(values);
3
+ }
@@ -0,0 +1,46 @@
1
+ require 'rubygems'
2
+ require 'couchrest'
3
+
4
+ couch = CouchRest.new("http://127.0.0.1:5984")
5
+ db = couch.database('word-count-example')
6
+ db.delete! rescue nil
7
+ db = couch.create_db('word-count-example')
8
+
9
+ books = {
10
+ 'outline-of-science.txt' => 'http://www.gutenberg.org/files/20417/20417.txt',
11
+ 'ulysses.txt' => 'http://www.gutenberg.org/dirs/etext03/ulyss12.txt',
12
+ 'america.txt' => 'http://www.gutenberg.org/files/16960/16960.txt',
13
+ 'da-vinci.txt' => 'http://www.gutenberg.org/dirs/etext04/7ldv110.txt'
14
+ }
15
+
16
+ books.each do |file, url|
17
+ pathfile = File.join(File.dirname(__FILE__),file)
18
+ `curl #{url} > #{pathfile}` unless File.exists?(pathfile)
19
+ end
20
+
21
+
22
+ books.keys.each do |book|
23
+ title = book.split('.')[0]
24
+ puts title
25
+ File.open(File.join(File.dirname(__FILE__),book),'r') do |file|
26
+ lines = []
27
+ chunk = 0
28
+ while line = file.gets
29
+ lines << line
30
+ if lines.length > 10
31
+ db.save({
32
+ :title => title,
33
+ :chunk => chunk,
34
+ :text => lines.join('')
35
+ })
36
+ chunk += 1
37
+ puts chunk
38
+ lines = []
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ # puts "The books have been stored in your CouchDB. To initiate the MapReduce process, visit http://127.0.0.1:5984/_utils/ in your browser and click 'word-count-example', then select view 'words' or 'count'. The process could take about 15 minutes on an average MacBook."
45
+
46
+
@@ -0,0 +1,40 @@
1
+ require 'rubygems'
2
+ require 'couchrest'
3
+
4
+ couch = CouchRest.new("http://127.0.0.1:5984")
5
+ db = couch.database('word-count-example')
6
+
7
+ puts "Now that we've parsed all those books into CouchDB, the queries we can run are incredibly flexible."
8
+ puts "\nThe simplest query we can run is the total word count for all words in all documents:"
9
+ puts "this will take a few minutes the first time. if it times out, just rerun this script in a few few minutes."
10
+ puts db.view('word_count/words').inspect
11
+
12
+ puts "\nWe can also narrow the query down to just one word, across all documents. Here is the count for 'flight' in all three books:"
13
+
14
+ word = 'flight'
15
+ params = {
16
+ :startkey => [word],
17
+ :endkey => [word,{}]
18
+ }
19
+
20
+ puts db.view('word_count/words',params).inspect
21
+
22
+ puts "\nWe scope the query using startkey and endkey params to take advantage of CouchDB's collation ordering. Here are the params for the last query:"
23
+ puts params.inspect
24
+
25
+ puts "\nWe can also count words on a per-title basis."
26
+
27
+ title = 'da-vinci'
28
+ params = {
29
+ :key => [word, title]
30
+ }
31
+
32
+ puts db.view('word_count/words',params).inspect
33
+
34
+
35
+ puts "\nHere are the params for 'flight' in the da-vinci book:"
36
+ puts params.inspect
37
+ puts
38
+ puts 'The url looks like this:'
39
+ puts 'http://127.0.0.1:5984/word-count-example/_view/word_count/count?key=["flight","da-vinci"]'
40
+ puts "\nTry dropping that in your browser..."
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ require 'couchrest'
3
+
4
+ couch = CouchRest.new("http://127.0.0.1:5984")
5
+ db = couch.database('word-count-example')
6
+
7
+ word_count = {
8
+ :map => 'function(doc){
9
+ var words = doc.text.split(/\W/);
10
+ words.forEach(function(word){
11
+ if (word.length > 0) emit([word,doc.title],1);
12
+ });
13
+ }',
14
+ :reduce => 'function(key,combine){
15
+ return sum(combine);
16
+ }'
17
+ }
18
+
19
+ db.delete db.get("_design/word_count") rescue nil
20
+
21
+ db.save({
22
+ "_id" => "_design/word_count",
23
+ :views => {
24
+ :words => word_count
25
+ }
26
+ })
data/history.txt ADDED
@@ -0,0 +1,65 @@
1
+ == 0.40
2
+
3
+ === Notes
4
+
5
+ This release slightly modifies the API and if you were using a previous version of CouchRest,
6
+ you will need to make the following modifications.
7
+
8
+
9
+
10
+ * Major enhancements
11
+
12
+ * Added a new Rack logger middleware letting you log/save requests/queries (Matt Aimonetti)
13
+
14
+ * Minor enhancements
15
+
16
+ * Bug fix: made ExtendedDocument#all compatible with Couch 0.10 (tc)
17
+
18
+ == 0.32
19
+
20
+ * Major enhancements
21
+
22
+ * ExtendedDocument.get doesn't raise an exception anymore. If no documents are found nil is returned.
23
+ * ExtendedDocument.get! works the say #get used to work and will raise an exception if a document isn't found.
24
+
25
+ * Minor enhancements
26
+
27
+ * Bug fix: Model.all(:keys => [1,2]) was not working (Matt Aimonetti)
28
+ * Added ValidationErrors#count in order to play nicely with Rails (Peter Wagenet)
29
+ * Bug fix: class proxy design doc refresh (Daniel Kirsh)
30
+ * Bug fix: the count method on the proxy collection was missing (Daniel Kirsch)
31
+ * Added #amount_pages to a paginated collection. (Matt Aimonetti)
32
+
33
+ == 0.31
34
+
35
+ * Major enhancements
36
+
37
+ * Created an abstraction HTTP layer to support different http adapters (Matt Aimonetti)
38
+ * Added ExtendedDocument.create({}) and #create!({}) so you don't have to do Model.new.create (Matt Aimonetti)
39
+
40
+ * Minor enhancements
41
+
42
+ * Added an init.rb file for easy usage as a Rails plugin (Aaron Quint)
43
+ * Bug fix: pagination shouldn't die on empty results (Arnaud Berthomier)
44
+ * Optimized ExtendedDocument.count to run about 3x faster (Matt Aimonetti)
45
+ * Added Float casting (Ryan Felton & Matt Aimonetti)
46
+
47
+ == 0.30
48
+
49
+ * Major enhancements
50
+
51
+ * Added support for pagination (John Wood)
52
+ * Improved performance when initializing documents with timestamps (Matt Aimonetti)
53
+
54
+ * Minor enhancements
55
+
56
+ * Extended the API to retrieve an attachment URI (Matt Aimonetti)
57
+ * Bug fix: default value should be able to be set as false (Alexander Uvarov)
58
+ * Bug fix: validates_is_numeric should be able to properly validate a Float instance (Rob Kaufman)
59
+ * Bug fix: fixed the Timeout implementation (Seth Falcon)
60
+
61
+
62
+ ---
63
+
64
+ Unfortunately, before 0.30 we did not keep a track of the modifications made to CouchRest.
65
+ You can see the full commit history on GitHub: http://github.com/mattetti/couchrest/commits/master/
data/lib/couchrest.rb ADDED
@@ -0,0 +1,199 @@
1
+ # Copyright 2008 J. Chris Anderson
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'rubygems'
16
+ begin
17
+ require 'json'
18
+ rescue LoadError
19
+ raise "You need install and require your own json compatible library since couchrest rest couldn't load the json/json_pure gem" unless Kernel.const_defined?("JSON")
20
+ end
21
+ require 'rest_client'
22
+
23
+ $:.unshift File.dirname(__FILE__) unless
24
+ $:.include?(File.dirname(__FILE__)) ||
25
+ $:.include?(File.expand_path(File.dirname(__FILE__)))
26
+
27
+ require 'couchrest/monkeypatches'
28
+
29
+ # = CouchDB, close to the metal
30
+ module CouchRest
31
+ VERSION = '0.32.1' unless self.const_defined?("VERSION")
32
+
33
+ autoload :Server, 'couchrest/core/server'
34
+ autoload :Database, 'couchrest/core/database'
35
+ autoload :Response, 'couchrest/core/response'
36
+ autoload :Document, 'couchrest/core/document'
37
+ autoload :Design, 'couchrest/core/design'
38
+ autoload :View, 'couchrest/core/view'
39
+ autoload :Model, 'couchrest/core/model'
40
+ autoload :Pager, 'couchrest/helper/pager'
41
+ autoload :FileManager, 'couchrest/helper/file_manager'
42
+ autoload :Streamer, 'couchrest/helper/streamer'
43
+ autoload :Upgrade, 'couchrest/helper/upgrade'
44
+
45
+ autoload :ExtendedDocument, 'couchrest/more/extended_document'
46
+ autoload :CastedModel, 'couchrest/more/casted_model'
47
+
48
+ require File.join(File.dirname(__FILE__), 'couchrest', 'core', 'http_abstraction')
49
+ require File.join(File.dirname(__FILE__), 'couchrest', 'mixins')
50
+
51
+ # The CouchRest module methods handle the basic JSON serialization
52
+ # and deserialization, as well as query parameters. The module also includes
53
+ # some helpers for tasks like instantiating a new Database or Server instance.
54
+ class << self
55
+
56
+ # extracted from Extlib
57
+ #
58
+ # Constantize tries to find a declared constant with the name specified
59
+ # in the string. It raises a NameError when the name is not in CamelCase
60
+ # or is not initialized.
61
+ #
62
+ # @example
63
+ # "Module".constantize #=> Module
64
+ # "Class".constantize #=> Class
65
+ def constantize(camel_cased_word)
66
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
67
+ raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
68
+ end
69
+
70
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
71
+ end
72
+
73
+ # extracted from Extlib
74
+ #
75
+ # Capitalizes the first word and turns underscores into spaces and strips _id.
76
+ # Like titleize, this is meant for creating pretty output.
77
+ #
78
+ # @example
79
+ # "employee_salary" #=> "Employee salary"
80
+ # "author_id" #=> "Author"
81
+ def humanize(lower_case_and_underscored_word)
82
+ lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
83
+ end
84
+
85
+ # todo, make this parse the url and instantiate a Server or Database instance
86
+ # depending on the specificity.
87
+ def new(*opts)
88
+ Server.new(*opts)
89
+ end
90
+
91
+ def parse url
92
+ case url
93
+ when /^http:\/\/(.*)\/(.*)\/(.*)/
94
+ host = $1
95
+ db = $2
96
+ docid = $3
97
+ when /^http:\/\/(.*)\/(.*)/
98
+ host = $1
99
+ db = $2
100
+ when /^http:\/\/(.*)/
101
+ host = $1
102
+ when /(.*)\/(.*)\/(.*)/
103
+ host = $1
104
+ db = $2
105
+ docid = $3
106
+ when /(.*)\/(.*)/
107
+ host = $1
108
+ db = $2
109
+ else
110
+ db = url
111
+ end
112
+
113
+ db = nil if db && db.empty?
114
+
115
+ {
116
+ :host => host || "127.0.0.1:5984",
117
+ :database => db,
118
+ :doc => docid
119
+ }
120
+ end
121
+
122
+ # set proxy to use
123
+ def proxy url
124
+ HttpAbstraction.proxy = url
125
+ end
126
+
127
+ # ensure that a database exists
128
+ # creates it if it isn't already there
129
+ # returns it after it's been created
130
+ def database! url
131
+ parsed = parse url
132
+ cr = CouchRest.new(parsed[:host])
133
+ cr.database!(parsed[:database])
134
+ end
135
+
136
+ def database url
137
+ parsed = parse url
138
+ cr = CouchRest.new(parsed[:host])
139
+ cr.database(parsed[:database])
140
+ end
141
+
142
+ def put(uri, doc = nil)
143
+ payload = doc.to_json if doc
144
+ begin
145
+ JSON.parse(HttpAbstraction.put(uri, payload))
146
+ rescue Exception => e
147
+ if $DEBUG
148
+ raise "Error while sending a PUT request #{uri}\npayload: #{payload.inspect}\n#{e}"
149
+ else
150
+ raise e
151
+ end
152
+ end
153
+ end
154
+
155
+ def get(uri)
156
+ begin
157
+ JSON.parse(HttpAbstraction.get(uri), :max_nesting => false)
158
+ rescue => e
159
+ if $DEBUG
160
+ raise "Error while sending a GET request #{uri}\n: #{e}"
161
+ else
162
+ raise e
163
+ end
164
+ end
165
+ end
166
+
167
+ def post uri, doc = nil
168
+ payload = doc.to_json if doc
169
+ begin
170
+ JSON.parse(HttpAbstraction.post(uri, payload))
171
+ rescue Exception => e
172
+ if $DEBUG
173
+ raise "Error while sending a POST request #{uri}\npayload: #{payload.inspect}\n#{e}"
174
+ else
175
+ raise e
176
+ end
177
+ end
178
+ end
179
+
180
+ def delete uri
181
+ JSON.parse(HttpAbstraction.delete(uri))
182
+ end
183
+
184
+ def copy uri, destination
185
+ JSON.parse(HttpAbstraction.copy(uri, {'Destination' => destination}))
186
+ end
187
+
188
+ def paramify_url url, params = {}
189
+ if params && !params.empty?
190
+ query = params.collect do |k,v|
191
+ v = v.to_json if %w{key startkey endkey}.include?(k.to_s)
192
+ "#{k}=#{CGI.escape(v.to_s)}"
193
+ end.join("&")
194
+ url = "#{url}?#{query}"
195
+ end
196
+ url
197
+ end
198
+ end # class << self
199
+ end