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.
@@ -0,0 +1,6 @@
1
+
2
+ class ::Persistence::Adapter::FlatFile::Bucket::Index
3
+
4
+ include ::Persistence::Adapter::FlatFile::Bucket::Index::IndexInterface
5
+
6
+ end
@@ -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,4 @@
1
+
2
+ class ::Persistence::Adapter::FlatFile::ClassName < ::String
3
+
4
+ end
@@ -0,0 +1,6 @@
1
+
2
+ class ::Persistence::Adapter::FlatFile::Cursor
3
+
4
+ include ::Persistence::Adapter::FlatFile::Cursor::CursorInterface
5
+
6
+ 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