2 # Copyright (c) 2007, Stuart Glaser <StuGlaser@gmail.com>
4 # Permission is hereby granted, free of charge, to any person
5 # obtaining a copy of this software and associated documentation
6 # files (the "Software"), to deal in the Software without
7 # restriction, including without limitation the rights to use,
8 # copy, modify, merge, publish, distribute, sublicense, and/or sell
9 # copies of the Software, and to permit persons to whom the
10 # Software is furnished to do so, subject to the following
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 # OTHER DEALINGS IN THE SOFTWARE.
28 # The JSON parser stupidly does not accept just strings.
29 def hacked_json_parse(json)
30 boxed = "[ " + json + " ]"
31 boxed_parsed = JSON.parse boxed
35 class CouchException < StandardError
36 attr_reader :couch, :response
38 def initialize(couch, response)
44 #json = JSON.parse @response.body
45 json = hacked_json_parse @response.body
46 json['error']['id'] + "\n" + json['error']['reason']
50 "#{@response.class}\n" +
51 "Couch Backtrace:" + couch_backtrace +
59 attr_accessor :host, :port
61 def initialize(host, port)
67 request Net::HTTP::Get.new(path)
70 def put(path, data) # TODO: support text/javascript
71 req = Net::HTTP::Put.new(path)
72 req['content-type'] = 'text/javascript'
73 req['content-type'] = 'application/json'
78 def post(path, data) # TODO: support text/javascript
79 req = Net::HTTP::Post.new(path)
85 request Net::HTTP::Delete.new(path)
89 resp = Net::HTTP.start(@host, @port) do |http|
92 if not resp.kind_of?(Net::HTTPSuccess)
93 raise CouchException.new(self, resp)
102 attr_accessor :server
105 def initialize(server, name)
111 resp = @server.get doc_path(doc_id)
112 #JSON.parse resp.body
113 hacked_json_parse resp.body
116 def put(doc_id, body)
117 resp = $server.put doc_path(doc_id), body.to_json
118 #JSON.parse resp.body
119 hacked_json_parse resp.body
123 resp = $server.post doc_path(''), body.to_json
124 #JSON.parse resp.body
125 hacked_json_parse resp.body
129 resp = $server.delete doc_path(doc_id)
130 #JSON.parse resp.body
131 hacked_json_parse resp.body
135 path = doc_path('_temp_view')
136 req = Net::HTTP::Post.new(path)
137 req['content-type'] = 'text/javascript'
142 def temp_view(body) # TODO: content-type
143 resp = $server.post doc_path('_temp_view'), body
144 #JSON.parse resp.body
145 hacked_json_parse resp.body
149 '/' + @name + '/' + doc_id
154 # Represents a document in a Couch database
159 attr_accessor :_attachments
162 @_attachments = [] if @_attachments.nil?
167 attr_reader :field_names
171 @field_names = [] if @field_names.nil?
180 doc.from_couch! $db.get(id)
182 rescue CouchException=>ex
183 return nil if ex.response.instance_of? Net::HTTPNotFound
189 def Doc.find_or_create(db, id)
191 return doc unless doc.nil?
208 results = if @id.nil? # POST
213 @_rev = results['rev']
219 raise "Never saved" if @id.nil?
220 self.from_couch! @db.get(@id)
225 results = @db.delete(@id + "?rev=#{@_rev}")
231 a = Attachment.new self
236 # Converts the object to (unparsed) JSON format for couch
238 # Fills in the fields for saving to the db
240 fields['_rev'] = @_rev unless @_rev.nil?
241 self.class.field_names.each do |field|
242 fields[field] = instance_variable_get '@' + field.to_s
244 unless _attachments.empty?
245 # Let ye of functional natures rejoice!
246 fields['_attachments'] = _attachments.inject({}) do |all, a|
253 # Takes (parsed) JSON from couch and places it into the fields
254 def from_couch!(couch)
256 @_rev = couch['_rev']
257 self.class.field_names.each do |field|
258 instance_variable_set('@' + field.to_s, couch[field.to_s])
260 unless couch['_attachments'].nil?
262 couch['_attachments'].each do |name,value|
263 a = Attachment.new self
264 a.from_couch! name, value
275 attr_accessor :name, :type, :data
276 attr_reader :length, :stub
287 path = @doc.id + '?attachment=' + @name
298 a = { 'type' => type, 'stub' => stub }
299 a['data'] = data unless stub
303 def from_couch!(name, couch)
305 @stub = couch['stub'] or false
306 @type = couch['type']
307 @length = couch['length']