jchris-couchrest 0.2.2 → 0.7.99

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/README +39 -0
  2. data/Rakefile +3 -54
  3. data/lib/couchrest.rb +39 -161
  4. data/lib/database.rb +83 -0
  5. data/script/couchdir +58 -0
  6. data/script/couchview +158 -0
  7. data/spec/couchrest_spec.rb +86 -0
  8. data/spec/database_spec.rb +407 -0
  9. metadata +15 -138
  10. data/LICENSE +0 -176
  11. data/README.md +0 -93
  12. data/THANKS.md +0 -18
  13. data/examples/model/example.rb +0 -144
  14. data/examples/word_count/markov +0 -38
  15. data/examples/word_count/views/books/chunked-map.js +0 -3
  16. data/examples/word_count/views/books/united-map.js +0 -1
  17. data/examples/word_count/views/markov/chain-map.js +0 -6
  18. data/examples/word_count/views/markov/chain-reduce.js +0 -7
  19. data/examples/word_count/views/word_count/count-map.js +0 -6
  20. data/examples/word_count/views/word_count/count-reduce.js +0 -3
  21. data/examples/word_count/word_count.rb +0 -46
  22. data/examples/word_count/word_count_query.rb +0 -40
  23. data/examples/word_count/word_count_views.rb +0 -26
  24. data/lib/couchrest/commands/generate.rb +0 -71
  25. data/lib/couchrest/commands/push.rb +0 -103
  26. data/lib/couchrest/core/database.rb +0 -318
  27. data/lib/couchrest/core/design.rb +0 -89
  28. data/lib/couchrest/core/document.rb +0 -96
  29. data/lib/couchrest/core/response.rb +0 -16
  30. data/lib/couchrest/core/server.rb +0 -88
  31. data/lib/couchrest/core/view.rb +0 -4
  32. data/lib/couchrest/helper/pager.rb +0 -103
  33. data/lib/couchrest/helper/streamer.rb +0 -44
  34. data/lib/couchrest/helper/upgrade.rb +0 -51
  35. data/lib/couchrest/mixins.rb +0 -4
  36. data/lib/couchrest/mixins/attachments.rb +0 -31
  37. data/lib/couchrest/mixins/callbacks.rb +0 -483
  38. data/lib/couchrest/mixins/design_doc.rb +0 -64
  39. data/lib/couchrest/mixins/document_queries.rb +0 -48
  40. data/lib/couchrest/mixins/extended_attachments.rb +0 -68
  41. data/lib/couchrest/mixins/extended_document_mixins.rb +0 -6
  42. data/lib/couchrest/mixins/properties.rb +0 -125
  43. data/lib/couchrest/mixins/validation.rb +0 -234
  44. data/lib/couchrest/mixins/views.rb +0 -168
  45. data/lib/couchrest/monkeypatches.rb +0 -119
  46. data/lib/couchrest/more/casted_model.rb +0 -28
  47. data/lib/couchrest/more/extended_document.rb +0 -217
  48. data/lib/couchrest/more/property.rb +0 -40
  49. data/lib/couchrest/support/blank.rb +0 -42
  50. data/lib/couchrest/support/class.rb +0 -191
  51. data/lib/couchrest/validation/auto_validate.rb +0 -163
  52. data/lib/couchrest/validation/contextual_validators.rb +0 -78
  53. data/lib/couchrest/validation/validation_errors.rb +0 -118
  54. data/lib/couchrest/validation/validators/absent_field_validator.rb +0 -74
  55. data/lib/couchrest/validation/validators/confirmation_validator.rb +0 -99
  56. data/lib/couchrest/validation/validators/format_validator.rb +0 -117
  57. data/lib/couchrest/validation/validators/formats/email.rb +0 -66
  58. data/lib/couchrest/validation/validators/formats/url.rb +0 -43
  59. data/lib/couchrest/validation/validators/generic_validator.rb +0 -120
  60. data/lib/couchrest/validation/validators/length_validator.rb +0 -134
  61. data/lib/couchrest/validation/validators/method_validator.rb +0 -89
  62. data/lib/couchrest/validation/validators/numeric_validator.rb +0 -104
  63. data/lib/couchrest/validation/validators/required_field_validator.rb +0 -109
  64. data/spec/couchrest/core/couchrest_spec.rb +0 -201
  65. data/spec/couchrest/core/database_spec.rb +0 -745
  66. data/spec/couchrest/core/design_spec.rb +0 -131
  67. data/spec/couchrest/core/document_spec.rb +0 -311
  68. data/spec/couchrest/core/server_spec.rb +0 -35
  69. data/spec/couchrest/helpers/pager_spec.rb +0 -122
  70. data/spec/couchrest/helpers/streamer_spec.rb +0 -23
  71. data/spec/couchrest/more/casted_extended_doc_spec.rb +0 -40
  72. data/spec/couchrest/more/casted_model_spec.rb +0 -98
  73. data/spec/couchrest/more/extended_doc_attachment_spec.rb +0 -130
  74. data/spec/couchrest/more/extended_doc_spec.rb +0 -509
  75. data/spec/couchrest/more/extended_doc_view_spec.rb +0 -207
  76. data/spec/couchrest/more/property_spec.rb +0 -130
  77. data/spec/couchrest/support/class_spec.rb +0 -59
  78. data/spec/fixtures/attachments/README +0 -3
  79. data/spec/fixtures/attachments/couchdb.png +0 -0
  80. data/spec/fixtures/attachments/test.html +0 -11
  81. data/spec/fixtures/more/article.rb +0 -34
  82. data/spec/fixtures/more/card.rb +0 -20
  83. data/spec/fixtures/more/course.rb +0 -14
  84. data/spec/fixtures/more/event.rb +0 -6
  85. data/spec/fixtures/more/invoice.rb +0 -17
  86. data/spec/fixtures/more/person.rb +0 -8
  87. data/spec/fixtures/more/question.rb +0 -6
  88. data/spec/fixtures/more/service.rb +0 -12
  89. data/spec/fixtures/views/lib.js +0 -3
  90. data/spec/fixtures/views/test_view/lib.js +0 -3
  91. data/spec/fixtures/views/test_view/only-map.js +0 -4
  92. data/spec/fixtures/views/test_view/test-map.js +0 -3
  93. data/spec/fixtures/views/test_view/test-reduce.js +0 -3
  94. data/spec/spec.opts +0 -6
  95. data/spec/spec_helper.rb +0 -26
  96. data/utils/remap.rb +0 -27
  97. data/utils/subset.rb +0 -30
data/README ADDED
@@ -0,0 +1,39 @@
1
+ Couchrest is a loose port of the couch.js library from CouchDB’s Futon admin interface, which I find to be concise, clear, and well designed.
2
+
3
+ I prefer to stay close to the metal, especially when the metal is as clean and simple as CouchDB’s HTTP API. The main thing Couchrest does for you is manage the Ruby <=> JSON serialization, and point your actions (database creation, document CRUD, view creation and queries, etc) at the appropriate API endpoints.
4
+
5
+ The core of Couchrest is Heroku’s excellent REST Client Ruby HTTP wrapper. REST Client takes all the nastyness of Net::HTTP and gives is a pretty face, while still giving you more control than Open-URI. I recommend it anytime you’re interfacing with a well-defined API. (We use it for Grabb.it’s Tumblr integration, and it works like a charm.)
6
+
7
+ The most complete source of documentation are the spec files. To ensure that your environment is setup for successful CouchRest operation, from the project root directory run `autotest` or `spec spec` (requires RSpec and optionally ZenTest for autotest support).
8
+
9
+ Quick Start:
10
+
11
+ @cr = CouchRest.new("http://localhost:5984")
12
+ @db = @cr.create_db('couchrest-test')
13
+ response = @db.save({:key => 'value', 'another key' => 'another value'})
14
+ doc = @db.get(response['id'])
15
+ puts doc.inspect
16
+
17
+
18
+ Bulk Save:
19
+
20
+ @db.bulk_save([
21
+ {"wild" => "and random"},
22
+ {"mild" => "yet local"},
23
+ {"another" => ["set","of","keys"]}
24
+ ])
25
+ puts @db.documents.inspect # returns ids and revs of the current docs
26
+
27
+ Creating and Querying Views:
28
+
29
+ @db.save({
30
+ "_id" => "_design/first",
31
+ :views => {
32
+ :test => {
33
+ :map => "function(doc){for(var w in doc){ if(!w.match(/^_/))emit(w,doc[w])}}"
34
+ }
35
+ }
36
+ })
37
+ puts @db.view('first/test')['rows'].inspect
38
+
39
+
data/Rakefile CHANGED
@@ -1,66 +1,15 @@
1
1
  require 'rake'
2
- require "rake/rdoctask"
3
- require 'rake/gempackagetask'
4
- require File.join(File.expand_path(File.dirname(__FILE__)),'lib','couchrest')
5
-
6
-
7
- begin
8
- require 'spec/rake/spectask'
9
- rescue LoadError
10
- puts <<-EOS
11
- To use rspec for testing you must install rspec gem:
12
- gem install rspec
13
- EOS
14
- exit(0)
15
- end
16
-
17
- spec = Gem::Specification.new do |s|
18
- s.name = "couchrest"
19
- s.version = CouchRest::VERSION
20
- s.date = "2008-11-22"
21
- s.summary = "Lean and RESTful interface to CouchDB."
22
- s.email = "jchris@apache.org"
23
- s.homepage = "http://github.com/jchris/couchrest"
24
- s.description = "CouchRest provides a simple interface on top of CouchDB's RESTful HTTP API, as well as including some utility scripts for managing views and attachments."
25
- s.has_rdoc = true
26
- s.authors = ["J. Chris Anderson", "Matt Aimonetti"]
27
- s.files = %w( LICENSE README.md Rakefile THANKS.md ) +
28
- Dir["{examples,lib,spec,utils}/**/*"] -
29
- Dir["spec/tmp"]
30
- s.extra_rdoc_files = %w( README.md LICENSE THANKS.md )
31
- s.require_path = "lib"
32
- s.add_dependency("json", ">= 1.1.2")
33
- s.add_dependency("rest-client", ">= 0.5")
34
- s.add_dependency("mime-types", ">= 1.15")
35
- end
36
-
37
-
38
- desc "create .gemspec file (useful for github)"
39
- task :gemspec do
40
- filename = "#{spec.name}.gemspec"
41
- File.open(filename, "w") do |f|
42
- f.puts spec.to_ruby
43
- end
44
- end
2
+ require 'spec/rake/spectask'
45
3
 
46
4
  desc "Run all specs"
47
5
  Spec::Rake::SpecTask.new('spec') do |t|
48
- t.spec_files = FileList['spec/**/*_spec.rb']
6
+ t.spec_files = FileList['spec/*_spec.rb']
49
7
  end
50
8
 
51
9
  desc "Print specdocs"
52
10
  Spec::Rake::SpecTask.new(:doc) do |t|
53
- t.spec_opts = ["--format", "specdoc"]
11
+ t.spec_opts = ["--format", "specdoc", "--dry-run"]
54
12
  t.spec_files = FileList['spec/*_spec.rb']
55
13
  end
56
14
 
57
- desc "Generate the rdoc"
58
- Rake::RDocTask.new do |rdoc|
59
- files = ["README.rdoc", "LICENSE", "lib/**/*.rb"]
60
- rdoc.rdoc_files.add(files)
61
- rdoc.main = "README.rdoc"
62
- rdoc.title = "CouchRest: Ruby CouchDB, close to the metal"
63
- end
64
-
65
- desc "Run the rspec"
66
15
  task :default => :spec
data/lib/couchrest.rb CHANGED
@@ -1,183 +1,61 @@
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
1
  require "rubygems"
16
- gem 'json'
17
2
  require 'json'
18
- gem 'rest-client'
19
3
  require 'rest_client'
20
4
 
21
- $:.unshift File.dirname(__FILE__) unless
22
- $:.include?(File.dirname(__FILE__)) ||
23
- $:.include?(File.expand_path(File.dirname(__FILE__)))
24
-
25
- $COUCHREST_DEBUG ||= false
26
-
27
- require 'couchrest/monkeypatches'
5
+ require File.dirname(__FILE__) + '/database'
28
6
 
29
- # = CouchDB, close to the metal
30
- module CouchRest
31
- VERSION = '0.2.2' unless self.const_defined?("VERSION")
7
+ class CouchRest
8
+ attr_accessor :uri
9
+ def initialize server = 'http://localhost:5984'
10
+ @uri = server
11
+ end
32
12
 
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'
13
+ # list all databases on the server
14
+ def databases
15
+ CouchRest.get "#{@uri}/_all_dbs"
16
+ end
44
17
 
45
- autoload :ExtendedDocument, 'couchrest/more/extended_document'
46
- autoload :CastedModel, 'couchrest/more/casted_model'
18
+ def database name
19
+ CouchRest::Database.new(@uri, name)
20
+ end
47
21
 
48
- require File.join(File.dirname(__FILE__), 'couchrest', 'mixins')
49
-
50
- # The CouchRest module methods handle the basic JSON serialization
51
- # and deserialization, as well as query parameters. The module also includes
52
- # some helpers for tasks like instantiating a new Database or Server instance.
53
- class << self
22
+ # get the welcome message
23
+ def info
24
+ CouchRest.get "#{@uri}/"
25
+ end
54
26
 
55
- # extracted from Extlib
56
- #
57
- # Constantize tries to find a declared constant with the name specified
58
- # in the string. It raises a NameError when the name is not in CamelCase
59
- # or is not initialized.
60
- #
61
- # @example
62
- # "Module".constantize #=> Module
63
- # "Class".constantize #=> Class
64
- def constantize(camel_cased_word)
65
- unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
66
- raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
67
- end
68
-
69
- Object.module_eval("::#{$1}", __FILE__, __LINE__)
70
- end
71
-
72
- # todo, make this parse the url and instantiate a Server or Database instance
73
- # depending on the specificity.
74
- def new(*opts)
75
- Server.new(*opts)
76
- end
77
-
78
- def parse url
79
- case url
80
- when /^http:\/\/(.*)\/(.*)\/(.*)/
81
- host = $1
82
- db = $2
83
- docid = $3
84
- when /^http:\/\/(.*)\/(.*)/
85
- host = $1
86
- db = $2
87
- when /^http:\/\/(.*)/
88
- host = $1
89
- when /(.*)\/(.*)\/(.*)/
90
- host = $1
91
- db = $2
92
- docid = $3
93
- when /(.*)\/(.*)/
94
- host = $1
95
- db = $2
96
- else
97
- db = url
98
- end
99
-
100
- db = nil if db && db.empty?
101
-
102
- {
103
- :host => host || "127.0.0.1:5984",
104
- :database => db,
105
- :doc => docid
106
- }
107
- end
108
-
109
- # set proxy for RestClient to use
110
- def proxy url
111
- RestClient.proxy = url
112
- end
27
+ # restart the couchdb instance
28
+ def restart!
29
+ CouchRest.post "#{@uri}/_restart"
30
+ end
31
+
32
+ # create a database
33
+ def create_db name
34
+ CouchRest.put "#{@uri}/#{name}"
35
+ database name
36
+ end
113
37
 
114
- # ensure that a database exists
115
- # creates it if it isn't already there
116
- # returns it after it's been created
117
- def database! url
118
- parsed = parse url
119
- cr = CouchRest.new(parsed[:host])
120
- cr.database!(parsed[:database])
38
+ class << self
39
+ def put uri, doc = nil
40
+ payload = doc.to_json if doc
41
+ JSON.parse(RestClient.put(uri, payload))
121
42
  end
122
43
 
123
- def database url
124
- parsed = parse url
125
- cr = CouchRest.new(parsed[:host])
126
- cr.database(parsed[:database])
44
+ def get uri
45
+ JSON.parse(RestClient.get(uri), :max_nesting => false)
127
46
  end
128
47
 
129
- def put(uri, doc = nil)
130
- payload = doc.to_json if doc
131
- begin
132
- JSON.parse(RestClient.put(uri, payload))
133
- rescue Exception => e
134
- if $COUCHREST_DEBUG == true
135
- raise "Error while sending a PUT request #{uri}\npayload: #{payload.inspect}\n#{e}"
136
- else
137
- raise e
138
- end
139
- end
140
- end
141
-
142
- def get(uri)
143
- begin
144
- JSON.parse(RestClient.get(uri), :max_nesting => false)
145
- rescue => e
146
- if $COUCHREST_DEBUG == true
147
- raise "Error while sending a GET request #{uri}\n: #{e}"
148
- else
149
- raise e
150
- end
151
- end
152
- end
153
-
154
48
  def post uri, doc = nil
155
49
  payload = doc.to_json if doc
156
- begin
157
- JSON.parse(RestClient.post(uri, payload))
158
- rescue Exception => e
159
- if $COUCHREST_DEBUG == true
160
- raise "Error while sending a POST request #{uri}\npayload: #{payload.inspect}\n#{e}"
161
- else
162
- raise e
163
- end
164
- end
50
+ JSON.parse(RestClient.post(uri, payload))
165
51
  end
166
-
52
+
167
53
  def delete uri
168
54
  JSON.parse(RestClient.delete(uri))
169
55
  end
170
56
 
171
- def copy uri, destination
172
- JSON.parse(RestClient.copy(uri, {'Destination' => destination}))
173
- end
174
-
175
- def move uri, destination
176
- JSON.parse(RestClient.move(uri, {'Destination' => destination}))
177
- end
178
-
179
- def paramify_url url, params = {}
180
- if params && !params.empty?
57
+ def paramify_url url, params = nil
58
+ if params
181
59
  query = params.collect do |k,v|
182
60
  v = v.to_json if %w{key startkey endkey}.include?(k.to_s)
183
61
  "#{k}=#{CGI.escape(v.to_s)}"
@@ -186,5 +64,5 @@ module CouchRest
186
64
  end
187
65
  url
188
66
  end
189
- end # class << self
190
- end
67
+ end
68
+ end
data/lib/database.rb ADDED
@@ -0,0 +1,83 @@
1
+ require 'cgi'
2
+ require "base64"
3
+ require File.dirname(__FILE__) + '/couchrest'
4
+
5
+ class CouchRest
6
+ class Database
7
+ attr_accessor :host, :name
8
+ def initialize host, name
9
+ @name = name
10
+ @host = host
11
+ @root = "#{host}/#{name}"
12
+ end
13
+
14
+ def documents params = nil
15
+ url = CouchRest.paramify_url "#{@root}/_all_docs", params
16
+ CouchRest.get url
17
+ end
18
+
19
+ def temp_view funcs, params = nil
20
+ url = CouchRest.paramify_url "#{@root}/_temp_view", params
21
+ JSON.parse(RestClient.post(url, funcs.to_json, {"Content-Type" => 'application/json'}))
22
+ end
23
+
24
+ def view name, params = nil
25
+ url = CouchRest.paramify_url "#{@root}/_view/#{name}", params
26
+ CouchRest.get url
27
+ end
28
+
29
+ # experimental
30
+ def search params = nil
31
+ url = CouchRest.paramify_url "#{@root}/_search", params
32
+ CouchRest.get url
33
+ end
34
+
35
+ def get id
36
+ slug = CGI.escape(id)
37
+ CouchRest.get "#{@root}/#{slug}"
38
+ end
39
+
40
+ def fetch_attachment doc, name
41
+ doc = CGI.escape(doc)
42
+ name = CGI.escape(name)
43
+ RestClient.get "#{@root}/#{doc}/#{name}"
44
+ end
45
+
46
+ # PUT or POST depending on presence of _id attribute
47
+ def save doc
48
+ if doc['_attachments']
49
+ doc['_attachments'] = encode_attachments(doc['_attachments'])
50
+ end
51
+ if doc['_id']
52
+ slug = CGI.escape(doc['_id'])
53
+ CouchRest.put "#{@root}/#{slug}", doc
54
+ else
55
+ CouchRest.post "#{@root}", doc
56
+ end
57
+ end
58
+
59
+ def bulk_save docs
60
+ CouchRest.post "#{@root}/_bulk_docs", {:docs => docs}
61
+ end
62
+
63
+ def delete doc
64
+ slug = CGI.escape(doc['_id'])
65
+ CouchRest.delete "#{@root}/#{slug}?rev=#{doc['_rev']}"
66
+ end
67
+
68
+ def delete!
69
+ CouchRest.delete @root
70
+ end
71
+ private
72
+ def encode_attachments attachments
73
+ attachments.each do |k,v|
74
+ next if v['stub']
75
+ v['data'] = base64(v['data'])
76
+ end
77
+ attachments
78
+ end
79
+ def base64 data
80
+ Base64.encode64(data).gsub(/\s/,'')
81
+ end
82
+ end
83
+ end
data/script/couchdir ADDED
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ unless ARGV.length >= 2
4
+ puts "usage: couchdir path/to/directory db-name"
5
+ exit
6
+ end
7
+
8
+ dirname = ARGV[0].sub(/\/$/,'')
9
+ dbname = ARGV[1]
10
+
11
+
12
+
13
+ puts "Shoving #{dirname} into #{dbname}."
14
+
15
+ require File.expand_path(File.dirname(__FILE__)) + '/../couchrest'
16
+ require 'fileutils'
17
+
18
+ cr = CouchRest.new("http://localhost:5984")
19
+ @db = cr.database(dbname)
20
+
21
+ @content_types = {
22
+ "html" => "text/html",
23
+ "htm" => "text/html",
24
+ "png" => "image/png",
25
+ "css" => "text/css"
26
+ }
27
+
28
+ files = Dir.glob(File.join(dirname,"**","*"))
29
+ attachments = {}
30
+ files.each do |filename|
31
+ content = open(filename).read
32
+ aname = filename.split('/')
33
+ aname.shift
34
+ aname = aname.join('/')
35
+ attachments[aname] = {
36
+ "data" => content,
37
+ "content_type" => @content_types[aname.split('.').last]
38
+ }
39
+ end
40
+
41
+ puts attachments.keys.inspect
42
+
43
+ doc = @db.get(dirname) rescue nil
44
+
45
+ # puts "get: #{doc.inspect}"
46
+
47
+ if doc
48
+ doc["_attachments"] = attachments
49
+ else
50
+ doc = {
51
+ "_id" => dirname,
52
+ "_attachments" => attachments
53
+ }
54
+ end
55
+
56
+ # puts "saving: #{doc.inspect}"
57
+ @db.save(doc)
58
+ puts "saved"