flexirecord 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/CHANGELOG +15 -0
  2. data/lib/flexirecord-demo.rb +6 -6
  3. data/lib/flexirecord.rb +197 -113
  4. metadata +3 -2
data/CHANGELOG ADDED
@@ -0,0 +1,15 @@
1
+
2
+ CHANGELOG for FlexiRecord:
3
+
4
+ 2007-02-10:
5
+ - Changed the order of arguments passed to FlexiRecord::Reference#combine to match the documentation and renamed the method to FlexiRecord::Reference#connect.
6
+ - Minor bugfix in FlexiRecord::Relationship#new
7
+ - Added FlexiRecord::BaseRecord.add_connected_references as a more simple way to add many-to-many relations.
8
+
9
+ 2007-02-11:
10
+ - Renamed the word 'column' to 'attr' or 'attribute' at several places in the code and documentation.
11
+ - Freezed record arrays, which are fetched through attributes for many-to-one or many-to-many relations.
12
+ - Added a lock method, to lock tables in a nicier way.
13
+
14
+ Release of version 0.0.4.
15
+
@@ -70,9 +70,9 @@ module FlexiRecordDemo
70
70
  class MediumEntry < FlexiRecord::BaseRecord
71
71
  include FlexiRecord::ListRecord
72
72
  self.table_name = 'medium_entry'
73
- add_many_to_one_reference(Medium, 'medium_', :medium, :entries).combine(
74
- add_many_to_one_reference(Movie, 'movie_', :movie, :movie_entries),
75
- :media, :movies
73
+ add_connected_references(
74
+ Medium, 'medium_', :medium, :entries, :movies,
75
+ Movie, 'movie_', :movie, :movie_entries, :media
76
76
  )
77
77
  Medium.add_read_option :entries, :default, 'ORDER BY "position"'
78
78
  Medium.add_read_option :movies, :default, 'ORDER BY "rel"."position"'
@@ -83,9 +83,9 @@ module FlexiRecordDemo
83
83
  # CREATE TABLE "rating" ("person_id" int8 not null references "person" ("id") on delete cascade on update cascade, "movie_id" int8 not null references "movie" ("id") on delete cascade on update cascade, "rating" numeric, "comment" text, PRIMARY KEY ("person_id", "movie_id") );
84
84
  class Rating < FlexiRecord::Relationship
85
85
  self.table_name = 'rating'
86
- add_many_to_one_reference(Person, 'person_', :person, :ratings).combine(
87
- add_many_to_one_reference(Movie, ['movie_id', 'id'], :movie, :ratings),
88
- :rated_by, :rated_movies
86
+ add_connected_references(
87
+ Person, 'person_', :person, :ratings, :rated_movies,
88
+ Movie, ['movie_id', 'id'], :movie, :ratings, :rated_by
89
89
  )
90
90
  end
91
91
 
data/lib/flexirecord.rb CHANGED
@@ -104,23 +104,81 @@ module FlexiRecord
104
104
  self.to_i <=> other.to_i
105
105
  end
106
106
 
107
- ReadUncommitted = new(0, :read_uncommitted, "READ UNCOMMITTED", "ReadUncommitted")
108
- ReadCommitted = new(1, :read_committed, "READ COMMITTED", "ReadCommitted")
109
- RepeatableRead = new(2, :repeatable_read, "REPEATABLE READ", "RepeatableRead")
110
- Serializable = new(3, :serializable, "SERIALIZABLE", "Serializable")
107
+ ReadUncommitted = new(0, :read_uncommitted, 'READ UNCOMMITTED', 'ReadUncommitted')
108
+ ReadCommitted = new(1, :read_committed, 'READ COMMITTED', 'ReadCommitted')
109
+ RepeatableRead = new(2, :repeatable_read, 'REPEATABLE READ', 'RepeatableRead')
110
+ Serializable = new(3, :serializable, 'SERIALIZABLE', 'Serializable')
111
111
 
112
112
  end # end of class IsolationLevel
113
113
 
114
114
 
115
- # Objects of this class are used to describe a reference between two tables. You can create and register them by calling BaseRecord::add_many_to_one_reference or RaseRecord::add_one_to_one_reference.
115
+ # Table lock modes are represented by (constant) LockMode objects:
116
+ # - LockMode::AccessShare
117
+ # (Acquired by any read operation.)
118
+ # - LockMode::RowShare
119
+ # (Acquired for any row-locking operation.)
120
+ # - LockMode::RowExclusive
121
+ # (Acquired when data in a table is modified.)
122
+ # - LockMode::ShareUpdateExclusive
123
+ # - LockMode::Share
124
+ # (Prohibits data changes in the table.)
125
+ # - LockMode::ShareRowExclusive
126
+ # (Prohibits data changes and other Share or ShareRowExclusive locks. Used instead of LockMode::Share to prohibit dead locks.)
127
+ # - LockMode::Exclusive
128
+ # (Prohibits anything else than reading from the table.)
129
+ # - LockMode::AccessExclusive
130
+ # (Prohibits any other access to the table.)
131
+
132
+ class LockMode
133
+
134
+ private_class_method :new
135
+ @@symbols = {}
136
+
137
+ # Returns a LockMode object, matching the given symbol.
138
+ def self.by_symbol(symbol)
139
+ @@symbols[symbol.to_sym]
140
+ end
141
+
142
+ # Used for generating the constants representing the possible table lock modes.
143
+ def initialize(symbol, sql, name)
144
+ @symbol = symbol.to_sym
145
+ @sql = sql.to_s.dup.freeze
146
+ @name = name.to_s.dup.freeze
147
+ @@symbols[@symbol] = self
148
+ nil
149
+ end
150
+
151
+ # Returns the SQL string representation of the lock mode.
152
+ def to_s
153
+ @sql
154
+ end
155
+
156
+ # Returns the name of the constant referring to the lock mode.
157
+ def inspect
158
+ "#{self.class.name}::#{@name}"
159
+ end
160
+
161
+ AccessShare = new(:access_share, 'ACCESS SHARE MODE', 'AccessShare')
162
+ RowShare = new(:row_share, 'ROW SHARE MODE', 'RowShare')
163
+ RowExclusive = new(:row_exclusive, 'ROW EXCLUSIVE MODE', 'RowExclusive')
164
+ ShareUpdateExclusive = new(:share_update_exclusive, 'SHARE UPDATE EXCLUSIVE MODE', 'ShareUpdateExclusive')
165
+ Share = new(:share, 'SHARE MODE', 'Share')
166
+ ShareRowExclusive = new(:share_row_exclusive, 'SHARE ROW EXCLUSIVE MODE', 'ShareRowExclusive')
167
+ Exclusive = new(:exclusive, 'EXCLUSIVE MODE', 'Exclusive')
168
+ AccessExclusive = new(:access_exclusive, 'ACCESS EXCLUSIVE MODE', 'AccessExclusive')
169
+
170
+ end # end of class LockMode
171
+
172
+
173
+ # Objects of this class are used to describe a reference between two tables. You can create and register them by calling FlexiRecord::BaseRecord.add_many_to_one_reference or FlexiRecord::RaseRecord.add_one_to_one_reference. For using many-to-many relationships you have to extend the class FlexiRecord::Relationship and create two connected ManyToOneReference's for that class by calling FlexiRecord::BaseRecord.add_connected_references.
116
174
 
117
175
  class Reference
118
176
 
119
177
  private_class_method :new
120
178
 
121
- # Returns a new reference object, describing a relation where objects of the 'source_class' refer to objects of the 'destination_class'. The 'column_info' field describes the columns used for that reference. If the 'column_info' field is a string, the primary key is used in the destination class, and the primary key prefixed by the string given in 'column_info' is used as the foreign key in the source class. If 'column_info' is an array, it contains the columns in the source class, followed by the columsn in the destination class. The field 'src_to_dst_column' contains the name of the column in the source class, which is referring to one object of the destination class. The field 'dst_to_src_column' contains the name of the column in the destination class, which is referring to one or many objects of the source class. After the reference has been created reader, loader and setter functions are added to the 'source_class' and 'destination_class', to provide access to referenced and referring objects.
179
+ # Returns a new reference object, describing a relation where objects of the 'source_class' refer to objects of the 'destination_class'. The 'column_info' field describes the columns used for that reference. If the 'column_info' field is a string, the primary key is used in the destination class, and the primary key prefixed by the string given in 'column_info' is used as the foreign key in the source class. If 'column_info' is an array, it contains the columns in the source class, followed by the columns in the destination class. The field 'src_to_dst_attr' contains the name of the attribute in the source class, which is referring to one object of the destination class. The field 'dst_to_src_attr' contains the name of the attribute in the destination class, which is referring to one or many objects of the source class. After the reference has been created, reader, loader and setter functions are added to the 'source_class' and 'destination_class', to provide access to referenced and referring objects.
122
180
  # This method is private, use one of the child classes OneToOneReference or ManyToOneReference, which get the same arguments, to generate references of a given type.
123
- def initialize(source_class, destination_class, column_info, src_to_dst_column, dst_to_src_column)
181
+ def initialize(source_class, destination_class, column_info, src_to_dst_attr, dst_to_src_attr)
124
182
  unless source_class.kind_of? Class and destination_class.kind_of? Class
125
183
  raise TypeError, "Class expected"
126
184
  end
@@ -146,10 +204,10 @@ module FlexiRecord
146
204
  end
147
205
  @source_columns.freeze
148
206
  @destination_columns.freeze
149
- @src_to_dst_column = src_to_dst_column.to_s.dup.freeze
150
- @dst_to_src_column = dst_to_src_column.to_s.dup.freeze
207
+ @src_to_dst_attr = src_to_dst_attr.to_s.dup.freeze
208
+ @dst_to_src_attr = dst_to_src_attr.to_s.dup.freeze
151
209
  # Work at the source class:
152
- @source_class.set_loader(@src_to_dst_column) do |source_records, arguments|
210
+ @source_class.set_loader(@src_to_dst_attr) do |source_records, arguments|
153
211
  unless arguments.empty?
154
212
  raise ArgumentError, "No extra arguments may be specified for outgoing reference columns."
155
213
  end
@@ -164,15 +222,15 @@ module FlexiRecord
164
222
  destination_records.each do |destination_record|
165
223
  destination_record_hash[@destination_columns.collect { |column| destination_record[column] }] = destination_record
166
224
  if self.one_to_one?
167
- destination_record[@dst_to_src_column] = nil
225
+ destination_record[@dst_to_src_attr] = nil
168
226
  end
169
227
  end
170
228
  source_records.each do |source_record|
171
229
  destination_record =
172
230
  destination_record_hash[@source_columns.collect { |column| source_record[column] }]
173
- source_record[@src_to_dst_column, *arguments] = destination_record
231
+ source_record[@src_to_dst_attr, *arguments] = destination_record
174
232
  if destination_record and self.one_to_one?
175
- destination_record[@dst_to_src_column] = source_record
233
+ destination_record[@dst_to_src_attr] = source_record
176
234
  end
177
235
  end
178
236
  next destination_records
@@ -181,28 +239,28 @@ module FlexiRecord
181
239
  source_column = @source_columns[column_index]
182
240
  destination_column = @destination_columns[column_index]
183
241
  @source_class.set_reader(source_column) do |source_record, arguments|
184
- destination_record = source_record[@src_to_dst_column]
242
+ destination_record = source_record[@src_to_dst_attr]
185
243
  next destination_record ? destination_record[destination_column] : source_record[source_column]
186
244
  end
187
245
  @source_class.set_setter(source_column) do |source_record, value|
188
- source_record.delete_from_cache(@src_to_dst_column)
246
+ source_record.delete_from_cache(@src_to_dst_attr)
189
247
  source_record[source_column] = value
190
248
  end
191
249
  end
192
250
  if self.one_to_one?
193
- @source_class.set_setter(@src_to_dst_column) do |source_record, value|
194
- old_destination_record = source_record[@src_to_dst_column]
251
+ @source_class.set_setter(@src_to_dst_attr) do |source_record, value|
252
+ old_destination_record = source_record[@src_to_dst_attr]
195
253
  if old_destination_record
196
- old_destination_record[@dst_to_src_column] = nil
254
+ old_destination_record[@dst_to_src_attr] = nil
197
255
  end
198
- source_record[@src_to_dst_column] = value
256
+ source_record[@src_to_dst_attr] = value
199
257
  if value
200
- value[@dst_to_src_column] = source_record
258
+ value[@dst_to_src_attr] = source_record
201
259
  end
202
260
  end
203
261
  end
204
262
  # Work at the destination class:
205
- @destination_class.set_loader(@dst_to_src_column) do |destination_records, arguments|
263
+ @destination_class.set_loader(@dst_to_src_attr) do |destination_records, arguments|
206
264
  unless arguments.empty?
207
265
  unless arguments[0].respond_to? :to_str
208
266
  raise "First argument of reader method is not a SQL snippet string."
@@ -219,7 +277,7 @@ module FlexiRecord
219
277
  destination_record_hash = {}
220
278
  destination_records.each do |destination_record|
221
279
  destination_record_hash[@destination_columns.collect { |column| destination_record[column] }] = destination_record
222
- destination_record[@dst_to_src_column, *arguments] =
280
+ destination_record[@dst_to_src_attr, *arguments] =
223
281
  if self.many_to_one?
224
282
  FlexiRecord::RecordArray.new(@source_class)
225
283
  else
@@ -228,49 +286,54 @@ module FlexiRecord
228
286
  end
229
287
  source_records.each do |source_record|
230
288
  destination_record = destination_record_hash[@source_columns.collect { |column| source_record[column] }]
231
- source_record[@src_to_dst_column] = destination_record
289
+ source_record[@src_to_dst_attr] = destination_record
232
290
  if destination_record
233
291
  if self.many_to_one?
234
- destination_record[@dst_to_src_column, *arguments] << source_record
292
+ destination_record[@dst_to_src_attr, *arguments] << source_record
235
293
  else
236
- destination_record[@dst_to_src_column, *arguments] = source_record
294
+ destination_record[@dst_to_src_attr, *arguments] = source_record
237
295
  end
238
296
  end
239
297
  end
298
+ if self.many_to_one?
299
+ destination_records.each do |destination_record|
300
+ destination_record[@dst_to_src_attr, *arguments].freeze
301
+ end
302
+ end
240
303
  next source_records
241
304
  end
242
305
  if self.one_to_one?
243
- @destination_class.set_setter(@dst_to_src_column) do |destination_record, value|
244
- old_source_record = destination_record[@dst_to_src_column]
306
+ @destination_class.set_setter(@dst_to_src_attr) do |destination_record, value|
307
+ old_source_record = destination_record[@dst_to_src_attr]
245
308
  if old_source_record
246
- old_source_record[@src_to_dst_column] = nil
309
+ old_source_record[@src_to_dst_attr] = nil
247
310
  end
248
- destination_record[@dst_to_src_column] = value
311
+ destination_record[@dst_to_src_attr] = value
249
312
  if value
250
- value[@src_to_dst_column] = destination_record
313
+ value[@src_to_dst_attr] = destination_record
251
314
  end
252
315
  end
253
316
  end
254
317
  return self
255
318
  end
256
319
 
257
- # Combines two ManyToOneReference's to a many-to-many relation. 'other_reference' is another Reference object (as returned by Reference#new, BaseRecord#add_many_to_one_reference or BaseRecord#add_one_to_one_reference), 'own_column' is the virtual column to be installed in the destination_class of this object and 'other_column' is the virtual column to be installed in the destination_class of the 'other_reference' object.
258
- def combine(other_reference, own_column, other_column)
320
+ # Connects two ManyToOneReference's to form a many-to-many relation. 'other_reference' is another Reference object (as returned by FlexiRecord::Reference.new, 'own_attr' is the attribute to be installed in the destination_class of this Reference object for accessing objects of the destination_class of the 'other_reference' object, and 'other_attr' is the attribute to be installed in the destination_class of the 'other_reference' object for accessing objects of the destination_class of this Reference object.
321
+ def connect(other_reference, own_attr, other_attr)
259
322
  unless other_reference.kind_of? FlexiRecord::Reference
260
323
  raise TypeError, "Object of class FlexiRecord::Reference expected."
261
324
  end
262
325
  reference1 = self
263
326
  reference2 = other_reference
264
- column1 = own_column.to_s
265
- column2 = other_column.to_s
327
+ attr1 = own_attr.to_s
328
+ attr2 = other_attr.to_s
266
329
  unless self.source_class == other_reference.source_class
267
330
  "Combining references having different source classes is not possible."
268
331
  end
269
332
  relationship_class = self.source_class
270
333
  [
271
- [reference1, reference2, column1, column2],
272
- [reference2, reference1, column2, column1]
273
- ].each do |source_reference, destination_reference, source_column, destination_column|
334
+ [reference1, reference2, attr1, attr2],
335
+ [reference2, reference1, attr2, attr1]
336
+ ].each do |source_reference, destination_reference, source_attr, destination_attr|
274
337
  source_class = source_reference.destination_class
275
338
  destination_class = destination_reference.destination_class
276
339
  tmp1 = []
@@ -280,7 +343,7 @@ module FlexiRecord
280
343
  destination_reference.destination_columns[column_index]
281
344
  ]
282
345
  end
283
- source_class.set_loader destination_column do |source_records, arguments|
346
+ source_class.set_loader source_attr do |source_records, arguments|
284
347
  sql_arguments = arguments.dup
285
348
  sql_snippet = sql_arguments.shift
286
349
  unless sql_snippet.nil?
@@ -307,7 +370,7 @@ module FlexiRecord
307
370
  destination_record['_flexirecord_rel_' << column]
308
371
  }
309
372
  ] ||= FlexiRecord::RecordArray.new(destination_class)) << destination_record
310
- relationship_hash = { destination_reference.src_to_dst_column => destination_record }
373
+ relationship_hash = { destination_reference.src_to_dst_attr => destination_record }
311
374
  relationship_class.columns.each do |column|
312
375
  relationship_hash[column] =
313
376
  destination_record.delete_from_cache("_flexirecord_rel_#{column}")
@@ -315,13 +378,18 @@ module FlexiRecord
315
378
  destination_record[FlexiRecord::RelationshipColumn] = relationship_class.new(relationship_hash)
316
379
  end
317
380
  source_records.each do |source_record|
318
- source_record[destination_column, *arguments] = (destination_record_hash[source_reference.destination_columns.collect { |column| source_record[column] } ]) || FlexiRecord::RecordArray.new(destination_class)
381
+ source_record[source_attr, *arguments] = ((destination_record_hash[source_reference.destination_columns.collect { |column| source_record[column] } ]) || FlexiRecord::RecordArray.new(destination_class)).freeze
319
382
  end
320
383
  next destination_records
321
384
  end
322
385
  end
323
386
  end
324
387
 
388
+ # Deprecated. Use FlexiRecord::Reference#connect instead.
389
+ def combine(*arguments)
390
+ self.connect(*arguments)
391
+ end
392
+
325
393
  # Class, whose objects are referring to others.
326
394
  attr_reader :source_class
327
395
 
@@ -334,11 +402,11 @@ module FlexiRecord
334
402
  # Columns in the referred class, providing a unique or primary key.
335
403
  attr_reader :destination_columns
336
404
 
337
- # Name (String) of the column in the source class, which is referring to one object of the destination class.
338
- attr_reader :src_to_dst_column
405
+ # Name (String) of the attribute in the source class, which is referring to one object of the destination class.
406
+ attr_reader :src_to_dst_attr
339
407
 
340
- # Name (String) of the column in the destination class, which is referring to one or many objects of the source class.
341
- attr_reader :dst_to_src_column
408
+ # Name (String) of the attribute in the destination class, which is referring to one or many objects of the source class.
409
+ attr_reader :dst_to_src_attr
342
410
 
343
411
  # Returns true, if the object describes a one-to-one relation.
344
412
  def one_to_one?
@@ -383,7 +451,7 @@ module FlexiRecord
383
451
  end # end of class ManyToOneReference
384
452
 
385
453
 
386
- # An abstract record, super-class of all record classes. By now there is only one sub-class 'BaseRecord', which represents a record being directly stored in one database table. Other sub-classes might be added in near future, to be able to use inheritance of data models to be stored in the database using multiple tables in the backend for one model.
454
+ # An abstract record, super-class of all record classes. By now there is only one sub-class 'BaseRecord', which represents a record being directly stored in one database table. Other sub-classes might be added in future, to be able to use inheritance of data models.
387
455
 
388
456
  class AbstractRecord
389
457
 
@@ -408,18 +476,18 @@ module FlexiRecord
408
476
  @flexirecord_class
409
477
  end
410
478
 
411
- # Preloads a virtualized column of an Array of records at once (without doing one SQL query for each record). Can return another RecordArray, which can be used for the next level of preloading.
412
- def preload(column, *arguments)
413
- column = column.to_s
414
- @flexirecord_class.prepare_read_parameters(column, arguments)
415
- cache_key = [column] + arguments
479
+ # Preloads the attribute named 'attr' in each record of the RecordArray, without doing one SQL query for each record. Can return another RecordArray, which can be used for the next level of preloading.
480
+ def preload(attr, *arguments)
481
+ attr = attr.to_s
482
+ @flexirecord_class.prepare_read_parameters(attr, arguments)
483
+ cache_key = [attr] + arguments
416
484
  if @flexirecord_preloaded.has_key?(cache_key)
417
485
  return @flexirecord_preloaded[cache_key]
418
486
  end
419
- column = column.to_s
420
- loader = self.record_class.loader(column)
487
+ attr = attr.to_s
488
+ loader = self.record_class.loader(attr)
421
489
  unless loader
422
- raise ArgumentError, "Could not preload column '#{column}', due to missing loading procedure."
490
+ raise ArgumentError, "Could not preload attribute '#{attr}', due to missing loading procedure."
423
491
  end
424
492
  return @flexirecord_preloaded[cache_key] = loader.call(self, arguments)
425
493
  end
@@ -464,7 +532,7 @@ module FlexiRecord
464
532
  )
465
533
  end
466
534
 
467
- # Returns the database schema name, even if no schema is set (in this case "public" is returned.
535
+ # Returns the database schema name, even if no schema is set (in this case "public" is returned).
468
536
  def schema_name!
469
537
  schema_name or "public".freeze
470
538
  end
@@ -562,6 +630,17 @@ module FlexiRecord
562
630
  result
563
631
  end
564
632
 
633
+ # Locks the table used to store objects of this class. Calling this command only makes sense, if a transaction is in progress.
634
+ def lock(mode)
635
+ if not mode.kind_of? FlexiRecord::LockMode and mode.respond_to? :to_sym
636
+ mode = FlexiRecord::LockMode.by_symbol(mode)
637
+ end
638
+ if mode.nil?
639
+ raise "Table lock mode expected"
640
+ end
641
+ db_execute("LOCK #{table} IN #{mode}")
642
+ end
643
+
565
644
  # Autodetects the columns (and primary key columns) of the underlaying table, to be later retrieved by the 'columns' and the 'primary_columns' methods.
566
645
  def autodetect_columns
567
646
  columns = []
@@ -682,22 +761,22 @@ module FlexiRecord
682
761
  # Adds a "shortcut" for a parameter to reader and loader functions.
683
762
  #
684
763
  # Example: List.add_read_option :items, :by_name, 'ORDER BY "name"'
685
- def add_read_option(column, symbol, value)
764
+ def add_read_option(attr, symbol, value)
686
765
  unless symbol.kind_of? Symbol
687
766
  raise "Symbol expected as second argument to 'add_read_option'."
688
767
  end
689
- (@read_options ||= {})[[column.to_s, symbol]] = value
768
+ (@read_options ||= {})[[attr.to_s, symbol]] = value
690
769
  end
691
770
 
692
771
  # Returns the value of a "shortcut" for a parameter to reader and loader functions.
693
- def read_option_value(column, symbol)
694
- value = (@read_options || {})[[column.to_s, symbol]] || ((superclass <= FlexiRecord::BaseRecord) ? superclass.read_option_value(column, symbol) : nil)
772
+ def read_option_value(attr, symbol)
773
+ value = (@read_options || {})[[attr.to_s, symbol]] || ((superclass <= FlexiRecord::BaseRecord) ? superclass.read_option_value(attr, symbol) : nil)
695
774
  end
696
775
 
697
776
  # Modifies a parameter array by replacing "shortcut" symbols being defined by add_read_option. Returns the parameter array.
698
- def prepare_read_parameters(column, parameters)
777
+ def prepare_read_parameters(attr, parameters)
699
778
  option = parameters.first
700
- value = read_option_value(column, option.nil? ? :default : option)
779
+ value = read_option_value(attr, option.nil? ? :default : option)
701
780
  if value
702
781
  parameters[0] = value
703
782
  elsif option == :default
@@ -706,43 +785,43 @@ module FlexiRecord
706
785
  return parameters
707
786
  end
708
787
 
709
- # Sets a given block to be the "reader function" for a particlular virtualized column. A reader function is invoked, when you try to read a value of the given 'column' of a record. Two arguments are passed to the block. The first is the record, whose data is to be read, the second is an Array of arguments passed to the method used for reading the value. The block has to evaluate to the value which should be read.
710
- def set_reader(column, &reader)
711
- (@readers ||= {})[column.to_s] = reader
788
+ # Sets a given block to be the "reader function" for a particlular attribute. A reader function is invoked, when you try to read a value of the given attribute of a record. Two arguments are passed to the block. The first is the record, whose data is to be read, the second is an Array of arguments passed to the method used for reading the value. The block has to evaluate to the value which should be read.
789
+ def set_reader(attr, &reader)
790
+ (@readers ||= {})[attr.to_s] = reader
712
791
  end
713
792
 
714
- # Returns the reader function (a Proc object) for a certain virtualized column.
715
- def reader(column)
716
- (@readers || {})[column.to_s] or ((superclass <= FlexiRecord::BaseRecord) ? superclass.reader(column) : nil)
793
+ # Returns the reader function (a Proc object) for a certain attribute.
794
+ def reader(attr)
795
+ (@readers || {})[attr.to_s] or ((superclass <= FlexiRecord::BaseRecord) ? superclass.reader(attr) : nil)
717
796
  end
718
797
 
719
- # Returns an array containing all virtualized columns having a reader Proc stored in the class.
720
- def reader_columns
798
+ # Returns an array containing all attributes having a reader Proc stored in the class.
799
+ def reader_attrs
721
800
  (@readers || {}).keys + (
722
801
  (superclass <= FlexiRecord::BaseRecord) ?
723
- superclass.reader_columns :
802
+ superclass.reader_attrs :
724
803
  []
725
804
  )
726
805
  end
727
806
 
728
- # Sets a given block to be the "loader function" for a particular virtualized column. Loader functions are invoked, when you try to read an uncached value of the given 'column' of a record, or when you preload data for a whole Array of records. Two arguments are passed to the block. The first is an Array of records, whose data is to be loaded, the second is an Array of arguments passed to the method used for accessing the value. The block has to evaluate to an Array of records, which can be used for more than one level deep preloads.
729
- def set_loader(column, &loader)
730
- (@loaders ||= {})[column.to_s] = loader
807
+ # Sets a given block to be the "loader function" for a particular attribute. Loader functions are invoked, when you try to read an uncached value of the given attribute of a record, or when you preload data for a whole Array of records. Two arguments are passed to the block. The first is an Array of records, whose data is to be loaded, the second is an Array of arguments passed to the method used for accessing the value. The block has to evaluate to an Array of records, which can be used for more than one level deep preloads.
808
+ def set_loader(attr, &loader)
809
+ (@loaders ||= {})[attr.to_s] = loader
731
810
  end
732
811
 
733
- # Returns the loader function (a Proc object) for a certain virtualized column.
734
- def loader(column)
735
- (@loaders || {})[column.to_s] or ((superclass <= FlexiRecord::BaseRecord) ? superclass.loader(column) : nil)
812
+ # Returns the loader function (a Proc object) for a certain attribute.
813
+ def loader(attr)
814
+ (@loaders || {})[attr.to_s] or ((superclass <= FlexiRecord::BaseRecord) ? superclass.loader(attr) : nil)
736
815
  end
737
816
 
738
- # Sets a given block to be the "setter function" for a particular virtualized column. The setter function is invoked, when you set the value of the given 'column' of a record. Two arguments are passed to the block. The first is the record, whose data is to be changed, the second is the new value to be written into the 'column' field.
739
- def set_setter(column, &setter)
740
- (@setters ||= {})[column.to_s] = setter
817
+ # Sets a given block to be the "setter function" for a particular attribute. The setter function is invoked, when you set the value of the given attribute of a record. Two arguments are passed to the block. The first is the record, whose data is to be changed, the second is the new value to be written into the field.
818
+ def set_setter(attr, &setter)
819
+ (@setters ||= {})[attr.to_s] = setter
741
820
  end
742
821
 
743
- # Returns the setter function (a Proc object) for a certain virtualized column.
744
- def setter(column)
745
- (@setters || {})[column.to_s] or ((superclass <= FlexiRecord::BaseRecord) ? superclass.setter(column) : nil)
822
+ # Returns the setter function (a Proc object) for a certain attribute.
823
+ def setter(attr)
824
+ (@setters || {})[attr.to_s] or ((superclass <= FlexiRecord::BaseRecord) ? superclass.setter(attr) : nil)
746
825
  end
747
826
 
748
827
  # Adds an OneToOneReference to the class (by simply creating it). The first argument is the destination class, followed by arguments being passed to Reference.new.
@@ -759,6 +838,11 @@ module FlexiRecord
759
838
  )
760
839
  end
761
840
 
841
+ # Adds two ManyToOneReferences and connects them to form a many-to-many relation between two other record classes. Please look at the FlexiRecordDemo module to see how this works.
842
+ def add_connected_references(destination_class1, column_info1, src_to_dst_attr1, dst_to_src_attr1, class1_to_class2_attr, destination_class2, column_info2, src_to_dst_attr2, dst_to_src_attr2, class2_to_class1_attr)
843
+ self.add_many_to_one_reference(destination_class1, column_info1, src_to_dst_attr1, dst_to_src_attr1).combine(self.add_many_to_one_reference(destination_class2, column_info2, src_to_dst_attr2, dst_to_src_attr2), class1_to_class2_attr, class2_to_class1_attr)
844
+ end
845
+
762
846
  end # end of singleton meta class of BaseRecord
763
847
 
764
848
  private # methods of BaseRecord
@@ -801,7 +885,7 @@ module FlexiRecord
801
885
 
802
886
  public # methods of BaseRecord
803
887
 
804
- # Rewrites several attributes given by the keys and values in the 'data' hash. Returns self.
888
+ # Rewrites several attributes given by the keys and values in the 'data' hash. Note: A SQL UPDATE command is not executed before 'save' is called, this method just updates the ruby object. Returns self.
805
889
  def update(data)
806
890
  synchronize do
807
891
  data.each { |key, value| self.set(key, value) }
@@ -827,14 +911,14 @@ module FlexiRecord
827
911
  end
828
912
  end
829
913
 
830
- # Reads a value (whose key can consist of multiple fields) from the internal cache. The first argument is by convention a name of a column as String(!), but NOT as a Symbol.
914
+ # Reads a value (whose key can consist of multiple fields) from the internal cache. The first argument is by convention usually a name of a column as String(!), but NOT as a Symbol.
831
915
  def [](*key)
832
916
  synchronize do
833
917
  return @data_hash[key]
834
918
  end
835
919
  end
836
920
 
837
- # Writes a value to the internal cache. The first argument is by convention a name of a column as String(!), but NOT as a Symbol.
921
+ # Writes a value to the internal cache. The first argument is by convention usually a name of a column as String(!), but NOT as a Symbol.
838
922
  def []=(*arguments)
839
923
  synchronize do
840
924
  value = arguments.pop
@@ -857,13 +941,13 @@ module FlexiRecord
857
941
  end
858
942
  end
859
943
 
860
- # Reads the field with the specified 'column'. If there is a dynamic reader, this reader is used, otherwise an existent assigned loader function is invoked or the internal cache will give a result. If there is no data for the column with the given name, then nil will be returned.
861
- def read(column, *arguments)
862
- column = column.to_s
863
- self.class.prepare_read_parameters(column, arguments)
864
- data_hash_key = [column] + arguments
865
- reader = self.class.reader(column)
866
- loader = self.class.loader(column)
944
+ # Reads an attribute. If there is a dynamic reader, this reader is used, otherwise an existent assigned loader function is invoked or the internal cache will give a result. If there is no data for the attribute with the given name, then nil will be returned.
945
+ def read(attr, *arguments)
946
+ attr = attr.to_s
947
+ self.class.prepare_read_parameters(attr, arguments)
948
+ data_hash_key = [attr] + arguments
949
+ reader = self.class.reader(attr)
950
+ loader = self.class.loader(attr)
867
951
  synchronize do
868
952
  if reader
869
953
  return reader.call(self, arguments)
@@ -879,22 +963,22 @@ module FlexiRecord
879
963
  end
880
964
  end
881
965
 
882
- # Sets a value of a field of the specified 'column'. If there is a dynamic setter, this setter is used, otherwise the value get's written in the internal cache.
883
- def set(column, value)
884
- column = column.to_s
885
- setter = self.class.setter(column)
966
+ # Sets an attribute. If there is a dynamic setter, this setter is used, otherwise the value get's written in the internal cache.
967
+ def set(attr, value)
968
+ attr = attr.to_s
969
+ setter = self.class.setter(attr)
886
970
  if setter
887
971
  setter.call(self, value)
888
- return @data_hash[[column]]
972
+ return @data_hash[[attr]]
889
973
  else
890
- return @data_hash[[column]] = value
974
+ return @data_hash[[attr]] = value
891
975
  end
892
976
  end
893
977
 
894
978
  # Returns an array of strings of the columns in the backend database, which have either values in the internal cache, or which have a dynamic reader function.
895
979
  def used_columns
896
980
  synchronize do
897
- return self.class.columns & (self.class.reader_columns + @data_hash.keys.reject { |key| key.length > 1 }.collect { |key| key.first })
981
+ return self.class.columns & (self.class.reader_attrs + @data_hash.keys.reject { |key| key.length > 1 }.collect { |key| key.first })
898
982
  end
899
983
  end
900
984
 
@@ -928,20 +1012,20 @@ module FlexiRecord
928
1012
  # Provides easy access to the fields of the record.
929
1013
  def method_missing(method_symbol, *arguments)
930
1014
  synchronize do
931
- column = method_symbol.to_s
932
- if column[-1, 1] == '='
933
- column = column[0, column.length-1]
1015
+ attr = method_symbol.to_s
1016
+ if attr[-1, 1] == '='
1017
+ attr = attr[0, attr.length-1]
934
1018
  mode = :write
935
1019
  value = arguments.pop
936
1020
  else
937
1021
  mode = :read
938
1022
  end
939
- reader = self.class.reader(column)
940
- loader = self.class.loader(column)
941
- table_column_existent = self.class.columns.include?(column)
1023
+ reader = self.class.reader(attr)
1024
+ loader = self.class.loader(attr)
1025
+ table_column_existent = self.class.columns.include?(attr)
942
1026
  if mode == :write
943
- data_hash_key = [column] + arguments
944
- setter = self.class.setter(column)
1027
+ data_hash_key = [attr] + arguments
1028
+ setter = self.class.setter(attr)
945
1029
  if setter
946
1030
  setter.call(self, value)
947
1031
  return nil
@@ -950,8 +1034,8 @@ module FlexiRecord
950
1034
  return nil
951
1035
  end
952
1036
  elsif mode == :read
953
- self.class.prepare_read_parameters(column, arguments)
954
- data_hash_key = [column] + arguments
1037
+ self.class.prepare_read_parameters(attr, arguments)
1038
+ data_hash_key = [attr] + arguments
955
1039
  if reader
956
1040
  return reader.call(self, arguments)
957
1041
  elsif @data_hash.has_key?(data_hash_key)
@@ -1079,7 +1163,7 @@ module FlexiRecord
1079
1163
 
1080
1164
  def initialize(*arguments)
1081
1165
  super
1082
- self['void'] = nil
1166
+ self['void'] = nil unless self.has_key?('void')
1083
1167
  end
1084
1168
 
1085
1169
  # Alias for the (field) method 'void'. True, if the Relationship is void, and is to be removed, when calling 'save'.
@@ -1091,7 +1175,7 @@ module FlexiRecord
1091
1175
  def save
1092
1176
  # TODO: improve efficiency, when a "REPLACE" command is available in PostgreSQL
1093
1177
  self.class.transaction(self, :read_committed) do
1094
- self.class.db_execute "LOCK TABLE #{self.class.table} IN SHARE ROW EXCLUSIVE MODE"
1178
+ self.class.lock(:share_row_exclusive)
1095
1179
  @saved =
1096
1180
  self.class.select_by_value_set(self.class.primary_columns, [self.class.primary_columns.collect { |column| self.read(column) }]).length > 0
1097
1181
  copy_primary_key
@@ -1187,7 +1271,7 @@ module FlexiRecord
1187
1271
  }
1188
1272
  raise ArgumentError, "Too many arguments supplied for SQL command." unless command_arguments.empty?
1189
1273
  if $flexirecord_debug_output
1190
- $flexirecord_debug_output << "===> #{command}\n"
1274
+ $flexirecord_debug_output << "#{command}\n"
1191
1275
  end
1192
1276
  begin
1193
1277
  synchronize do
@@ -1399,8 +1483,8 @@ module FlexiRecord
1399
1483
 
1400
1484
  def save
1401
1485
  self.class.transaction(self, :read_committed) do
1402
- self.class.db_execute "LOCK TABLE #{self.class.table} IN SHARE ROW EXCLUSIVE MODE"
1403
- if self.position == :first
1486
+ self.class.lock(:share_row_exclusive)
1487
+ if self.position == :first
1404
1488
  db_result = self.class.db_query1(
1405
1489
  'SELECT COALESCE("position" - 1, 0) AS "result" ' <<
1406
1490
  'FROM ' << self.class.table << ' WHERE "position" NOTNULL ORDER BY "position" ASC)')
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: flexirecord
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.3
7
- date: 2007-02-08 00:00:00 +00:00
6
+ version: 0.0.4
7
+ date: 2007-02-11 00:00:00 +00:00
8
8
  summary: Object-Oriented Database Access Library
9
9
  require_paths:
10
10
  - lib/
@@ -30,6 +30,7 @@ authors:
30
30
  - Jan Behrens
31
31
  files:
32
32
  - ./LICENSE
33
+ - ./CHANGELOG
33
34
  - lib/thread_resource_pool.rb
34
35
  - lib/flexirecord.rb
35
36
  - lib/flexirecord-demo.rb