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,183 @@
|
|
1
|
+
module Aqua
|
2
|
+
module Store
|
3
|
+
module CouchDB
|
4
|
+
# Attachments is a Hash-like container with keys that are attacment names and values that are file-type
|
5
|
+
# objects. Initializing and adding to the collection assures the types of both keys and values. The
|
6
|
+
# collection implements a lazy-loading scheme, such that when an attachment is requested and not found,
|
7
|
+
# it will try to load it from CouchDB.
|
8
|
+
class Attachments < Mash
|
9
|
+
attr_reader :document
|
10
|
+
attr_reader :stubs
|
11
|
+
|
12
|
+
# Creates a new attachment collection with keys that are attachment names and values that are
|
13
|
+
# file-type objects. The collection manages both the key and the value types.
|
14
|
+
#
|
15
|
+
# @param [String] Document uri; used to save and retrieve attachments directly
|
16
|
+
# @param [Hash] Initialization values
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
def initialize( doc, hash={} )
|
20
|
+
raise ArgumentError, "must be initialized with a document" unless doc.respond_to?( :retrieve )
|
21
|
+
@document = doc
|
22
|
+
self.class.validate_hash( hash ) unless hash.empty?
|
23
|
+
super( hash )
|
24
|
+
end
|
25
|
+
|
26
|
+
# Adds an attachment to the collection, checking for type. Does not add directly to the database.
|
27
|
+
#
|
28
|
+
# @param [String, Symbol] Name of the attachment as a string or symbol
|
29
|
+
# @param [File] The attachment
|
30
|
+
#
|
31
|
+
# @api public
|
32
|
+
def add( name, file )
|
33
|
+
self.class.validate_hash( name => file )
|
34
|
+
self[name] = file
|
35
|
+
end
|
36
|
+
|
37
|
+
# Adds an attachment to the collection and to the database. Document doesn't have to be saved,
|
38
|
+
# but it does need to have an id.
|
39
|
+
#
|
40
|
+
# @param [String, Symbol] Name of the attachment as a string or symbol
|
41
|
+
# @param [File] The attachment
|
42
|
+
#
|
43
|
+
# @api public
|
44
|
+
def add!( name, file )
|
45
|
+
add( name, file )
|
46
|
+
content_type = MIME::Types.type_for(file.path).first
|
47
|
+
content_type = content_type.nil? ? "text\/plain" : content_type.simplified
|
48
|
+
data = {
|
49
|
+
'content_type' => content_type,
|
50
|
+
'data' => Base64.encode64( file.read ).gsub(/\s/,'')
|
51
|
+
}
|
52
|
+
file.rewind
|
53
|
+
response = CouchDB.put( uri_for( name ), data )
|
54
|
+
update_doc_rev( response )
|
55
|
+
file
|
56
|
+
end
|
57
|
+
|
58
|
+
# Deletes an attachment from the collection, and from the database. Use #delete (from Hash) to just
|
59
|
+
# delete the attachment from the collection.
|
60
|
+
#
|
61
|
+
# @param [String, Symbol] Name of the attachment as a string or symbol
|
62
|
+
# @return [File, nil] File at that location or nil if no file found
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
def delete!( name )
|
66
|
+
if self[name]
|
67
|
+
file = delete( name )
|
68
|
+
unless document.new?
|
69
|
+
CouchDB.delete( uri_for( name ) )
|
70
|
+
end
|
71
|
+
file
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Gets an attachment from the collection first. If not found, it will be requested from the database.
|
76
|
+
#
|
77
|
+
# @param [String, Symbol] Name of the attachment
|
78
|
+
# @return [File, nil] File for that name, or nil if not found in hash or in database
|
79
|
+
#
|
80
|
+
# @api public
|
81
|
+
def get( name, stream=false )
|
82
|
+
file = self[name]
|
83
|
+
unless file
|
84
|
+
file = get!( name, stream )
|
85
|
+
end
|
86
|
+
file.rewind if file # just in case of previous streaming
|
87
|
+
file
|
88
|
+
end
|
89
|
+
|
90
|
+
# Gets an attachment from the database. Stores it in the hash.
|
91
|
+
#
|
92
|
+
# @param [String, Symbol] Name of the attachment
|
93
|
+
# @param [true, false] Stream boolean flag indicating whether the data should be converted to
|
94
|
+
# a file or kept as a stream
|
95
|
+
# @return [File, nil] File for that name, or nil if not found in the database
|
96
|
+
# @raise Any error encountered on retrieval of the attachment, json, http_client, Aqua etc
|
97
|
+
#
|
98
|
+
# @todo make this more memory favorable, maybe streaming/saving in a max number of bytes
|
99
|
+
# @api public
|
100
|
+
def get!( name, stream=false )
|
101
|
+
file = nil
|
102
|
+
response = CouchDB.get( uri_for( name, false ), true ) rescue nil
|
103
|
+
data = response && response.respond_to?(:keys) ? Base64.decode64( response['data'] ) : nil
|
104
|
+
if data || response
|
105
|
+
file = Tempfile.new( CGI.escape( name.to_s ) )
|
106
|
+
file.binmode if file.respond_to?( :binmode )
|
107
|
+
data ? file.write( data ) : file.write( response )
|
108
|
+
file.rewind
|
109
|
+
self[name] = file
|
110
|
+
end
|
111
|
+
stream ? file.read : file
|
112
|
+
end
|
113
|
+
|
114
|
+
# Constructs the standalone attachment uri for PUT and DELETE actions.
|
115
|
+
#
|
116
|
+
# @param [String] Name of the attachment as a string or symbol
|
117
|
+
#
|
118
|
+
# @api private
|
119
|
+
def uri_for( name, include_rev = true )
|
120
|
+
raise ArgumentError, 'Document must have id in order to save an attachment' if document.id.nil? || document.id.empty?
|
121
|
+
document.uri + "/#{CGI.escape( name.to_s )}" + ( document.rev && include_rev ? "?rev=#{document.rev}" : "" )
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
# Validates and throws an error on a hash, insisting that the key is a string or symbol,
|
126
|
+
# and the value is a file.
|
127
|
+
#
|
128
|
+
# @param [Hash]
|
129
|
+
#
|
130
|
+
# @api private
|
131
|
+
def self.validate_hash( hash )
|
132
|
+
hash.each do |name, file|
|
133
|
+
raise ArgumentError, "Attachment name, #{name.inspect}, must be a Symbol or a String" unless [Symbol, String ].include?( name.class )
|
134
|
+
raise ArgumentError, "Attachment file, #{file.inspect}, must be a File-like object" unless file.respond_to?( :read )
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Goes into the document and updates it's rev to match the returned rev. That way #new? will return false
|
139
|
+
# when an attachment is created before the document is saved. It also means that future attempts to save
|
140
|
+
# the doc won't fail with a conflict.
|
141
|
+
#
|
142
|
+
# @param [Hash] response from the put request
|
143
|
+
# @api private
|
144
|
+
def update_doc_rev( response )
|
145
|
+
document[:_rev] = response['rev']
|
146
|
+
end
|
147
|
+
|
148
|
+
# Creates a hash for the CouchDB _attachments key.
|
149
|
+
# @example
|
150
|
+
# "_attachments":
|
151
|
+
# {
|
152
|
+
# "foo.txt":
|
153
|
+
# {
|
154
|
+
# "content_type":"text\/plain",
|
155
|
+
# "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
|
156
|
+
# },
|
157
|
+
#
|
158
|
+
# "bar.txt":
|
159
|
+
# {
|
160
|
+
# "content_type":"text\/plain",
|
161
|
+
# "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
|
162
|
+
# }
|
163
|
+
# }
|
164
|
+
def pack
|
165
|
+
pack_hash = {}
|
166
|
+
self.keys.each do |key|
|
167
|
+
file = self[key]
|
168
|
+
content_type = MIME::Types.type_for(file.path).first
|
169
|
+
content_type = content_type.nil? ? "text\/plain" : content_type.simplified
|
170
|
+
data = {
|
171
|
+
'content_type' => content_type,
|
172
|
+
'data' => Base64.encode64( file.read ).gsub(/\s/,'')
|
173
|
+
}
|
174
|
+
file.rewind
|
175
|
+
pack_hash[key.to_s] = data
|
176
|
+
end
|
177
|
+
pack_hash
|
178
|
+
end
|
179
|
+
|
180
|
+
end # Attachments
|
181
|
+
end # CouchDB
|
182
|
+
end # Store
|
183
|
+
end # Aqua
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/http_client/rest_api'
|
2
|
+
require File.dirname(__FILE__) + '/server'
|
3
|
+
require File.dirname(__FILE__) + '/database'
|
4
|
+
require File.dirname(__FILE__) + '/attachments'
|
5
|
+
require File.dirname(__FILE__) + '/storage_methods'
|
6
|
+
require File.dirname(__FILE__) + '/design_document'
|
7
|
+
|
8
|
+
module Aqua
|
9
|
+
module Store
|
10
|
+
module CouchDB
|
11
|
+
|
12
|
+
class ResourceNotFound < IOError; end
|
13
|
+
class RequestFailed < IOError; end
|
14
|
+
class RequestTimeout < IOError; end
|
15
|
+
class ServerBrokeConnection < IOError; end
|
16
|
+
class Conflict < ArgumentError; end
|
17
|
+
|
18
|
+
# Returns a string describing the http adapter in use, or loads the default and returns a similar string
|
19
|
+
# @return [String] A string identifier for the HTTP adapter in use
|
20
|
+
def self.http_adapter
|
21
|
+
@adapter ||= set_http_adapter
|
22
|
+
end
|
23
|
+
|
24
|
+
# Sets a class variable with the library name that will be loaded.
|
25
|
+
# Then attempts to load said library from the adapter directory.
|
26
|
+
# It is extended into the HttpAbstraction module. Then the RestAPI which
|
27
|
+
# references the HttpAbstraction module is loaded/extended into Aqua
|
28
|
+
# this makes available Aqua.get 'http:://someaddress.com' and other requests
|
29
|
+
|
30
|
+
# Loads an http_adapter from the internal http_client libraries. Right now there is only the
|
31
|
+
# RestClient Adapter. Other adapters will be added when people get motivated to write and submit them.
|
32
|
+
# By default the RestClientAdapter is used, and if the CouchDB module is used without prior configuration
|
33
|
+
# it is automatically loaded.
|
34
|
+
#
|
35
|
+
# @param [optional, String] Maps to the HTTP Client Adapter module name, file name is inferred by removing the 'Adapter' suffix and underscoring the string
|
36
|
+
# @return [String] Name of HTTP Client Adapter module
|
37
|
+
# @see Aqua::Store::CouchDB::RestAPI Has detail about the required interface
|
38
|
+
# @api public
|
39
|
+
def self.set_http_adapter( mod_string='RestClientAdapter' )
|
40
|
+
|
41
|
+
# what is happening here:
|
42
|
+
# strips the Adapter portion of the module name to get at the client name
|
43
|
+
# convention over configurationing to get the file name as it relates to files in http_client/adapter
|
44
|
+
# require the hopefully found file
|
45
|
+
# modify the RestAPI class to extend the Rest methods from the adapter
|
46
|
+
# add the RestAPI to Aqua for easy access throughout the library
|
47
|
+
|
48
|
+
@adapter = mod_string
|
49
|
+
mod = @adapter.gsub(/Adapter/, '')
|
50
|
+
file = mod.underscore
|
51
|
+
require File.dirname(__FILE__) + "/http_client/adapter/#{file}"
|
52
|
+
RestAPI.adapter = "#{mod_string}".constantize
|
53
|
+
extend(::RestAPI)
|
54
|
+
@adapter # return the adapter
|
55
|
+
end
|
56
|
+
|
57
|
+
# Cache of CouchDB Servers used by Aqua. Each is identified by its namespace.
|
58
|
+
#
|
59
|
+
# @api private
|
60
|
+
def self.servers
|
61
|
+
@servers ||= {}
|
62
|
+
end
|
63
|
+
|
64
|
+
# Reader for getting or initializtion and getting a server by namespace. Used by various parts of store
|
65
|
+
# to define storage strategies. Also conserves memory so that there is only one instance of a Server per
|
66
|
+
# namespace.
|
67
|
+
#
|
68
|
+
# @param [String] Server Namespace
|
69
|
+
# @api private
|
70
|
+
def self.server( namespace=nil )
|
71
|
+
namespace ||= :aqua
|
72
|
+
namespace = namespace.to_sym unless namespace.class == Symbol
|
73
|
+
s = servers[ namespace ]
|
74
|
+
s = servers[namespace.to_sym] = Server.new( :namespace => namespace ) unless s
|
75
|
+
s
|
76
|
+
end
|
77
|
+
|
78
|
+
# Clears the cached servers. So far this is most useful for testing.
|
79
|
+
# API will depend on usefulness outside this.
|
80
|
+
#
|
81
|
+
# @api private
|
82
|
+
def self.clear_servers
|
83
|
+
@servers = {}
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
# TEXT HELPERS ================================================
|
88
|
+
|
89
|
+
# This comes from the CouchRest Library and its licence applies.
|
90
|
+
# It is included in this library as LICENCE_COUCHREST.
|
91
|
+
# The method breaks the parameters into a url query string.
|
92
|
+
#
|
93
|
+
# @param [String] The base url upon which to attach query params
|
94
|
+
# @param [optional Hash] A series of key value pairs that define the url query params
|
95
|
+
# @api semi-public
|
96
|
+
def self.paramify_url( url, params = {} )
|
97
|
+
if params && !params.empty?
|
98
|
+
query = params.collect do |k,v|
|
99
|
+
v = v.to_json if %w{key startkey endkey}.include?(k.to_s)
|
100
|
+
"#{k}=#{CGI.escape(v.to_s)}"
|
101
|
+
end.join("&")
|
102
|
+
url = "#{url}?#{query}"
|
103
|
+
end
|
104
|
+
url
|
105
|
+
end
|
106
|
+
|
107
|
+
# A convenience method for escaping a string,
|
108
|
+
# namespaced classes with :: notation will be converted to __
|
109
|
+
# all other non-alpha numeric characters besides hyphens and underscores are removed
|
110
|
+
#
|
111
|
+
# @param [String] to be converted
|
112
|
+
# @return [String] converted
|
113
|
+
#
|
114
|
+
# @api private
|
115
|
+
def self.escape( str )
|
116
|
+
str.gsub!('::', '__')
|
117
|
+
str.gsub!(/[^a-z0-9\-_]/, '')
|
118
|
+
str
|
119
|
+
end
|
120
|
+
|
121
|
+
# DATABASE STRATEGIES ----------------------------------
|
122
|
+
# This library was built with to be flexible but have some sensible defaults. Database strategies is
|
123
|
+
# one of those areas. You can configure the CouchDB module to use one of three ways of managing data
|
124
|
+
# into databases:
|
125
|
+
# * :single - This is the default. It uses the CouchDB.server(:aqua) to build a single database where
|
126
|
+
# all the documents are stored.
|
127
|
+
# * :per_class - This strategy is the opposite of the single strategy in that each class has it's own
|
128
|
+
# database. This will make complex cross class lookups more difficult.
|
129
|
+
# * :configured - Each class configures its own database and server namespace. Any server not
|
130
|
+
# configured will default to the CouchDB.server. Any database not configured will default to the
|
131
|
+
# default database ... that set by the server namespace.
|
132
|
+
# TODO: store these strategies; give feedback to documents about the appropriate database.
|
133
|
+
|
134
|
+
|
135
|
+
# AUTOLOADING ---------
|
136
|
+
# auto loads the default http_adapter if Aqua gets used without configuring it first
|
137
|
+
|
138
|
+
class << self
|
139
|
+
def method_missing( method, *args )
|
140
|
+
if @adapter.nil?
|
141
|
+
set_http_adapter # loads up the adapter related stuff
|
142
|
+
send( method.to_sym, eval(args.map{|value| "'#{value}'"}.join(', ')) )
|
143
|
+
else
|
144
|
+
raise NoMethodError
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
end # CouchDB
|
150
|
+
end # Store
|
151
|
+
end # Aqua
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require "base64"
|
2
|
+
|
3
|
+
module Aqua
|
4
|
+
module Store
|
5
|
+
module CouchDB
|
6
|
+
class Database
|
7
|
+
attr_reader :server, :name, :uri
|
8
|
+
attr_accessor :bulk_cache
|
9
|
+
|
10
|
+
# Create a CouchDB database representation from a name. Does not actually create a database on couchdb.
|
11
|
+
# It does not ensure that the database actually exists either. Just creates a ruby representation
|
12
|
+
# of a ruby database interface.
|
13
|
+
#
|
14
|
+
# @param [optional String] Name of database. If not provided server namespace will be used as database name.
|
15
|
+
# @param [optional Hash] Options for initialization. Currently the only option is :server which must be either a CouchDB server object or a symbol representing a server stored in the CouchDB module.
|
16
|
+
# @return [Database] The initialized object
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
def initialize( name=nil, opts={})
|
20
|
+
name = nil if name && name.empty?
|
21
|
+
opts = Mash.new( opts ) unless opts.empty?
|
22
|
+
@name = name if name
|
23
|
+
initialize_server( opts[:server] )
|
24
|
+
@uri = "#{server.uri}/#{namespaced( name )}"
|
25
|
+
self.bulk_cache = []
|
26
|
+
end
|
27
|
+
|
28
|
+
# Initializes the database server with the option provided. If not option is provided the default CouchDB
|
29
|
+
# server is used instead.
|
30
|
+
#
|
31
|
+
# @param [Symbol, Server]
|
32
|
+
# If server_option argument is a Symbol then the CouchDB server stash will be queried for a matching
|
33
|
+
# server. CouchDB manages the creation of that server in the stash, if not found.
|
34
|
+
# If server_option argument is a Server object then then it is added directly to the database object.
|
35
|
+
# No management of the server will be done with CouchDB's server stash.
|
36
|
+
# @raise [ArgumentError] Raised if a server_option is passed in and if that option is neither a Hash nor a Symbol.
|
37
|
+
# @return [Server]
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
def initialize_server( server_option )
|
41
|
+
if server_option
|
42
|
+
if server_option.class == Symbol
|
43
|
+
@server = CouchDB.server( server_option )
|
44
|
+
elsif server_option.class == Aqua::Store::CouchDB::Server
|
45
|
+
@server = server_option # WARNING: this won't get stashed in CouchDB for use with other database.
|
46
|
+
else
|
47
|
+
raise ArgumentError, ":server option must be a symbol identifying a CouchDB server, or a Server object"
|
48
|
+
end
|
49
|
+
else
|
50
|
+
@server = CouchDB.server
|
51
|
+
end
|
52
|
+
@server
|
53
|
+
end
|
54
|
+
|
55
|
+
# Namespaces the database path for the given server. If no name is provided, then the database name is
|
56
|
+
# just the Server's namespace.
|
57
|
+
#
|
58
|
+
# @param [String] Name that the database is initialized with, if any.
|
59
|
+
# @return [String] Namespaced database for use as a http path
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
def namespaced( name )
|
63
|
+
if name
|
64
|
+
"#{server.namespace}_#{CouchDB.escape(@name)}"
|
65
|
+
else
|
66
|
+
server.namespace
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Creates a database representation and PUTs it on the CouchDB server.
|
71
|
+
# If successfull returns a database object. If not successful in creating
|
72
|
+
# the database on the CouchDB server then, false will be returned.
|
73
|
+
#
|
74
|
+
# @see Aqua::Store::CouchDB#initialize for option details
|
75
|
+
# @return [Database, false] Will return the database on success, and false if it did not succeed.
|
76
|
+
#
|
77
|
+
# @api pubilc
|
78
|
+
def self.create( name=nil, opts={} )
|
79
|
+
db = new(name, opts)
|
80
|
+
begin
|
81
|
+
CouchDB.put( db.uri )
|
82
|
+
rescue Exception => e # catch database already exists errors ...
|
83
|
+
unless e.message.match(/412/)
|
84
|
+
db = false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
db
|
88
|
+
end
|
89
|
+
|
90
|
+
# Creates a database representation and PUTs it on the CouchDB server.
|
91
|
+
# This version of the #create method raises an error if the PUT request fails.
|
92
|
+
# The exception on this, is if the database already exists then the 412 HTTP code will be ignored.
|
93
|
+
#
|
94
|
+
# @see Aqua::Store::CouchDB#initialize for option details
|
95
|
+
# @return [Database] Will return the database on success.
|
96
|
+
# @raise HttpAdapter Exceptions depending on the reason for failure.
|
97
|
+
#
|
98
|
+
# @api pubilc
|
99
|
+
def self.create!( name=nil, opts={} )
|
100
|
+
db = new( name, opts )
|
101
|
+
begin
|
102
|
+
CouchDB.put( db.uri )
|
103
|
+
rescue Exception => e # catch database already exists errors ...
|
104
|
+
raise e unless e.class == RequestFailed && e.message.match(/412/)
|
105
|
+
end
|
106
|
+
db
|
107
|
+
end
|
108
|
+
|
109
|
+
# Checks to see if the database exists on the couchdb server.
|
110
|
+
#
|
111
|
+
# @return [true, false] depending on whether the database already exists in CouchDB land
|
112
|
+
#
|
113
|
+
# @api public
|
114
|
+
def exists?
|
115
|
+
begin
|
116
|
+
info
|
117
|
+
true
|
118
|
+
rescue CouchDB::ResourceNotFound
|
119
|
+
false
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# GET the database info from CouchDB
|
124
|
+
def info
|
125
|
+
CouchDB.get( uri )
|
126
|
+
end
|
127
|
+
|
128
|
+
# Deletes a database; use with caution as this isn't reversible.
|
129
|
+
#
|
130
|
+
# @return A JSON response on success. nil if the resource is not found. And raises an error if another exception was raised
|
131
|
+
# @raise Exception related to request failure that is not a ResourceNotFound error.
|
132
|
+
def delete
|
133
|
+
begin
|
134
|
+
CouchDB.delete( uri )
|
135
|
+
rescue CouchDB::ResourceNotFound
|
136
|
+
nil
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Deletes a database; use with caution as this isn't reversible. Similar to #delete,
|
141
|
+
# except that it will raise an error on failure to find the database.
|
142
|
+
#
|
143
|
+
# @return A JSON response on success.
|
144
|
+
# @raise Exception related to request failure or ResourceNotFound.
|
145
|
+
def delete!
|
146
|
+
CouchDB.delete( uri )
|
147
|
+
end
|
148
|
+
|
149
|
+
# # Query the <tt>documents</tt> view. Accepts all the same arguments as view.
|
150
|
+
def documents(params = {})
|
151
|
+
keys = params.delete(:keys)
|
152
|
+
url = CouchDB.paramify_url( "#{uri}/_all_docs", params )
|
153
|
+
if keys
|
154
|
+
CouchDB.post(url, {:keys => keys})
|
155
|
+
else
|
156
|
+
CouchDB.get url
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Deletes all the documents in a given database
|
161
|
+
def delete_all
|
162
|
+
documents['rows'].each do |doc|
|
163
|
+
CouchDB.delete( "#{uri}/#{CGI.escape( doc['id'])}?rev=#{doc['value']['rev']}" ) #rescue nil
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# BULK ACTIVITIES ------------------------------------------
|
168
|
+
def add_to_bulk_cache( doc )
|
169
|
+
if server.uuid_count/2.0 > bulk_cache.size
|
170
|
+
self.bulk_cache << doc
|
171
|
+
else
|
172
|
+
bulk_save
|
173
|
+
self.bulk_cache << doc
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def bulk_save
|
178
|
+
docs = bulk_cache
|
179
|
+
self.bulk_cache = []
|
180
|
+
CouchDB.post( "#{uri}/_bulk_docs", {:docs => docs} )
|
181
|
+
end
|
182
|
+
|
183
|
+
end # Database
|
184
|
+
end # CouchDB
|
185
|
+
end # Store
|
186
|
+
end # Aqua
|