persistence-adapter-kyotocabinet 0.0.1
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/CHANGELOG.rdoc +4 -0
- data/README.md +65 -0
- data/lib/persistence/adapter/kyotocabinet.rb +18 -0
- data/lib/persistence/adapter/kyotocabinet/adapter_interface.rb +196 -0
- data/lib/persistence/adapter/kyotocabinet/bucket.rb +6 -0
- data/lib/persistence/adapter/kyotocabinet/bucket/bucket_interface.rb +435 -0
- data/lib/persistence/adapter/kyotocabinet/bucket/index.rb +6 -0
- data/lib/persistence/adapter/kyotocabinet/bucket/index/index_interface.rb +165 -0
- data/lib/persistence/adapter/kyotocabinet/cursor.rb +6 -0
- data/lib/persistence/adapter/kyotocabinet/cursor/cursor_interface.rb +163 -0
- data/lib/persistence/adapter/kyotocabinet/database_support.rb +122 -0
- data/lib/persistence/adapter/kyotocabinet/marshal.rb +14 -0
- data/lib/persistence/adapter/kyotocabinet/yaml.rb +14 -0
- data/lib/persistence/adapter/namespaces.rb +10 -0
- data/lib/persistence/adapter/requires.rb +28 -0
- data/spec/Persistence/Adapter/kyotocabinet/cursor_spec.rb +25 -0
- data/spec/Persistence/Adapter/kyotocabinet/marshal_spec.rb +15 -0
- data/spec/Persistence/Adapter/kyotocabinet/yaml_spec.rb +15 -0
- data/spec/Persistence/Adapter/kyotocabinet_spec.rb +25 -0
- metadata +80 -0
data/CHANGELOG.rdoc
ADDED
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# KyotoCabinet Persistence Adapter #
|
2
|
+
|
3
|
+
http://rubygems.org/gems/persistence-adapter-kyotocabinet
|
4
|
+
|
5
|
+
# Summary #
|
6
|
+
|
7
|
+
Adapter to use <a href="http://fallabs.com/kyotocabinet/">KyotoCabinet</a> as storage port for <a href="https://rubygems.org/gems/persistence">Persistence</a> (<a href="https://github.com/RidiculousPower/persistence">on GitHub</a>).
|
8
|
+
|
9
|
+
# Description #
|
10
|
+
|
11
|
+
Implements necessary methods to run Persistence on top of Kyoto Cabinet.
|
12
|
+
|
13
|
+
# Install #
|
14
|
+
|
15
|
+
* sudo gem install persistence-adapter-kyotocabinet
|
16
|
+
|
17
|
+
# Usage #
|
18
|
+
|
19
|
+
The KyotoCabinet adapter is an abstract implementation. Using it requires specifying serialization methods. This permits the creation of concrete adapter implementations that are highly configurable.
|
20
|
+
|
21
|
+
At this point, two versions exist:
|
22
|
+
|
23
|
+
* KyotoCabinet::Marshal
|
24
|
+
* KyotoCabinet::YAML
|
25
|
+
|
26
|
+
To use Marshal:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
kyotocabinet_marshal_adapter = ::Persistence::Adapter::KyotoCabinet::Marshal.new
|
30
|
+
|
31
|
+
Persistence.enable_port( :kyotocabinet_marshal_port, kyotocabinet_marshal_adapter )
|
32
|
+
```
|
33
|
+
|
34
|
+
To use YAML:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
kyotocabinet_yaml_adapter = ::Persistence::Adapter::KyotoCabinet::YAML.new
|
38
|
+
|
39
|
+
Persistence.enable_port( :kyotocabinet_yaml_port, kyotocabinet_yaml_adapter )
|
40
|
+
```
|
41
|
+
|
42
|
+
# License #
|
43
|
+
|
44
|
+
(The MIT License)
|
45
|
+
|
46
|
+
Copyright (c) 2012, Asher, Ridiculous Power
|
47
|
+
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
49
|
+
a copy of this software and associated documentation files (the
|
50
|
+
'Software'), to deal in the Software without restriction, including
|
51
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
52
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
53
|
+
permit persons to whom the Software is furnished to do so, subject to
|
54
|
+
the following conditions:
|
55
|
+
|
56
|
+
The above copyright notice and this permission notice shall be
|
57
|
+
included in all copies or substantial portions of the Software.
|
58
|
+
|
59
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
60
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
61
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
62
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
63
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
64
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
65
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
require 'persistence'
|
3
|
+
|
4
|
+
require 'kyotocabinet'
|
5
|
+
|
6
|
+
require 'yaml'
|
7
|
+
|
8
|
+
# namespaces that have to be declared ahead of time for proper load order
|
9
|
+
require_relative './namespaces'
|
10
|
+
|
11
|
+
# source file requires
|
12
|
+
require_relative './requires.rb'
|
13
|
+
|
14
|
+
class ::Persistence::Adapter::KyotoCabinet
|
15
|
+
|
16
|
+
include ::Persistence::Adapter::KyotoCabinet::AdapterInterface
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
|
2
|
+
module ::Persistence::Adapter::KyotoCabinet::AdapterInterface
|
3
|
+
|
4
|
+
include ::Persistence::Adapter::Abstract::EnableDisable
|
5
|
+
|
6
|
+
include ::Persistence::Adapter::KyotoCabinet::DatabaseSupport
|
7
|
+
|
8
|
+
DatabaseFlags = ::KyotoCabinet::DB::OWRITER | ::KyotoCabinet::DB::OCREATE
|
9
|
+
|
10
|
+
Delimiter = '.'
|
11
|
+
|
12
|
+
################
|
13
|
+
# initialize #
|
14
|
+
################
|
15
|
+
|
16
|
+
def initialize( home_directory )
|
17
|
+
|
18
|
+
super( home_directory )
|
19
|
+
|
20
|
+
@buckets = { }
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
############
|
25
|
+
# enable #
|
26
|
+
############
|
27
|
+
|
28
|
+
def enable
|
29
|
+
|
30
|
+
super
|
31
|
+
|
32
|
+
# holds global ID sequence
|
33
|
+
@database__id_sequence = ::KyotoCabinet::DB.new
|
34
|
+
@database__id_sequence.open( file__id_sequence_database, DatabaseFlags )
|
35
|
+
|
36
|
+
# holds global ID => primary bucket
|
37
|
+
@database__primary_bucket_for_id = ::KyotoCabinet::DB.new
|
38
|
+
@database__primary_bucket_for_id.open( file__primary_bucket_for_id_database, DatabaseFlags )
|
39
|
+
|
40
|
+
return self
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
#############
|
45
|
+
# disable #
|
46
|
+
#############
|
47
|
+
|
48
|
+
def disable
|
49
|
+
|
50
|
+
super
|
51
|
+
|
52
|
+
@database__id_sequence.close
|
53
|
+
@database__primary_bucket_for_id.close
|
54
|
+
|
55
|
+
return self
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
########################
|
60
|
+
# persistence_bucket #
|
61
|
+
########################
|
62
|
+
|
63
|
+
def persistence_bucket( bucket_name )
|
64
|
+
|
65
|
+
bucket_instance = nil
|
66
|
+
|
67
|
+
unless bucket_instance = @buckets[ bucket_name ]
|
68
|
+
bucket_instance = ::Persistence::Adapter::KyotoCabinet::Bucket.new( self, bucket_name )
|
69
|
+
@buckets[ bucket_name ] = bucket_instance
|
70
|
+
end
|
71
|
+
|
72
|
+
return bucket_instance
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
###################################
|
77
|
+
# get_bucket_name_for_object_id #
|
78
|
+
###################################
|
79
|
+
|
80
|
+
def get_bucket_name_for_object_id( global_id )
|
81
|
+
|
82
|
+
bucket_name = @database__primary_bucket_for_id.get( global_id )
|
83
|
+
|
84
|
+
bucket_name = bucket_name.to_sym if bucket_name
|
85
|
+
|
86
|
+
return bucket_name
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
#############################
|
91
|
+
# get_class_for_object_id #
|
92
|
+
#############################
|
93
|
+
|
94
|
+
def get_class_for_object_id( global_id )
|
95
|
+
|
96
|
+
bucket_name = get_bucket_name_for_object_id( global_id )
|
97
|
+
|
98
|
+
bucket_instance = persistence_bucket( bucket_name )
|
99
|
+
|
100
|
+
return bucket_instance.get_class( global_id )
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
#################################
|
105
|
+
# delete_bucket_for_object_id #
|
106
|
+
#################################
|
107
|
+
|
108
|
+
def delete_bucket_for_object_id( global_id )
|
109
|
+
|
110
|
+
return @database__primary_bucket_for_id.remove( global_id )
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
################################
|
115
|
+
# delete_class_for_object_id #
|
116
|
+
################################
|
117
|
+
|
118
|
+
def delete_class_for_object_id( global_id )
|
119
|
+
|
120
|
+
bucket_name = get_bucket_name_for_object_id( global_id )
|
121
|
+
|
122
|
+
bucket_instance = persistence_bucket( bucket_name )
|
123
|
+
|
124
|
+
return bucket_instance.delete_class( global_id )
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
##########################################
|
129
|
+
# ensure_object_has_globally_unique_id #
|
130
|
+
##########################################
|
131
|
+
|
132
|
+
def ensure_object_has_globally_unique_id( object )
|
133
|
+
|
134
|
+
unless object.persistence_id
|
135
|
+
|
136
|
+
# we only store one sequence so we don't need a key; increment it by 1
|
137
|
+
global_id = @database__id_sequence.increment( :sequence, 1, -1 )
|
138
|
+
|
139
|
+
# and write it to our global object database with a bucket/key struct as data
|
140
|
+
@database__primary_bucket_for_id.set( global_id, object.persistence_bucket.name )
|
141
|
+
|
142
|
+
object.persistence_id = global_id
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
return self
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
##################################################################################################
|
151
|
+
private ######################################################################################
|
152
|
+
##################################################################################################
|
153
|
+
|
154
|
+
################################
|
155
|
+
# file__id_sequence_database #
|
156
|
+
################################
|
157
|
+
|
158
|
+
def file__id_sequence_database
|
159
|
+
|
160
|
+
return File.join( home_directory,
|
161
|
+
'IDSequence' + extension__id_sequence_database )
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
##########################################
|
166
|
+
# file__primary_bucket_for_id_database #
|
167
|
+
##########################################
|
168
|
+
|
169
|
+
def file__primary_bucket_for_id_database
|
170
|
+
|
171
|
+
return File.join( home_directory,
|
172
|
+
'PrimaryBucketForID' + extension__primary_bucket_for_id_database )
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
#####################################
|
177
|
+
# extension__id_sequence_database #
|
178
|
+
#####################################
|
179
|
+
|
180
|
+
def extension__id_sequence_database
|
181
|
+
|
182
|
+
return extension__database( :tree )
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
###############################################
|
187
|
+
# extension__primary_bucket_for_id_database #
|
188
|
+
###############################################
|
189
|
+
|
190
|
+
def extension__primary_bucket_for_id_database
|
191
|
+
|
192
|
+
return extension__database( :hash )
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
@@ -0,0 +1,435 @@
|
|
1
|
+
|
2
|
+
module ::Persistence::Adapter::KyotoCabinet::Bucket::BucketInterface
|
3
|
+
|
4
|
+
include ::Persistence::Adapter::Abstract::PrimaryKey::IDPropertyString
|
5
|
+
|
6
|
+
include ::Persistence::Adapter::KyotoCabinet::DatabaseSupport
|
7
|
+
|
8
|
+
attr_accessor :parent_adapter, :name
|
9
|
+
|
10
|
+
# we're always opening as writers and creating the files if they don't exist
|
11
|
+
|
12
|
+
################
|
13
|
+
# initialize #
|
14
|
+
################
|
15
|
+
|
16
|
+
def initialize( parent_adapter, bucket_name )
|
17
|
+
|
18
|
+
@parent_adapter = parent_adapter
|
19
|
+
@name = bucket_name
|
20
|
+
|
21
|
+
# storage for index objects
|
22
|
+
@indexes = { }
|
23
|
+
|
24
|
+
database_flags = @parent_adapter.class::DatabaseFlags
|
25
|
+
|
26
|
+
# bucket database corresponding to self - holds properties
|
27
|
+
#
|
28
|
+
# objectID => klass
|
29
|
+
# objectID.property_A => property_value_A
|
30
|
+
# objectID.property_B => property_value_B
|
31
|
+
#
|
32
|
+
@database__bucket = ::KyotoCabinet::DB.new
|
33
|
+
@database__bucket.open( file__bucket_database( bucket_name ), database_flags )
|
34
|
+
|
35
|
+
# holds IDs that are presently in this bucket so we can iterate objects normally
|
36
|
+
#
|
37
|
+
# objectID => objectID
|
38
|
+
#
|
39
|
+
@database__ids_in_bucket = ::KyotoCabinet::DB.new
|
40
|
+
@database__ids_in_bucket.open( file__ids_in_bucket_database( bucket_name ), database_flags )
|
41
|
+
|
42
|
+
# holds whether each index permits duplicates
|
43
|
+
@database__index_permits_duplicates = ::KyotoCabinet::DB.new
|
44
|
+
@database__index_permits_duplicates.open( file__index_permits_duplicates_database( bucket_name ), database_flags )
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
###########
|
49
|
+
# count #
|
50
|
+
###########
|
51
|
+
|
52
|
+
def count
|
53
|
+
|
54
|
+
return @database__ids_in_bucket.count
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
###########
|
59
|
+
# close #
|
60
|
+
###########
|
61
|
+
|
62
|
+
def close
|
63
|
+
|
64
|
+
close_indexes
|
65
|
+
|
66
|
+
@database__index_permits_duplicates.close
|
67
|
+
|
68
|
+
super
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
###################
|
73
|
+
# close_indexes #
|
74
|
+
###################
|
75
|
+
|
76
|
+
def close_indexes
|
77
|
+
|
78
|
+
@indexes.each do |this_index_name, this_index_instance|
|
79
|
+
this_index_instance.close
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
############
|
85
|
+
# cursor #
|
86
|
+
############
|
87
|
+
|
88
|
+
def cursor
|
89
|
+
return ::Persistence::Adapter::KyotoCabinet::Cursor.new( self, nil, @database__ids_in_bucket.cursor )
|
90
|
+
end
|
91
|
+
|
92
|
+
#########################
|
93
|
+
# permits_duplicates? #
|
94
|
+
#########################
|
95
|
+
|
96
|
+
def permits_duplicates?( index )
|
97
|
+
|
98
|
+
permits_duplicates = @database__index_permits_duplicates.get( index )
|
99
|
+
permits_duplicates = ( permits_duplicates == 1 ? true : false ) unless permits_duplicates.nil?
|
100
|
+
|
101
|
+
return permits_duplicates
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
#################
|
106
|
+
# put_object! #
|
107
|
+
#################
|
108
|
+
|
109
|
+
def put_object!( object )
|
110
|
+
|
111
|
+
@parent_adapter.ensure_object_has_globally_unique_id( object )
|
112
|
+
|
113
|
+
# insert object class definition: ID => klass
|
114
|
+
# class definition is used as header/placeholder for object properties
|
115
|
+
@database__bucket.set( object.persistence_id, object.class.to_s )
|
116
|
+
|
117
|
+
# insert ID to cursor index
|
118
|
+
@database__ids_in_bucket.set( object.persistence_id, object.persistence_id )
|
119
|
+
|
120
|
+
# insert properties
|
121
|
+
object.persistence_hash_to_port.each do |primary_key, attribute_value|
|
122
|
+
put_attribute!( object.persistence_id, primary_key, attribute_value )
|
123
|
+
end
|
124
|
+
|
125
|
+
return object.persistence_id
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
################
|
130
|
+
# get_object #
|
131
|
+
################
|
132
|
+
|
133
|
+
def get_object( global_id )
|
134
|
+
|
135
|
+
object_persistence_hash = { }
|
136
|
+
|
137
|
+
# create cursor and set to position of ID
|
138
|
+
@database__bucket.cursor_process do |object_cursor|
|
139
|
+
|
140
|
+
if object_cursor.jump( global_id )
|
141
|
+
|
142
|
+
# Iterate until the key no longer begins with ID
|
143
|
+
# First record (ID only, no attribute) points to klass, so we have to move forward to start.
|
144
|
+
while this_attribute = next_attribute_of_this_object( object_cursor, global_id )
|
145
|
+
|
146
|
+
serialized_value = object_cursor.get_value
|
147
|
+
|
148
|
+
value = @parent_adapter.class::SerializationClass.__send__( @parent_adapter.class::UnserializationMethod, serialized_value )
|
149
|
+
|
150
|
+
object_persistence_hash[ this_attribute ] = value
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
return object_persistence_hash.empty? ? nil : object_persistence_hash
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
####################
|
163
|
+
# delete_object! #
|
164
|
+
####################
|
165
|
+
|
166
|
+
def delete_object!( global_id )
|
167
|
+
|
168
|
+
# delete from IDs in bucket database
|
169
|
+
@database__ids_in_bucket.remove( global_id )
|
170
|
+
|
171
|
+
# create cursor and set to position of ID
|
172
|
+
@database__bucket.cursor_process do |object_cursor|
|
173
|
+
|
174
|
+
if object_cursor.jump( global_id )
|
175
|
+
|
176
|
+
object_cursor.remove
|
177
|
+
|
178
|
+
# Iterate until the key no longer begins with ID
|
179
|
+
# First record (ID only, no attribute) points to klass, so we have to move forward to start.
|
180
|
+
while this_attribute = next_attribute_of_this_object( object_cursor, global_id )
|
181
|
+
|
182
|
+
this_attribute_value = object_cursor.get_value
|
183
|
+
|
184
|
+
object_cursor.remove
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
####################
|
195
|
+
# put_attribute! #
|
196
|
+
####################
|
197
|
+
|
198
|
+
def put_attribute!( global_id, attribute_name, value )
|
199
|
+
|
200
|
+
serialization_class = @parent_adapter.class::SerializationClass
|
201
|
+
|
202
|
+
serialized_value = serialization_class.__send__( @parent_adapter.class::SerializationMethod, value )
|
203
|
+
|
204
|
+
@database__bucket.set( attribute_name, serialized_value )
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
###################
|
209
|
+
# get_attribute #
|
210
|
+
###################
|
211
|
+
|
212
|
+
def get_attribute( global_id, attribute_name )
|
213
|
+
|
214
|
+
value = nil
|
215
|
+
|
216
|
+
if serialized_value = @database__bucket.get( attribute_name )
|
217
|
+
|
218
|
+
serialization_class = @parent_adapter.class::SerializationClass
|
219
|
+
|
220
|
+
value = serialization_class.__send__( @parent_adapter.class::UnserializationMethod, serialized_value )
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
return value
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
#######################
|
229
|
+
# delete_attribute! #
|
230
|
+
#######################
|
231
|
+
|
232
|
+
def delete_attribute!( global_id, attribute_name )
|
233
|
+
|
234
|
+
# delete primary info on attribute
|
235
|
+
@database__bucket.remove( attribute_name )
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
##################
|
240
|
+
# create_index #
|
241
|
+
##################
|
242
|
+
|
243
|
+
def create_index( index_name, permits_duplicates )
|
244
|
+
|
245
|
+
# make sure index doesn't already exist with conflict duplicate permission
|
246
|
+
unless ( permits_duplicates_value = permits_duplicates?( index_name ) ).nil?
|
247
|
+
if ! permits_duplicates_value != ! permits_duplicates
|
248
|
+
raise 'Index on :' + index_name.to_s + ' already exists and ' +
|
249
|
+
( permits_duplicates ? 'does not permit' : 'permits' ) + ' duplicates, which conflicts.'
|
250
|
+
end
|
251
|
+
|
252
|
+
else
|
253
|
+
|
254
|
+
@database__index_permits_duplicates.set( index_name, permits_duplicates )
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
# create/instantiate the index
|
259
|
+
index_instance = self.class::Index.new( index_name, self, permits_duplicates )
|
260
|
+
|
261
|
+
# store index instance
|
262
|
+
@indexes[ index_name ] = index_instance
|
263
|
+
|
264
|
+
return index_instance
|
265
|
+
|
266
|
+
end
|
267
|
+
|
268
|
+
###########
|
269
|
+
# index #
|
270
|
+
###########
|
271
|
+
|
272
|
+
def index( index_name )
|
273
|
+
|
274
|
+
return @indexes[ index_name ]
|
275
|
+
|
276
|
+
end
|
277
|
+
|
278
|
+
##################
|
279
|
+
# delete_index #
|
280
|
+
##################
|
281
|
+
|
282
|
+
def delete_index( index_name )
|
283
|
+
|
284
|
+
# remove permits_duplicates configuration
|
285
|
+
@database__index_permits_duplicates.remove( index_name )
|
286
|
+
|
287
|
+
index_instance = @indexes.delete( index_name )
|
288
|
+
|
289
|
+
index_instance.delete
|
290
|
+
|
291
|
+
end
|
292
|
+
|
293
|
+
################
|
294
|
+
# has_index? #
|
295
|
+
################
|
296
|
+
|
297
|
+
def has_index?( index_name )
|
298
|
+
|
299
|
+
return @indexes.has_key?( index_name )
|
300
|
+
|
301
|
+
end
|
302
|
+
|
303
|
+
###############
|
304
|
+
# get_class #
|
305
|
+
###############
|
306
|
+
|
307
|
+
def get_class( global_id )
|
308
|
+
|
309
|
+
klass = nil
|
310
|
+
|
311
|
+
if klass_path_string = @database__bucket.get( global_id )
|
312
|
+
|
313
|
+
klass_path_parts = klass_path_string.split( '::' )
|
314
|
+
|
315
|
+
klass = klass_path_parts.inject( Object ) do |object_container_namespace, next_path_part|
|
316
|
+
object_container_namespace.const_get( next_path_part )
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
320
|
+
|
321
|
+
return klass
|
322
|
+
|
323
|
+
end
|
324
|
+
|
325
|
+
###############
|
326
|
+
# get_class #
|
327
|
+
###############
|
328
|
+
|
329
|
+
def delete_class( global_id )
|
330
|
+
|
331
|
+
@database__bucket.remove( global_id )
|
332
|
+
|
333
|
+
end
|
334
|
+
|
335
|
+
##################################################################################################
|
336
|
+
private ######################################################################################
|
337
|
+
##################################################################################################
|
338
|
+
|
339
|
+
###################################
|
340
|
+
# next_attribute_of_this_object #
|
341
|
+
###################################
|
342
|
+
|
343
|
+
def next_attribute_of_this_object( object_cursor, global_id )
|
344
|
+
|
345
|
+
attribute_name = nil
|
346
|
+
|
347
|
+
if object_cursor.step and primary_key = object_cursor.get_key
|
348
|
+
|
349
|
+
this_global_id, this_attribute = primary_key.split( @parent_adapter.class::Delimiter )
|
350
|
+
|
351
|
+
if this_global_id.to_i == global_id
|
352
|
+
attribute_name = this_attribute.to_sym
|
353
|
+
end
|
354
|
+
|
355
|
+
end
|
356
|
+
|
357
|
+
return attribute_name
|
358
|
+
|
359
|
+
end
|
360
|
+
|
361
|
+
###########################
|
362
|
+
# file__bucket_database #
|
363
|
+
###########################
|
364
|
+
|
365
|
+
def file__bucket_database( bucket_name )
|
366
|
+
|
367
|
+
return File.join( @parent_adapter.home_directory,
|
368
|
+
bucket_name.to_s + extension__bucket_database )
|
369
|
+
|
370
|
+
end
|
371
|
+
|
372
|
+
##################################
|
373
|
+
# file__ids_in_bucket_database #
|
374
|
+
##################################
|
375
|
+
|
376
|
+
def file__ids_in_bucket_database( bucket_name )
|
377
|
+
|
378
|
+
return File.join( @parent_adapter.home_directory,
|
379
|
+
bucket_name.to_s + '__ids_in_bucket__' + extension__ids_in_bucket_database )
|
380
|
+
|
381
|
+
end
|
382
|
+
|
383
|
+
#############################################
|
384
|
+
# file__index_permits_duplicates_database #
|
385
|
+
#############################################
|
386
|
+
|
387
|
+
def file__index_permits_duplicates_database( bucket_name )
|
388
|
+
|
389
|
+
index_file_name = bucket_name.to_s + '__index_permits_duplicates__' + extension__index_permits_duplicates_database
|
390
|
+
|
391
|
+
return File.join( @parent_adapter.home_directory, index_file_name )
|
392
|
+
|
393
|
+
end
|
394
|
+
|
395
|
+
################################
|
396
|
+
# extension__bucket_database #
|
397
|
+
################################
|
398
|
+
|
399
|
+
def extension__bucket_database
|
400
|
+
|
401
|
+
return extension__database( :tree )
|
402
|
+
|
403
|
+
end
|
404
|
+
|
405
|
+
###################################################
|
406
|
+
# extension__indexes_permit_duplicates_database #
|
407
|
+
###################################################
|
408
|
+
|
409
|
+
def extension__indexes_permit_duplicates_database
|
410
|
+
|
411
|
+
return extension__database( :hash )
|
412
|
+
|
413
|
+
end
|
414
|
+
|
415
|
+
#######################################
|
416
|
+
# extension__ids_in_bucket_database #
|
417
|
+
#######################################
|
418
|
+
|
419
|
+
def extension__ids_in_bucket_database
|
420
|
+
|
421
|
+
return extension__database( :hash )
|
422
|
+
|
423
|
+
end
|
424
|
+
|
425
|
+
##################################################
|
426
|
+
# extension__index_permits_duplicates_database #
|
427
|
+
##################################################
|
428
|
+
|
429
|
+
def extension__index_permits_duplicates_database
|
430
|
+
|
431
|
+
return extension__database( :hash )
|
432
|
+
|
433
|
+
end
|
434
|
+
|
435
|
+
end
|