persistence-adapter-flat_file 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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