aqua 0.1.6
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/.document +5 -0
- data/.gitignore +7 -0
- data/Aqua.gemspec +121 -0
- data/LICENCE_COUCHREST +176 -0
- data/LICENSE +20 -0
- data/README.rdoc +105 -0
- data/Rakefile +83 -0
- data/VERSION +1 -0
- data/lib/aqua.rb +101 -0
- data/lib/aqua/object/config.rb +43 -0
- data/lib/aqua/object/extensions/ar_convert.rb +0 -0
- data/lib/aqua/object/extensions/ar_style.rb +0 -0
- data/lib/aqua/object/extensions/property.rb +0 -0
- data/lib/aqua/object/extensions/validation.rb +0 -0
- data/lib/aqua/object/pack.rb +306 -0
- data/lib/aqua/object/query.rb +18 -0
- data/lib/aqua/object/stub.rb +122 -0
- data/lib/aqua/object/tank.rb +54 -0
- data/lib/aqua/object/unpack.rb +253 -0
- data/lib/aqua/store/couch_db/attachments.rb +183 -0
- data/lib/aqua/store/couch_db/couch_db.rb +151 -0
- data/lib/aqua/store/couch_db/database.rb +186 -0
- data/lib/aqua/store/couch_db/design_document.rb +57 -0
- data/lib/aqua/store/couch_db/http_client/adapter/rest_client.rb +53 -0
- data/lib/aqua/store/couch_db/http_client/rest_api.rb +62 -0
- data/lib/aqua/store/couch_db/server.rb +103 -0
- data/lib/aqua/store/couch_db/storage_methods.rb +405 -0
- data/lib/aqua/store/storage.rb +59 -0
- data/lib/aqua/support/initializers.rb +216 -0
- data/lib/aqua/support/mash.rb +144 -0
- data/lib/aqua/support/set.rb +23 -0
- data/lib/aqua/support/string_extensions.rb +121 -0
- data/spec/aqua_spec.rb +19 -0
- data/spec/object/config_spec.rb +58 -0
- data/spec/object/object_fixtures/array_udder.rb +5 -0
- data/spec/object/object_fixtures/canned_hash.rb +5 -0
- data/spec/object/object_fixtures/gerbilmiester.rb +18 -0
- data/spec/object/object_fixtures/grounded.rb +13 -0
- data/spec/object/object_fixtures/log.rb +19 -0
- data/spec/object/object_fixtures/persistent.rb +12 -0
- data/spec/object/object_fixtures/sugar.rb +4 -0
- data/spec/object/object_fixtures/user.rb +38 -0
- data/spec/object/pack_spec.rb +607 -0
- data/spec/object/query_spec.rb +27 -0
- data/spec/object/stub_spec.rb +51 -0
- data/spec/object/tank_spec.rb +61 -0
- data/spec/object/unpack_spec.rb +361 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/store/couchdb/attachments_spec.rb +164 -0
- data/spec/store/couchdb/couch_db_spec.rb +104 -0
- data/spec/store/couchdb/database_spec.rb +161 -0
- data/spec/store/couchdb/design_document_spec.rb +43 -0
- data/spec/store/couchdb/fixtures_and_data/document_fixture.rb +3 -0
- data/spec/store/couchdb/fixtures_and_data/image_attach.png +0 -0
- data/spec/store/couchdb/server_spec.rb +96 -0
- data/spec/store/couchdb/storage_methods_spec.rb +408 -0
- data/utils/code_statistics.rb +134 -0
- data/utils/console +11 -0
- metadata +136 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
# Design documents are responsible for saving views. It is also the place that Aqua will
|
2
|
+
# be saving the Class code. There will be one design document per class. There may be additional
|
3
|
+
# design documents created without being tied to a class. Don't know yet.
|
4
|
+
module Aqua
|
5
|
+
module Store
|
6
|
+
module CouchDB
|
7
|
+
class DesignDocument < Mash
|
8
|
+
|
9
|
+
# the DesignDocument is essentially a special type of Document.
|
10
|
+
include Aqua::Store::CouchDB::StorageMethods
|
11
|
+
|
12
|
+
# In the design document the name is the same as the id. That way initialization can
|
13
|
+
# include a name parameter, which will change the id, and therefore the address of the
|
14
|
+
# document. This method returns the id.
|
15
|
+
# @return [String] id for document
|
16
|
+
# @api public
|
17
|
+
def name
|
18
|
+
id
|
19
|
+
end
|
20
|
+
|
21
|
+
# Sets the id and is an alias for id=.
|
22
|
+
# @param [String] Unique identifier
|
23
|
+
# @return [String] Escaped identifier
|
24
|
+
# @api public
|
25
|
+
def name=( n )
|
26
|
+
self.id = ( n )
|
27
|
+
end
|
28
|
+
|
29
|
+
alias :document_initialize :initialize
|
30
|
+
|
31
|
+
def initialize( hash={} )
|
32
|
+
hash = Mash.new( hash ) unless hash.empty?
|
33
|
+
self.id = hash.delete(:name) if hash[:name]
|
34
|
+
document_initialize( hash )
|
35
|
+
end
|
36
|
+
|
37
|
+
# couchdb database url for the design document
|
38
|
+
# @return [String] representing CouchDB uri for document
|
39
|
+
# @api public
|
40
|
+
def uri
|
41
|
+
raise ArgumentError, 'DesignDocument must have a name' if name.nil? || name.empty?
|
42
|
+
database.uri + '/_design/' + name
|
43
|
+
end
|
44
|
+
|
45
|
+
# Updates the id and rev after a design document is successfully saved. The _design/
|
46
|
+
# portion of the id has to be stripped.
|
47
|
+
# @param [Hash] Result returned by CouchDB document save
|
48
|
+
# @api private
|
49
|
+
def update_version( result )
|
50
|
+
self.id = result['id'].gsub(/\A_design\//, '')
|
51
|
+
self.rev = result['rev']
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
|
3
|
+
module RestClientAdapter
|
4
|
+
def self.convert_exception(&blk)
|
5
|
+
begin
|
6
|
+
yield
|
7
|
+
rescue Exception => e
|
8
|
+
ending = e.class.to_s.match(/[a-z0-9_]*\z/i)
|
9
|
+
if e.message.match(/409\z/)
|
10
|
+
raise Aqua::Store::CouchDB::Conflict, e.message
|
11
|
+
else
|
12
|
+
begin
|
13
|
+
error = "Aqua::Store::CouchDB::#{ending}".constantize
|
14
|
+
rescue
|
15
|
+
raise e
|
16
|
+
end
|
17
|
+
raise error, e.message
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.get(uri, headers={})
|
23
|
+
convert_exception do
|
24
|
+
RestClient.get(uri, headers)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.post(uri, hash, headers={})
|
29
|
+
convert_exception do
|
30
|
+
RestClient.post(uri, hash, headers)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.put(uri, hash, headers={})
|
35
|
+
convert_exception do
|
36
|
+
RestClient.put(uri, hash, headers)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.delete(uri, headers={})
|
41
|
+
convert_exception do
|
42
|
+
RestClient.delete(uri, headers)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.copy(uri, headers)
|
47
|
+
convert_exception do
|
48
|
+
RestClient::Request.execute( :method => :copy,
|
49
|
+
:url => uri,
|
50
|
+
:headers => headers)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# HTTP Adapters should implement the following to be used with the RestAPI module
|
2
|
+
#
|
3
|
+
# def self.get(uri, headers=nil)
|
4
|
+
# end
|
5
|
+
#
|
6
|
+
# def self.post(uri, hash, headers=nil)
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# def self.put(uri, hash, headers=nil)
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# def self.delete(uri, headers=nil)
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# def self.copy(uri, headers)
|
16
|
+
# end
|
17
|
+
|
18
|
+
module RestAPI
|
19
|
+
def self.adapter=( klass )
|
20
|
+
@adapter = klass
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.adapter
|
24
|
+
@adapter
|
25
|
+
end
|
26
|
+
|
27
|
+
def put(uri, doc = nil)
|
28
|
+
hash = doc.to_json if doc
|
29
|
+
response = RestAPI.adapter.put( uri, hash )
|
30
|
+
JSON.parse( response )
|
31
|
+
end
|
32
|
+
|
33
|
+
def get(uri, streamable=false)
|
34
|
+
response = RestAPI.adapter.get(uri)
|
35
|
+
begin
|
36
|
+
JSON.parse( response , :max_nesting => false)
|
37
|
+
rescue Exception => e
|
38
|
+
if streamable
|
39
|
+
response
|
40
|
+
else
|
41
|
+
raise e
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def post(uri, doc = nil)
|
47
|
+
hash = doc.to_json if doc
|
48
|
+
response = RestAPI.adapter.post(uri, hash)
|
49
|
+
JSON.parse( response )
|
50
|
+
end
|
51
|
+
|
52
|
+
def delete(uri)
|
53
|
+
response = RestAPI.adapter.delete(uri)
|
54
|
+
JSON.parse( response )
|
55
|
+
end
|
56
|
+
|
57
|
+
def copy(uri, destination)
|
58
|
+
response = RestAPI.adapter.copy(uri, {'Destination' => destination})
|
59
|
+
JSON.parse( response )
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# This has been ripped in part from CouchRest: http://github.com/mattetti/couchrest/tree/master
|
2
|
+
# License information is in LICENSE_COUCHREST, modifications are covered under the aqua license
|
3
|
+
|
4
|
+
module Aqua
|
5
|
+
module Store
|
6
|
+
module CouchDB
|
7
|
+
class Server
|
8
|
+
attr_accessor :uri, :uuid_batch_count, :uuids
|
9
|
+
attr_reader :namespace
|
10
|
+
|
11
|
+
def initialize(opts={})
|
12
|
+
opts = Mash.new(opts) unless opts.empty?
|
13
|
+
self.uri = opts[:server] || 'http://127.0.0.1:5984'
|
14
|
+
self.uuid_batch_count = opts[:uuid_batch_count] || 1000
|
15
|
+
self.namespace = opts[:namespace].to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def namespace=( name )
|
19
|
+
default = 'aqua'
|
20
|
+
name ||= default
|
21
|
+
name = CouchDB.escape( name )
|
22
|
+
name = default if name.empty?
|
23
|
+
@namespace = name
|
24
|
+
end
|
25
|
+
|
26
|
+
# DATABASE MANAGMENT -----------------
|
27
|
+
|
28
|
+
# Lists all database names on the server
|
29
|
+
def database_names
|
30
|
+
dbs = CouchDB.get( "#{@uri}/_all_dbs" )
|
31
|
+
dbs.select{|name| name.match(/\A#{namespace}_?/)}
|
32
|
+
end
|
33
|
+
|
34
|
+
def databases
|
35
|
+
dbs = []
|
36
|
+
database_names.each do |db_name|
|
37
|
+
dbs << Database.new( db_name.gsub(/\A#{namespace}_|\A#{namespace}\z/, '') , :server => self )
|
38
|
+
end
|
39
|
+
dbs
|
40
|
+
end
|
41
|
+
|
42
|
+
# Deletes all databases named for this namespace (i.e. this server)
|
43
|
+
# Use with caution ... it is a permanent and undoable change
|
44
|
+
def delete_all!
|
45
|
+
databases.each{ |db| db.delete! }
|
46
|
+
end
|
47
|
+
|
48
|
+
# Deletes all database with the less exection raising method: database.delete. This will
|
49
|
+
# only raise errors related to request problems, and not errors related to the database not
|
50
|
+
# being found for deletion.
|
51
|
+
def delete_all
|
52
|
+
databases.each{ |db| db.delete }
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns a CouchRest::Database for the given name
|
56
|
+
def database(name)
|
57
|
+
db = Database.new( name, :server => self )
|
58
|
+
db.exists? ? db : nil
|
59
|
+
end
|
60
|
+
|
61
|
+
# Creates the database if it doesn't exist
|
62
|
+
def database!(name)
|
63
|
+
Database.create( name, :server => self )
|
64
|
+
end
|
65
|
+
|
66
|
+
# GET the welcome message
|
67
|
+
def info
|
68
|
+
CouchDB.get "#{uri}/"
|
69
|
+
end
|
70
|
+
|
71
|
+
# Restart the CouchDB instance
|
72
|
+
def restart!
|
73
|
+
CouchDB.post "#{uri}/_restart"
|
74
|
+
end
|
75
|
+
|
76
|
+
# counts the number of uuids available, used by Database to limit bulk save
|
77
|
+
def uuid_count
|
78
|
+
if uuids
|
79
|
+
uuids.size
|
80
|
+
else
|
81
|
+
load_uuids
|
82
|
+
uuid_batch_count
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Retrive an unused UUID from CouchDB. Server instances manage caching a list of unused UUIDs.
|
87
|
+
def next_uuid(count = @uuid_batch_count)
|
88
|
+
@uuids ||= []
|
89
|
+
if uuids.empty?
|
90
|
+
load_uuids(count)
|
91
|
+
end
|
92
|
+
uuids.pop
|
93
|
+
end
|
94
|
+
|
95
|
+
def load_uuids( count=@uuid_batch_count )
|
96
|
+
@uuids = CouchDB.get("#{@uri}/_uuids?count=#{count}")["uuids"]
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
end # Server
|
101
|
+
end # CouchDB
|
102
|
+
end # Store
|
103
|
+
end # Aqua
|
@@ -0,0 +1,405 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module Aqua
|
5
|
+
module Store
|
6
|
+
module CouchDB
|
7
|
+
# This module of storage methods was built to be flexible enough to step in as a replacement
|
8
|
+
# for CouchRest core or another super lite CouchDB library. A lot of the methods are added for that
|
9
|
+
# convenience, and not for the needs of Aqua. Adding the module to a Mash/Hash class is sufficient
|
10
|
+
# to get the full core access library.
|
11
|
+
#
|
12
|
+
# @see Aqua::Storage for details on the require methods for a storage library.
|
13
|
+
module StorageMethods
|
14
|
+
|
15
|
+
def self.included( klass )
|
16
|
+
klass.class_eval do
|
17
|
+
include InstanceMethods
|
18
|
+
extend ClassMethods
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
# Initializes a new storage document and saves it without raising any errors
|
24
|
+
#
|
25
|
+
# @param [Hash, Mash]
|
26
|
+
# @return [Aqua::Storage, false] On success it returns an aqua storage object. On failure it returns false.
|
27
|
+
#
|
28
|
+
# @api public
|
29
|
+
def create( hash )
|
30
|
+
doc = new( hash )
|
31
|
+
doc.save
|
32
|
+
end
|
33
|
+
|
34
|
+
# Initializes a new storage document and saves it raising any errors.
|
35
|
+
#
|
36
|
+
# @param [Hash, Mash]
|
37
|
+
# @return [Aqua::Storage] On success it returns an aqua storage object.
|
38
|
+
# @raise Any of the CouchDB exceptions
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
def create!( hash )
|
42
|
+
doc = new( hash )
|
43
|
+
doc.save!
|
44
|
+
end
|
45
|
+
|
46
|
+
# Sets default database for class. This can be overwritten by individual documents
|
47
|
+
# @todo Look to CouchDB database strategy to determine if there is a database per class
|
48
|
+
# or just one big database for all classes
|
49
|
+
#
|
50
|
+
# @return [Aqua::Database]
|
51
|
+
#
|
52
|
+
# @api public
|
53
|
+
def database
|
54
|
+
@database ||= Database.create # defaults to 'aqua'
|
55
|
+
end
|
56
|
+
|
57
|
+
# Setter for the database per class. Used to override default per class or default strategy.
|
58
|
+
#
|
59
|
+
# @return [Aqua::Database]
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
def database=( db )
|
63
|
+
db = Database.create( db ) if db.class == String
|
64
|
+
@database = db
|
65
|
+
end
|
66
|
+
|
67
|
+
# gets a document from the database based on id
|
68
|
+
# @param [String] id
|
69
|
+
# @return [Hash] representing the CouchDB data
|
70
|
+
# @api public
|
71
|
+
def get( id )
|
72
|
+
new( CouchDB.get( "#{database.uri}/#{CGI.escape(id)}" ) )
|
73
|
+
end
|
74
|
+
|
75
|
+
# Retrieves an attachment when provided the document id and attachment id, or the combined id
|
76
|
+
#
|
77
|
+
# @return [Tempfile]
|
78
|
+
#
|
79
|
+
# @api public
|
80
|
+
def attachment( document_id, attachment_id )
|
81
|
+
new( :id => document_id ).attachments.get!( attachment_id )
|
82
|
+
end
|
83
|
+
|
84
|
+
# Creates basic map reduce view for a given field
|
85
|
+
def index_on( field, opts={} )
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
module InstanceMethods
|
91
|
+
# Initializes a new storage document.
|
92
|
+
#
|
93
|
+
# @param [Hash, Mash]
|
94
|
+
# @return [Aqua::Storage] a Hash/Mash with some extras
|
95
|
+
#
|
96
|
+
# @api public
|
97
|
+
def initialize( hash={} )
|
98
|
+
hash = Mash.new( hash ) unless hash.empty?
|
99
|
+
self.id = hash.delete(:id) if hash[:id]
|
100
|
+
|
101
|
+
# ignore these keys
|
102
|
+
hash.delete(:rev) # This is omited to aleviate confusion
|
103
|
+
hash.delete(:_rev) # CouchDB determines _rev attribute
|
104
|
+
hash.delete(:_id) # this is set via by the id=(value) method
|
105
|
+
# TODO: have to deal with attachments as well
|
106
|
+
|
107
|
+
# feed the rest of the hash to the super
|
108
|
+
super( hash )
|
109
|
+
end
|
110
|
+
|
111
|
+
# Saves an Aqua::Storage instance to CouchDB as a document. Save can be deferred for bulk saving.
|
112
|
+
#
|
113
|
+
# @param [optional true, false] Determines whether the document is cached for bulk saving later. true will cause it to be defered. Default is false.
|
114
|
+
# @return [Aqua::Storage, false] Will return false if the document is not saved. Otherwise it will return the Aqua::Storage object.
|
115
|
+
#
|
116
|
+
# @api public
|
117
|
+
def save( defer=false )
|
118
|
+
save_logic( defer )
|
119
|
+
end
|
120
|
+
|
121
|
+
# Saves an Aqua::Storage instance to CouchDB as a document. Save can be deferred for bulk saving from the database.
|
122
|
+
# Unlike #save, this method will raise an error if the document is not saved.
|
123
|
+
#
|
124
|
+
# @param [optional true, false] Determines whether the document is cached for bulk saving later. true will cause it to be defered. Default is false.
|
125
|
+
# @return [Aqua::Storage] On success.
|
126
|
+
#
|
127
|
+
# @api public
|
128
|
+
def commit( defer=false )
|
129
|
+
save_logic( defer, false )
|
130
|
+
end
|
131
|
+
alias :save! :commit
|
132
|
+
|
133
|
+
# Internal logic used by save, save! and commit to save an object.
|
134
|
+
#
|
135
|
+
# @param [optional true, false] Determines whether a object cached for save in the database in bulk. By default this is false.
|
136
|
+
# @param [optional true, false] Determines whether an exception is raised or whether false is returned.
|
137
|
+
# @return [Aqua::Storage, false] Depening on the type of execption masking and also the outcome
|
138
|
+
# @raise Any of the CouchDB execptions.
|
139
|
+
#
|
140
|
+
# @api private
|
141
|
+
def save_logic( defer=false, mask_exception = true )
|
142
|
+
ensure_id
|
143
|
+
self[:_attachments] = attachments.pack unless attachments.empty?
|
144
|
+
if defer
|
145
|
+
database.add_to_bulk_cache( self )
|
146
|
+
else
|
147
|
+
# clear any bulk saving left over ...
|
148
|
+
database.bulk_save if database.bulk_cache.size > 0
|
149
|
+
if mask_exception
|
150
|
+
save_now
|
151
|
+
else
|
152
|
+
save_now( false )
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Internal logic used by save_logic to save an object immediately instead of deferring for bulk save.
|
158
|
+
#
|
159
|
+
# @param [optional true, false] Determines whether an exception is raised or whether false is returned.
|
160
|
+
# @return [Aqua::Storage, false] Depening on the type of execption masking and also the outcome
|
161
|
+
# @raise Any of the CouchDB execptions.
|
162
|
+
#
|
163
|
+
# @api private
|
164
|
+
def save_now( mask_exception = true )
|
165
|
+
begin
|
166
|
+
result = CouchDB.put( uri, self )
|
167
|
+
rescue Exception => e
|
168
|
+
if mask_exception
|
169
|
+
result = false
|
170
|
+
else
|
171
|
+
raise e
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
if result && result['ok']
|
176
|
+
update_version( result )
|
177
|
+
self
|
178
|
+
else
|
179
|
+
result
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# couchdb database url for this document
|
184
|
+
# @return [String] representing CouchDB uri for document
|
185
|
+
# @api public
|
186
|
+
def uri
|
187
|
+
database.uri + '/' + ensure_id
|
188
|
+
end
|
189
|
+
|
190
|
+
# retrieves self from CouchDB database
|
191
|
+
# @return [Hash] representing the CouchDB data
|
192
|
+
# @api public
|
193
|
+
def retrieve
|
194
|
+
self.class.new( CouchDB.get( uri ) )
|
195
|
+
end
|
196
|
+
|
197
|
+
# reloads self from CouchDB database
|
198
|
+
# @return [Hash] representing CouchDB data
|
199
|
+
# @api public
|
200
|
+
def reload
|
201
|
+
self.replace( CouchDB.get( uri ) )
|
202
|
+
end
|
203
|
+
|
204
|
+
# Deletes an document from CouchDB. Delete can be deferred for bulk saving/deletion.
|
205
|
+
#
|
206
|
+
# @param [optional true, false] Determines whether the document is cached for bulk saving later. true will cause it to be defered. Default is false.
|
207
|
+
# @return [String, false] Will return a json string with the response if successful. Otherwise returns false.
|
208
|
+
#
|
209
|
+
# @api public
|
210
|
+
def delete(defer = false)
|
211
|
+
delete_logic( defer )
|
212
|
+
end
|
213
|
+
|
214
|
+
# Deletes an document from CouchDB. Delete can be deferred for bulk saving/deletion. This version raises an exception if an error other that resource not found is raised.
|
215
|
+
#
|
216
|
+
# @param [optional true, false] Determines whether the document is cached for bulk saving later. true will cause it to be defered. Default is false.
|
217
|
+
# @return [String, false] Will return a json string with the response if successful. It will return false if the resource was not found. Other exceptions will be raised.
|
218
|
+
# @raise Any of the CouchDB exceptions
|
219
|
+
#
|
220
|
+
# @api public
|
221
|
+
def delete!(defer = false)
|
222
|
+
delete_logic( defer, false )
|
223
|
+
end
|
224
|
+
|
225
|
+
# Internal logic used by delete and delete! to delete a resource.
|
226
|
+
#
|
227
|
+
# @param [optional true, false] Determines whether resource is deleted immediately or saved for bulk processing.
|
228
|
+
# @param [optional true, false] Determines whether an exception is raised or whether false is returned.
|
229
|
+
# @return [String, false] Depening on the type of execption masking and also the outcome
|
230
|
+
# @raise Any of the CouchDB execptions.
|
231
|
+
#
|
232
|
+
# @api private
|
233
|
+
def delete_logic( defer = false, mask_exceptions = true )
|
234
|
+
if defer
|
235
|
+
database.add_to_bulk_cache( { '_id' => self['_id'], '_rev' => rev, '_deleted' => true } )
|
236
|
+
else
|
237
|
+
begin
|
238
|
+
delete_now
|
239
|
+
rescue Exception => e
|
240
|
+
if mask_exceptions || e.class == CouchDB::ResourceNotFound
|
241
|
+
false
|
242
|
+
else
|
243
|
+
raise e
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# Internal logic used by delete_logic delete a resource immediately.
|
250
|
+
#
|
251
|
+
# @return [String, false] Depening on the type of execption masking and also the outcome
|
252
|
+
# @raise Any of the CouchDB execptions.
|
253
|
+
#
|
254
|
+
# @api private
|
255
|
+
def delete_now
|
256
|
+
revisions.each do |rev_id|
|
257
|
+
CouchDB.delete( "#{uri}?rev=#{rev_id}" )
|
258
|
+
end
|
259
|
+
true
|
260
|
+
end
|
261
|
+
|
262
|
+
# Gets revision history, which is needed by Delete to remove all versions of a document
|
263
|
+
#
|
264
|
+
# @return [Array] Containing strings with revision numbers
|
265
|
+
#
|
266
|
+
# @api semi-private
|
267
|
+
def revisions
|
268
|
+
active_revisions = []
|
269
|
+
begin
|
270
|
+
hash = CouchDB.get( "#{uri}?revs_info=true" )
|
271
|
+
rescue
|
272
|
+
return active_revisions
|
273
|
+
end
|
274
|
+
hash['_revs_info'].each do |rev_hash|
|
275
|
+
active_revisions << rev_hash['rev'] if ['disk', 'available'].include?( rev_hash['status'] )
|
276
|
+
end
|
277
|
+
active_revisions
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
# sets the database
|
282
|
+
# @param [Aqua::Store::CouchDB::Database]
|
283
|
+
# @return [Aqua::Store::CouchDB::Database]
|
284
|
+
# @api private
|
285
|
+
attr_writer :database
|
286
|
+
|
287
|
+
# retrieves the previously set database or sets the new one with a default value
|
288
|
+
# @return [Aqua::Store::CouchDB::Database]
|
289
|
+
# @api private
|
290
|
+
def database
|
291
|
+
@database ||= determine_database
|
292
|
+
end
|
293
|
+
|
294
|
+
# Looks to class for database information about how the CouchDB store has generally
|
295
|
+
# been configured to store its data across databases and/or servers. In some cases the class for
|
296
|
+
# the parent object has configuration details about the database and server to use.
|
297
|
+
# @todo Build the strategies in CouchDB. Use them here
|
298
|
+
# @api private
|
299
|
+
def determine_database
|
300
|
+
self.class.database
|
301
|
+
end
|
302
|
+
|
303
|
+
# setters and getters couchdb document specifics -------------------------
|
304
|
+
|
305
|
+
# Gets the document id. In this engine id and _id are different data. The main reason for this is that
|
306
|
+
# CouchDB needs a relatively clean string as the key, where as the user can assign a messy string to
|
307
|
+
# the id. The user can continue to use the messy string since the engine also has access to the _id.
|
308
|
+
#
|
309
|
+
# @return [String]
|
310
|
+
#
|
311
|
+
# @api public
|
312
|
+
def id
|
313
|
+
self[:id]
|
314
|
+
end
|
315
|
+
|
316
|
+
# Allows the id to be set. If the id is changed after creation, then the CouchDB document for the old
|
317
|
+
# id is deleted, and the _rev is set to nil, making it a new document. The id can only be a string (right now).
|
318
|
+
#
|
319
|
+
# @return [String, false] Will return the string it received if it is indeed a string. Otherwise it will
|
320
|
+
# return false.
|
321
|
+
#
|
322
|
+
# @api public
|
323
|
+
def id=( str )
|
324
|
+
if str.respond_to?(:match)
|
325
|
+
escaped = CGI.escape( str )
|
326
|
+
|
327
|
+
# CLEANUP: do a bulk delete request on the old id, now that it has changed
|
328
|
+
delete(true) if !new? && escaped != self[:_id]
|
329
|
+
|
330
|
+
self[:id] = str
|
331
|
+
self[:_id] = escaped
|
332
|
+
str
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
# Returns CouchDB document revision identifier.
|
337
|
+
#
|
338
|
+
# @return [String]
|
339
|
+
#
|
340
|
+
# @api semi-public
|
341
|
+
def rev
|
342
|
+
self[:_rev]
|
343
|
+
end
|
344
|
+
|
345
|
+
protected
|
346
|
+
def rev=( str )
|
347
|
+
self[:_rev] = str
|
348
|
+
end
|
349
|
+
public
|
350
|
+
|
351
|
+
# Updates the id and rev after a document is successfully saved.
|
352
|
+
# @param [Hash] Result returned by CouchDB document save
|
353
|
+
# @api private
|
354
|
+
def update_version( result )
|
355
|
+
self.id = result['id']
|
356
|
+
self.rev = result['rev']
|
357
|
+
end
|
358
|
+
|
359
|
+
# Returns true if the document has never been saved or false if it has been saved.
|
360
|
+
# @return [true, false]
|
361
|
+
# @api public
|
362
|
+
def new?
|
363
|
+
!rev
|
364
|
+
end
|
365
|
+
alias :new_document? :new?
|
366
|
+
|
367
|
+
# Returns true if a document exists at the CouchDB uri for this document. Otherwise returns false
|
368
|
+
# @return [true, false]
|
369
|
+
# @api public
|
370
|
+
def exists?
|
371
|
+
begin
|
372
|
+
CouchDB.get uri
|
373
|
+
true
|
374
|
+
rescue
|
375
|
+
false
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
# gets a uuid from the server if one doesn't exist, otherwise escapes existing id.
|
380
|
+
# @api private
|
381
|
+
def ensure_id
|
382
|
+
self[:_id] = ( id ? escape_doc_id : database.server.next_uuid )
|
383
|
+
end
|
384
|
+
|
385
|
+
# Escapes document id. Different strategies for design documents and normal documents.
|
386
|
+
# @api private
|
387
|
+
def escape_doc_id
|
388
|
+
CGI.escape( id )
|
389
|
+
end
|
390
|
+
|
391
|
+
# Hash of attachments, keyed by name
|
392
|
+
# @params [Document] Document object that is self
|
393
|
+
# @return [Hash] Attachments keyed by name
|
394
|
+
#
|
395
|
+
# @api public
|
396
|
+
def attachments
|
397
|
+
@attachments ||= Attachments.new( self )
|
398
|
+
end
|
399
|
+
|
400
|
+
end # InstanceMethods
|
401
|
+
|
402
|
+
end # StoreMethods
|
403
|
+
end # CouchDB
|
404
|
+
end # Store
|
405
|
+
end # Aqua
|