Skip to content

Commit

Permalink
Working on documents no inheriting from Hash
Browse files Browse the repository at this point in the history
  • Loading branch information
samlown committed Jun 7, 2011
1 parent 8e9271d commit 74f24bd
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 144 deletions.
8 changes: 7 additions & 1 deletion history.txt
@@ -1,8 +1,14 @@
== 1.1.0.pre3 - 2011-06-06

* Major changes
* CouchRest::Response removed
* CouchRest::Document now delegates to Hash (instead of inherits)
* Support multiple JSON gems with multi_json

== 1.1.0.pre2 - 2011-04-08

* Major changes
* Time#to_json monkey patch removed! Standard JSON methods now used instead.
* Support multiple JSON gems with multi_json

* Minor alterations
* Named doc replication (thanks @ahamid)
Expand Down
13 changes: 7 additions & 6 deletions lib/couchrest.rb
Expand Up @@ -19,29 +19,30 @@
$:.unshift File.dirname(__FILE__) unless
$:.include?(File.dirname(__FILE__)) ||
$:.include?(File.expand_path(File.dirname(__FILE__)))

require 'couchrest/monkeypatches'
require 'couchrest/rest_api'
require 'couchrest/support/inheritable_attributes'
require 'couchrest/version'

require 'delegate'

# = CouchDB, close to the metal
module CouchRest
autoload :Server, 'couchrest/server'
autoload :Database, 'couchrest/database'
autoload :Response, 'couchrest/response'
autoload :Document, 'couchrest/document'
autoload :Design, 'couchrest/design'
autoload :Model, 'couchrest/model'
autoload :Pager, 'couchrest/helper/pager'
autoload :Streamer, 'couchrest/helper/streamer'
autoload :Attachments, 'couchrest/helper/attachments'
autoload :Upgrade, 'couchrest/helper/upgrade'

# we extend CouchRest with the RestAPI module which gives us acess to
# the get, post, put, delete and copy
CouchRest.extend(::RestAPI)
CouchRest.extend(::CouchRest::RestAPI)

# The CouchRest module methods handle the basic JSON serialization
# and deserialization, as well as query parameters. The module also includes
# some helpers for tasks like instantiating a new Database or Server instance.
Expand All @@ -52,7 +53,7 @@ class << self
def new(*opts)
Server.new(*opts)
end

def parse url
case url
when /^(https?:\/\/)(.*)\/(.*)\/(.*)/
Expand Down
28 changes: 14 additions & 14 deletions lib/couchrest/database.rb
Expand Up @@ -75,7 +75,7 @@ def delete!

# == Retrieving and saving single documents

# GET a document from CouchDB, by id. Returns a Ruby Hash.
# GET a document from CouchDB, by id. Returns a Document or Design.
def get(id, params = {})
slug = escape_docid(id)
url = CouchRest.paramify_url("#{@root}/#{slug}", params)
Expand Down Expand Up @@ -118,13 +118,13 @@ def save_doc(doc, bulk = false, batch = false)
if bulk
@bulk_save_cache << doc
bulk_save if @bulk_save_cache.length >= @bulk_save_cache_limit
return {"ok" => true} # Compatibility with Document#save
return {'ok' => true} # Compatibility with Document#save
elsif !bulk && @bulk_save_cache.length > 0
bulk_save
end
result = if doc['_id']
slug = escape_docid(doc['_id'])
begin
begin
uri = "#{@root}/#{slug}"
uri << "?batch=ok" if batch
CouchRest.put uri, doc
Expand Down Expand Up @@ -186,11 +186,11 @@ def bulk_save(docs = nil, use_uuids = true)
# If <tt>bulk</tt> is true (false by default) the deletion is recorded for bulk-saving (bulk-deletion :) later.
# Bulk saving happens automatically when #bulk_save_cache limit is exceded, or on the next non bulk save.
def delete_doc(doc, bulk = false)
raise ArgumentError, "_id and _rev required for deleting" unless doc['_id'] && doc['_rev']
raise ArgumentError, "_id and _rev required for deleting" unless doc['_id'] && doc['_rev']
if bulk
@bulk_save_cache << { '_id' => doc['_id'], '_rev' => doc['_rev'], '_deleted' => true }
@bulk_save_cache << { '_id' => doc['_id'], '_rev' => doc['_rev'], :_deleted => true }
return bulk_save if @bulk_save_cache.length >= @bulk_save_cache_limit
return { "ok" => true } # Mimic the non-deferred version
return {'ok' => true} # Mimic the non-deferred version
end
slug = escape_docid(doc['_id'])
CouchRest.delete "#{@root}/#{slug}?rev=#{doc['_rev']}"
Expand All @@ -201,7 +201,7 @@ def delete_doc(doc, bulk = false)
# hash with a '_rev' key
def copy_doc(doc, dest)
raise ArgumentError, "_id is required for copying" unless doc['_id']
slug = escape_docid(doc['_id'])
slug = escape_docid(doc['_id'])
destination = if dest.respond_to?(:has_key?) && dest['_id'] && dest['_rev']
"#{dest['_id']}?rev=#{dest['_rev']}"
else
Expand Down Expand Up @@ -243,7 +243,7 @@ def update_doc(doc_id, params = {}, update_limit = 10)
# Query a CouchDB view as defined by a <tt>_design</tt> document. Accepts
# paramaters as described in http://wiki.apache.org/couchdb/HttpViewApi
def view(name, params = {}, payload = {}, &block)
payload[:keys] = params.delete(:keys) if params[:keys]
payload['keys'] = params.delete(:keys) if params[:keys]
# Try recognising the name, otherwise assume already prepared
view_path = name =~ /^([^_].+?)\/(.*)$/ ? "_design/#{$1}/_view/#{$2}" : name
url = CouchRest.paramify_url "#{@root}/#{view_path}", params
Expand Down Expand Up @@ -306,14 +306,14 @@ def get_bulk(ids)
# GET an attachment directly from CouchDB
def fetch_attachment(doc, name)
uri = url_for_attachment(doc, name)
RestClient.get uri, CouchRest.default_headers
CouchRest.get uri, :raw => true
end

# PUT an attachment directly to CouchDB
def put_attachment(doc, name, file, options = {})
docid = escape_docid(doc['_id'])
uri = url_for_attachment(doc, name)
MultiJson.decode(RestClient.put(uri, file, CouchRest.default_headers.merge(options)))
CouchRest.put(uri, file, options.merge(:raw => true))
end

# DELETE an attachment directly from CouchDB
Expand Down Expand Up @@ -343,12 +343,12 @@ def replicate(other_db, continuous, options)
raise ArgumentError, "must provide a target or source option" unless (options.key?(:target) || options.key?(:source))
payload = options
if options.has_key?(:target)
payload[:source] = other_db.root
payload['source'] = other_db.root
else
payload[:target] = other_db.root
payload['target'] = other_db.root
end
payload[:continuous] = continuous
payload[:doc_ids] = options[:doc_ids] if options[:doc_ids]
payload['continuous'] = continuous
payload['doc_ids'] = options[:doc_ids] if options[:doc_ids]
CouchRest.post "#{@host}/_replicate", payload
end

Expand Down
18 changes: 9 additions & 9 deletions lib/couchrest/design.rb
@@ -1,16 +1,16 @@
module CouchRest
module CouchRest
class Design < Document
def view_by *keys
opts = keys.pop if keys.last.is_a?(Hash)
opts ||= {}
self['views'] ||= {}
self[:views] ||= {}
method_name = "by_#{keys.join('_and_')}"

if opts[:map]
view = {}
view['map'] = opts.delete(:map)
view['reduce'] = opts.delete(:reduce) if opts[:reduce]
self['views'][method_name] = view
self[:views][method_name] = view
else
doc_keys = keys.collect{|k| "doc['#{k}']"}
key_emit = doc_keys.length == 1 ? "#{doc_keys.first}" : "[#{doc_keys.join(', ')}]"
Expand All @@ -24,11 +24,11 @@ def view_by *keys
}
}
JAVASCRIPT
self['views'][method_name] = {
self[:views][method_name] = {
'map' => map_function
}
end
self['views'][method_name]['couchrest-defaults'] = opts unless opts.empty?
self[:views][method_name]['couchrest-defaults'] = opts unless opts.empty?
method_name
end

Expand Down Expand Up @@ -56,7 +56,7 @@ def name
end

def name= newname
self['_id'] = "_design/#{newname}"
self[:_id] = "_design/#{newname}"
end

def save
Expand All @@ -67,17 +67,17 @@ def save
# Return the hash of default values to include in all queries sent
# to a view from couchrest.
def view_defaults(name)
(self['views'][name.to_s] && self['views'][name.to_s]["couchrest-defaults"]) || {}
(self[:views][name.to_s] && self[:views][name.to_s]["couchrest-defaults"]) || {}
end

# Returns true or false if the view is available.
def has_view?(name)
!self['views'][name.to_s].nil?
!self[:views][name.to_s].nil?
end

# Check if the view has a reduce method defined.
def can_reduce_view?(name)
has_view?(name) && !self['views'][name.to_s]['reduce'].to_s.empty?
has_view?(name) && !self[:views][name.to_s]['reduce'].to_s.empty?
end

private
Expand Down
83 changes: 62 additions & 21 deletions lib/couchrest/document.rb
@@ -1,38 +1,62 @@
require 'delegate'

module CouchRest
class Document < Response
#
# CouchRest::Document
#
# Provides basic functions for controlling documents returned from
# the CouchDB database and provides methods to act as a wrapper around
# a Hash of @_attributes.
#
# The idea is to provide the basic functionality of a Hash, just
# enought to support the needs of CouchRest, but not inherit all
# of the functionality found in a basic Hash.
#
# A Response is similar to Rails' HashWithIndifferentAccess as all
# requests will convert the keys into Symbols and be stored in the
# master hash as such.
#

module CouchRest
class Document < DelegateClass(Hash)
include CouchRest::Attachments
extend CouchRest::InheritableAttributes

couchrest_inheritable_accessor :database
attr_accessor :database

# override the CouchRest::Model-wide default_database
# This is not a thread safe operation, do not change the model
# database at runtime.
def self.use_database(db)
self.database = db
end


def initialize(attrs = nil)
@_attributes = {}
attrs.each{|k,v| self[k] = v} unless attrs.nil?
super(@_attributes)
end
def []=(key, value)
@_attributes[key.to_s] = value
end
def store(key, value)
@_attributes[key.to_s] = value
end
def [](key)
@_attributes[key.to_s]
end
def has_key?(key)
@_attributes.has_key?(key.to_s)
end

def id
self['_id']
end

def id=(id)
self['_id'] = id
end

def rev
self['_rev']
end

# returns true if the document has never been saved
def new?
!rev
end
alias :new_document? :new?

# Saves the document to the db using create or update. Also runs the :save
# callbacks. Sets the <tt>_id</tt> and <tt>_rev</tt> fields based on
# CouchDB's response.
Expand All @@ -57,7 +81,7 @@ def destroy(bulk = false)
end
result['ok']
end

# copies the document to a new id. If the destination id currently exists, a rev must be provided.
# <tt>dest</tt> can take one of two forms if overwriting: "id_to_overwrite?rev=revision" or the actual doc
# hash with a '_rev' key
Expand All @@ -66,7 +90,7 @@ def copy(dest)
result = database.copy_doc(self, dest)
result['ok']
end

# Returns the CouchDB uri for the document
def uri(append_rev = false)
return nil if new?
Expand All @@ -78,12 +102,29 @@ def uri(append_rev = false)
end
couch_uri
end

# Returns the document's database
def database
@database || self.class.database
end


# Provide details of the current keys in the reponse. Based on ActiveRecord::Base.
def inspect
attributes_as_nice_string = self.keys.collect { |key|
"#{key}: #{self[key].inspect}"
}.compact.join(", ")
"#<#{self.class} #{attributes_as_nice_string}>"
end

class << self
# override the CouchRest::Model-wide default_database
# This is not a thread safe operation, do not change the model
# database at runtime.
def use_database(db)
self.database = db
end
end

end

end
34 changes: 0 additions & 34 deletions lib/couchrest/response.rb

This file was deleted.

0 comments on commit 74f24bd

Please sign in to comment.