persistence-adapter-flat_file 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/flat_file.rb +17 -0
- data/lib/persistence/adapter/flat_file/adapter_interface.rb +246 -0
- data/lib/persistence/adapter/flat_file/bucket.rb +6 -0
- data/lib/persistence/adapter/flat_file/bucket/bucket_interface.rb +374 -0
- data/lib/persistence/adapter/flat_file/bucket/index.rb +6 -0
- data/lib/persistence/adapter/flat_file/bucket/index/index_interface.rb +240 -0
- data/lib/persistence/adapter/flat_file/class_name.rb +4 -0
- data/lib/persistence/adapter/flat_file/cursor.rb +6 -0
- data/lib/persistence/adapter/flat_file/cursor/cursor_interface.rb +207 -0
- data/lib/persistence/adapter/flat_file/marshal.rb +29 -0
- data/lib/persistence/adapter/flat_file/path_helpers.rb +63 -0
- data/lib/persistence/adapter/flat_file/serialization.rb +85 -0
- data/lib/persistence/adapter/flat_file/yaml.rb +31 -0
- data/lib/persistence/adapter/flat_file/yaml/adapter_Interface.rb +21 -0
- data/lib/persistence/adapter/namespaces.rb +15 -0
- data/lib/persistence/adapter/requires.rb +32 -0
- data/spec/Persistence/Adapter/flat_file/cursor_spec.rb +28 -0
- data/spec/Persistence/Adapter/flat_file_spec.rb +28 -0
- data/spec/Persistence/Adapter/marshal/cursor_spec.rb +15 -0
- data/spec/Persistence/Adapter/marshal_spec.rb +15 -0
- data/spec/Persistence/Adapter/yaml/cursor_spec.rb +15 -0
- data/spec/Persistence/Adapter/yaml_spec.rb +15 -0
- metadata +86 -0
@@ -0,0 +1,240 @@
|
|
1
|
+
|
2
|
+
module ::Persistence::Adapter::FlatFile::Bucket::Index::IndexInterface
|
3
|
+
|
4
|
+
include ::Persistence::Adapter::FlatFile::PathHelpers
|
5
|
+
include ::Persistence::Adapter::FlatFile::Serialization
|
6
|
+
|
7
|
+
################
|
8
|
+
# initialize #
|
9
|
+
################
|
10
|
+
|
11
|
+
def initialize( index_name, parent_bucket, permits_duplicates )
|
12
|
+
|
13
|
+
@name = index_name
|
14
|
+
|
15
|
+
@parent_bucket = parent_bucket
|
16
|
+
|
17
|
+
@permits_duplicates = permits_duplicates
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
###################
|
22
|
+
# adapter_class #
|
23
|
+
###################
|
24
|
+
|
25
|
+
def adapter_class
|
26
|
+
|
27
|
+
return @parent_bucket.parent_adapter.class
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
###########
|
32
|
+
# count #
|
33
|
+
###########
|
34
|
+
|
35
|
+
def count
|
36
|
+
|
37
|
+
glob_list = Dir.glob( File.join( directory__index, '*' ) )
|
38
|
+
|
39
|
+
return glob_list.count
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
############
|
44
|
+
# delete #
|
45
|
+
############
|
46
|
+
|
47
|
+
def delete
|
48
|
+
|
49
|
+
directories = [ directory__index,
|
50
|
+
directory__reverse_index ]
|
51
|
+
|
52
|
+
# delete index data
|
53
|
+
directories.each do |this_directory|
|
54
|
+
|
55
|
+
# delete all indexed contents
|
56
|
+
Dir[ File.join( this_directory, '*' ) ].each do |this_file|
|
57
|
+
File.delete( this_file )
|
58
|
+
end
|
59
|
+
|
60
|
+
# delete index
|
61
|
+
Dir.delete( this_directory )
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
############
|
68
|
+
# cursor #
|
69
|
+
############
|
70
|
+
|
71
|
+
def cursor
|
72
|
+
|
73
|
+
return ::Persistence::Adapter::FlatFile::Cursor.new( @parent_bucket, self )
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
#########################
|
78
|
+
# permits_duplicates? #
|
79
|
+
#########################
|
80
|
+
|
81
|
+
def permits_duplicates?
|
82
|
+
|
83
|
+
return @permits_duplicates
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
###################
|
88
|
+
# get_object_id #
|
89
|
+
###################
|
90
|
+
|
91
|
+
def get_object_id( key )
|
92
|
+
|
93
|
+
file__id_for_key = file__index( key )
|
94
|
+
|
95
|
+
return open_read_unserialize_and_close( file__id_for_key )
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
#####################
|
100
|
+
# index_object_id #
|
101
|
+
#####################
|
102
|
+
|
103
|
+
def index_object_id( global_id, value )
|
104
|
+
|
105
|
+
file_path = file__index( value )
|
106
|
+
|
107
|
+
if permits_duplicates?
|
108
|
+
|
109
|
+
if File.exists?( file_path )
|
110
|
+
|
111
|
+
open_read_unserialize_perform_action_serialize_write_and_close( file_path ) do |ids|
|
112
|
+
ids.push( global_id ).sort.uniq
|
113
|
+
end
|
114
|
+
|
115
|
+
else
|
116
|
+
|
117
|
+
# index value => ID
|
118
|
+
create_or_update_value_serialize_and_write( file_path, [ global_id ] )
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
else
|
123
|
+
|
124
|
+
if File.exists?( file_path )
|
125
|
+
|
126
|
+
# check to make sure that we do not already have an index value with a different ID
|
127
|
+
open_read_unserialize_perform_action_serialize_write_and_close( file_path ) do |id|
|
128
|
+
|
129
|
+
# If we already have a file for this index value and it holds this object's id,
|
130
|
+
# then we don't need to do anything.
|
131
|
+
# Otherwise we have a duplicate key in a unique index, which is a problem.
|
132
|
+
unless id == global_id
|
133
|
+
raise ::Persistence::Exception::DuplicateViolatesUniqueIndex.new(
|
134
|
+
'Attempt to create entry in index named :' + @name.to_s +
|
135
|
+
' would create duplicates in a unique index.' )
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
else
|
141
|
+
|
142
|
+
# index value => ID
|
143
|
+
create_or_update_value_serialize_and_write( file_path, global_id )
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
# index ID => value for updating keys if they change
|
150
|
+
object_index_location = file__reverse_index_keys_for_global_id( @parent_bucket.name,
|
151
|
+
@name,
|
152
|
+
global_id )
|
153
|
+
create_or_update_value_serialize_and_write( object_index_location, value )
|
154
|
+
|
155
|
+
return self
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
################################
|
160
|
+
# delete_keys_for_object_id! #
|
161
|
+
################################
|
162
|
+
|
163
|
+
def delete_keys_for_object_id!( global_id )
|
164
|
+
|
165
|
+
key = open_read_unserialize_and_close( file__reverse_index_keys_for_global_id( @parent_bucket.name, @name, global_id ) )
|
166
|
+
|
167
|
+
files = [ file__reverse_index_keys_for_global_id( @parent_bucket.name, @name, global_id ),
|
168
|
+
file__index( key ) ]
|
169
|
+
|
170
|
+
files.each do |this_file|
|
171
|
+
File.delete( this_file )
|
172
|
+
end
|
173
|
+
|
174
|
+
return self
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
######################
|
179
|
+
# directory__index #
|
180
|
+
######################
|
181
|
+
|
182
|
+
def directory__index
|
183
|
+
|
184
|
+
directory__index = File.join( @parent_bucket.parent_adapter.home_directory,
|
185
|
+
'indexes',
|
186
|
+
@parent_bucket.name.to_s,
|
187
|
+
@name.to_s )
|
188
|
+
|
189
|
+
ensure_directory_path_exists( directory__index )
|
190
|
+
|
191
|
+
return directory__index
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
##############################
|
196
|
+
# directory__reverse_index #
|
197
|
+
##############################
|
198
|
+
|
199
|
+
# Bucket Index IDs: <home_directory>/global_ids/bucket/
|
200
|
+
def directory__reverse_index
|
201
|
+
|
202
|
+
directory__reverse_index = File.join( @parent_bucket.parent_adapter.home_directory,
|
203
|
+
'indexes',
|
204
|
+
'reverse_index',
|
205
|
+
@parent_bucket.name.to_s,
|
206
|
+
@name.to_s )
|
207
|
+
|
208
|
+
ensure_directory_path_exists( directory__reverse_index )
|
209
|
+
|
210
|
+
return directory__reverse_index
|
211
|
+
|
212
|
+
end
|
213
|
+
|
214
|
+
############################################
|
215
|
+
# file__reverse_index_keys_for_global_id #
|
216
|
+
############################################
|
217
|
+
|
218
|
+
# Global IDs: <home_directory>/global_ids/bucket/file__path_encoded_name_from_key.ruby_serialize.txt
|
219
|
+
def file__reverse_index_keys_for_global_id( bucket_name, index_name, global_id )
|
220
|
+
|
221
|
+
return File.join( directory__reverse_index,
|
222
|
+
global_id.to_s + adapter_class::SerializationExtension )
|
223
|
+
|
224
|
+
end
|
225
|
+
|
226
|
+
#################
|
227
|
+
# file__index #
|
228
|
+
#################
|
229
|
+
|
230
|
+
# Global ID: <home_directory>/indexes/bucket/file__path_encoded_name_from_key.ruby_serialize.txt
|
231
|
+
def file__index( key )
|
232
|
+
|
233
|
+
key = key.to_s if key.is_a?( Class )
|
234
|
+
|
235
|
+
return File.join( directory__index,
|
236
|
+
file__path_encoded_name_from_key( key ) + adapter_class::SerializationExtension )
|
237
|
+
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
|
2
|
+
module ::Persistence::Adapter::FlatFile::Cursor::CursorInterface
|
3
|
+
|
4
|
+
include ::Persistence::Adapter::FlatFile::PathHelpers
|
5
|
+
include ::Persistence::Adapter::FlatFile::Serialization
|
6
|
+
|
7
|
+
################
|
8
|
+
# initialize #
|
9
|
+
################
|
10
|
+
|
11
|
+
def initialize( parent_bucket_instance, parent_index_instance = nil )
|
12
|
+
|
13
|
+
@parent_bucket = parent_bucket_instance
|
14
|
+
@parent_index = parent_index_instance
|
15
|
+
|
16
|
+
# we're iterating a directory of files named after their key_digest and containing their global ID
|
17
|
+
@key_to_id_directory = ( @parent_index ? @parent_index.instance_eval { directory__index }
|
18
|
+
: @parent_bucket.instance_eval { directory__ids_in_bucket } )
|
19
|
+
|
20
|
+
# instantiate enumerator to track our current position
|
21
|
+
init_current_position
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
############################
|
26
|
+
# supports_bucket_order? #
|
27
|
+
############################
|
28
|
+
|
29
|
+
def supports_bucket_order?
|
30
|
+
|
31
|
+
return true
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
###########################
|
36
|
+
# supports_index_order? #
|
37
|
+
###########################
|
38
|
+
|
39
|
+
def supports_index_order?
|
40
|
+
|
41
|
+
return false
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
###########
|
46
|
+
# close #
|
47
|
+
###########
|
48
|
+
|
49
|
+
def close
|
50
|
+
|
51
|
+
# nothing required
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
###################
|
56
|
+
# adapter_class #
|
57
|
+
###################
|
58
|
+
|
59
|
+
def adapter_class
|
60
|
+
|
61
|
+
return @parent_bucket.parent_adapter.class
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
################
|
66
|
+
# persisted? #
|
67
|
+
################
|
68
|
+
|
69
|
+
# persisted? is responsible for setting the cursor position
|
70
|
+
def persisted?( *args )
|
71
|
+
|
72
|
+
has_key = false
|
73
|
+
no_key = false
|
74
|
+
case args.count
|
75
|
+
when 1
|
76
|
+
key = args[0]
|
77
|
+
when 0
|
78
|
+
no_key = true
|
79
|
+
end
|
80
|
+
|
81
|
+
# if we have no args we are asking whether any keys exist
|
82
|
+
if no_key
|
83
|
+
|
84
|
+
init_current_position
|
85
|
+
|
86
|
+
has_key = true unless ::Dir[ @key_to_id_directory ].empty?
|
87
|
+
|
88
|
+
else
|
89
|
+
|
90
|
+
# check if we have a position that currently points to key - if so we are done
|
91
|
+
if has_key = ( current_key == key )
|
92
|
+
return has_key
|
93
|
+
end
|
94
|
+
|
95
|
+
init_current_position
|
96
|
+
|
97
|
+
# Find location of key in Directory enumerator.
|
98
|
+
# It seems we can't use Dir#seek because there is no direct way to find the index from a file path.
|
99
|
+
# Please prove me wrong if you can!
|
100
|
+
begin
|
101
|
+
|
102
|
+
until has_key = ( current_key == key )
|
103
|
+
self.next
|
104
|
+
end
|
105
|
+
rescue StopIteration
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
return has_key
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
###########
|
115
|
+
# first #
|
116
|
+
###########
|
117
|
+
|
118
|
+
# first should set the cursor position and return the first ID or object hash
|
119
|
+
def first
|
120
|
+
|
121
|
+
@current_position.rewind
|
122
|
+
|
123
|
+
return current
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
#############
|
128
|
+
# current #
|
129
|
+
#############
|
130
|
+
|
131
|
+
# current should return the current ID or object hash
|
132
|
+
def current
|
133
|
+
|
134
|
+
current = nil
|
135
|
+
|
136
|
+
if @current_position
|
137
|
+
current = open_read_unserialize_and_close( File.join( @key_to_id_directory,
|
138
|
+
@current_position.peek ) )
|
139
|
+
end
|
140
|
+
|
141
|
+
return current
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
#################
|
146
|
+
# current_key #
|
147
|
+
#################
|
148
|
+
|
149
|
+
# current should return the current ID or object hash
|
150
|
+
def current_key
|
151
|
+
|
152
|
+
current_key = nil
|
153
|
+
|
154
|
+
if @parent_index
|
155
|
+
encoded_key_as_file_name = @current_position.peek
|
156
|
+
encoded_key = encoded_key_as_file_name.split( '.' )[ 0 ]
|
157
|
+
current_key = file__key_from_path_encoded_name( encoded_key )
|
158
|
+
else
|
159
|
+
current_key = current
|
160
|
+
end
|
161
|
+
|
162
|
+
return current_key
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
##########
|
167
|
+
# next #
|
168
|
+
##########
|
169
|
+
|
170
|
+
def next
|
171
|
+
|
172
|
+
@current_position.next
|
173
|
+
|
174
|
+
return current
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
##################################################################################################
|
179
|
+
private ######################################################################################
|
180
|
+
##################################################################################################
|
181
|
+
|
182
|
+
###########################
|
183
|
+
# init_current_position #
|
184
|
+
###########################
|
185
|
+
|
186
|
+
def init_current_position( directory = nil )
|
187
|
+
|
188
|
+
if directory
|
189
|
+
@key_to_id_directory = directory
|
190
|
+
end
|
191
|
+
|
192
|
+
if @current_position
|
193
|
+
|
194
|
+
@current_position.rewind
|
195
|
+
|
196
|
+
else
|
197
|
+
|
198
|
+
current_position_entries = Dir.new( @key_to_id_directory ).entries
|
199
|
+
current_position_entries.delete( '.' )
|
200
|
+
current_position_entries.delete( '..' )
|
201
|
+
@current_position = current_position_entries.each
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|