lafcadio 0.9.0 → 0.9.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.
@@ -137,9 +137,7 @@ module Lafcadio
137
137
  end
138
138
  end
139
139
 
140
- def eql?(otherObj)
141
- self == otherObj
142
- end
140
+ def eql?( otherObj ); self == otherObj; end
143
141
 
144
142
  def hash; "#{ self.class.name } #{ pk_id }".hash; end
145
143
  end
@@ -242,18 +240,7 @@ module Lafcadio
242
240
  # that each table has a +pk_id+ field that is used to match rows between
243
241
  # different levels.
244
242
  class DomainObject
245
- @@subclassHash = {}
246
- @@class_fields = {}
247
- @@pk_fields =
248
- Hash.new { |hash, key|
249
- pk_field = PrimaryKeyField.new( key )
250
- pk_field.db_field_name = @@sql_primary_keys[key]
251
- hash[key] = pk_field
252
- }
253
- @@sql_primary_keys = Hash.new( 'pk_id' )
254
- @@default_field_setup_hashes = Hash.new { |hash, key|
255
- hash[key] = Hash.new( {} )
256
- }
243
+ @@subclass_records = Hash.new do |h, k| h[k] = SubclassRecord.new( k ); end
257
244
 
258
245
  COMMIT_ADD = 1
259
246
  COMMIT_EDIT = 2
@@ -263,8 +250,11 @@ module Lafcadio
263
250
 
264
251
  def self.[]( pk_id ); get( pk_id ); end
265
252
 
253
+ def self.abstract_subclass?( a_class ) #:nodoc:
254
+ abstract_subclasses.include? a_class
255
+ end
256
+
266
257
  def self.abstract_subclasses #:nodoc:
267
- require 'lafcadio/domain'
268
258
  [ MapObject ]
269
259
  end
270
260
 
@@ -273,19 +263,19 @@ module Lafcadio
273
263
  # Returns an array of all fields defined for this class and all concrete
274
264
  # superclasses.
275
265
  def self.all_fields
276
- all_fields = []
277
- self_and_concrete_superclasses.each { |aClass|
278
- aClass.class_fields.each { |field| all_fields << field }
279
- }
280
- all_fields
266
+ self_and_concrete_superclasses.map { |a_class|
267
+ a_class.class_fields
268
+ }.flatten
281
269
  end
282
270
 
271
+ def self.class_field(fieldName) #:nodoc:
272
+ self.class_fields.find { |field| field.name == fieldName }
273
+ end
274
+
283
275
  def self.class_fields #:nodoc:
284
- class_fields = @@class_fields[self]
285
- unless class_fields
286
- @@class_fields[self] = self.get_class_fields
287
- class_fields = @@class_fields[self]
288
- class_fields.each do |class_field|
276
+ unless subclass_record.fields
277
+ subclass_record.fields = self.get_class_fields
278
+ subclass_record.fields.each do |class_field|
289
279
  begin
290
280
  undef_method class_field.name.to_sym
291
281
  rescue NameError
@@ -293,57 +283,80 @@ module Lafcadio
293
283
  end
294
284
  end
295
285
  end
296
- class_fields
286
+ subclass_record.fields
297
287
  end
298
288
 
299
289
  def self.create_field( field_class, *args )
300
- class_fields = @@class_fields[self]
301
- if args.last.is_a? Hash
302
- att_hash = args.last
303
- else
304
- att_hash = @@default_field_setup_hashes[self][field_class].clone
290
+ subclass_record.maybe_init_fields
291
+ att_hash = create_field_att_hash( field_class, *args )
292
+ field = field_class.create_with_args( self, att_hash )
293
+ unless class_field( field.name )
294
+ att_hash.each { |field_name, value|
295
+ setter = field_name + '='
296
+ field.send( setter, value ) if field.respond_to?( setter )
297
+ }
298
+ class_fields << field
305
299
  end
306
- if class_fields.nil?
307
- class_fields = [ @@pk_fields[self] ]
308
- @@class_fields[self] = class_fields
300
+ end
301
+
302
+ def self.create_field_att_hash( field_class, *args ) #:nodoc:
303
+ att_hash = args.last
304
+ unless att_hash.is_a? Hash
305
+ att_hash = subclass_record.default_field_setup_hash[field_class].clone
309
306
  end
310
307
  if field_class == DomainObjectField
311
308
  att_hash['linked_type'] = args.first
312
309
  att_hash['name'] = args[1] if args[1] and !args[1].is_a? Hash
313
310
  else
314
- name = args.first
315
- att_hash['name'] = name == String ? name : name.to_s
311
+ att_hash['name'] = args.first.to_s
316
312
  end
317
- field = field_class.instantiate_with_parameters( self, att_hash )
318
- unless class_fields.any? { |cf| cf.name == field.name }
319
- att_hash.each { |field_name, value|
320
- setter = field_name + '='
321
- field.send( setter, value ) if field.respond_to?( setter )
322
- }
323
- class_fields << field
313
+ att_hash
314
+ end
315
+
316
+ def self.create_fields( field_class, *args ) #:nodoc:
317
+ arg = args.shift
318
+ until args.empty?
319
+ next_arg = args.shift
320
+ if next_arg.is_a? String or next_arg.is_a? Symbol
321
+ create_field( field_class, arg )
322
+ arg = next_arg
323
+ else
324
+ create_field( field_class, arg, next_arg )
325
+ arg = args.shift
326
+ end
324
327
  end
328
+ create_field( field_class, arg ) unless arg.nil?
325
329
  end
326
330
 
327
331
  def self.default_field_setup_hash( field_class, hash )
328
- @@default_field_setup_hashes[self][field_class] = hash
332
+ subclass_record.default_field_setup_hash[field_class] = hash
329
333
  end
330
334
 
331
335
  def self.dependent_classes #:nodoc:
332
336
  dependent_classes = {}
333
337
  DomainObject.subclasses.each { |aClass|
334
- if aClass != DomainObjectProxy &&
335
- (!DomainObject.abstract_subclasses.index(aClass))
336
- aClass.class_fields.each { |field|
337
- if ( field.is_a?( DomainObjectField ) &&
338
- field.linked_type == self.domain_class )
339
- dependent_classes[aClass] = field
340
- end
341
- }
338
+ if (
339
+ !abstract_subclass?( aClass ) and
340
+ ( field = aClass.link_field( self ) )
341
+ )
342
+ dependent_classes[aClass] = field
342
343
  end
343
344
  }
344
345
  dependent_classes
345
346
  end
346
347
 
348
+ def self.domain_class #:nodoc:
349
+ self
350
+ end
351
+
352
+ def self.domain_dirs #:nodoc:
353
+ if ( domainDirStr = LafcadioConfig.new['domainDirs'] )
354
+ domainDirStr.split(',')
355
+ else
356
+ []
357
+ end
358
+ end
359
+
347
360
  def self.exist?( search_term, field_name = :pk_id )
348
361
  query = Query.infer( self ) { |dobj|
349
362
  dobj.send( field_name ).equals( search_term )
@@ -351,6 +364,16 @@ module Lafcadio
351
364
  !ObjectStore.get_object_store.get_subset( query ).empty?
352
365
  end
353
366
 
367
+ def self.field( fieldName ) #:nodoc:
368
+ aDomainClass = self
369
+ field = nil
370
+ while aDomainClass < DomainObject && !field
371
+ field = aDomainClass.class_field( fieldName )
372
+ aDomainClass = aDomainClass.superclass
373
+ end
374
+ field
375
+ end
376
+
354
377
  def self.first; all.first; end
355
378
 
356
379
  def self.get( *args )
@@ -370,29 +393,17 @@ module Lafcadio
370
393
  end
371
394
  end
372
395
 
373
- def self.get_class_field(fieldName) #:nodoc:
374
- field = nil
375
- self.class_fields.each { |aField|
376
- field = aField if aField.name == fieldName
377
- }
378
- field
379
- end
380
-
381
- def DomainObject.get_class_field_by_db_name( fieldName ) #:nodoc:
382
- self.class_fields.find { |field| field.db_field_name == fieldName }
383
- end
384
-
385
396
  # Returns an Array of ObjectField instances for this domain class, parsing
386
397
  # them from XML if necessary.
387
398
  def self.get_class_fields
388
399
  if self.methods( false ).include?( 'get_class_fields' )
389
- [ @@pk_fields[ self ] ]
390
- elsif abstract_subclasses.include?( self )
400
+ [ subclass_record.pk_field ]
401
+ elsif abstract_subclass?( self )
391
402
  []
392
403
  else
393
404
  xmlParser = try_load_xml_parser
394
405
  if xmlParser
395
- xmlParser.get_class_fields
406
+ xmlParser.get_class_fields
396
407
  else
397
408
  error_msg = "Couldn't find either an XML class description file " +
398
409
  "or get_class_fields method for " + self.name
@@ -401,107 +412,57 @@ module Lafcadio
401
412
  end
402
413
  end
403
414
 
404
- def self.get_domain_dirs #:nodoc:
405
- if ( domainDirStr = LafcadioConfig.new['domainDirs'] )
406
- domainDirStr.split(',')
407
- else
408
- []
409
- end
410
- end
411
-
412
- def self.get_field( fieldName ) #:nodoc:
413
- aDomainClass = self
414
- field = nil
415
- while aDomainClass < DomainObject && !field
416
- field = aDomainClass.get_class_field( fieldName )
417
- aDomainClass = aDomainClass.superclass
418
- end
419
- if field
420
- field
421
- else
422
- errStr = "Couldn't find field \"#{ fieldName }\" in " +
423
- "#{ self } domain class"
424
- raise( MissingError, errStr, caller )
425
- end
415
+ def self.is_child_domain_class? #:nodoc:
416
+ superclass != DomainObject and !abstract_subclass?( superclass )
426
417
  end
427
418
 
428
- def self.get_domain_class_from_string(typeString) #:nodoc:
429
- domain_class = nil
430
- require_domain_file( typeString )
431
- subclasses.each { |subclass|
432
- domain_class = subclass if subclass.to_s == typeString
433
- }
434
- if domain_class
435
- domain_class
436
- else
437
- raise CouldntMatchDomainClassError,
438
- "couldn't match domain_class #{typeString}", caller
439
- end
440
- end
419
+ def self.last; all.last; end
441
420
 
442
- def self.get_link_field( linked_domain_class ) # :nodoc:
421
+ def self.link_field( linked_domain_class ) # :nodoc:
443
422
  class_fields.find { |field|
444
- field.is_a? DomainObjectField and field.linked_type == linked_domain_class
423
+ field.is_a? DomainObjectField and
424
+ field.linked_type == linked_domain_class
445
425
  }
446
426
  end
447
427
 
448
- def self.inherited(subclass) #:nodoc:
449
- @@subclassHash[subclass] = true
450
- end
451
-
452
- def self.is_based_on? #:nodoc:
453
- self.superclass.is_concrete?
454
- end
455
-
456
- def self.is_concrete? #:nodoc:
457
- (self != DomainObject && abstract_subclasses.index(self).nil?)
458
- end
459
-
460
- def self.last; all.last; end
461
-
462
428
  def self.method_missing( methodId, *args ) #:nodoc:
429
+ dispatched = false
463
430
  method_name = methodId.id2name
464
- maybe_field_class_name = method_name.underscore_to_camel_case + 'Field'
465
431
  begin
466
- field_class = Lafcadio.const_get( maybe_field_class_name )
467
- create_field( field_class, *args )
432
+ method_missing_try_create_field( method_name, *args )
433
+ dispatched = true
468
434
  rescue NameError
469
- singular = method_name.singular
470
- if singular
471
- maybe_field_class_name = singular.underscore_to_camel_case + 'Field'
472
- begin
473
- field_class = Lafcadio.const_get( maybe_field_class_name )
474
- arg = args.shift
475
- until args.empty?
476
- next_arg = args.shift
477
- if next_arg.is_a? String or next_arg.is_a? Symbol
478
- create_field( field_class, arg )
479
- arg = next_arg
480
- else
481
- create_field( field_class, arg, next_arg )
482
- arg = args.shift
483
- end
484
- end
485
- create_field( field_class, arg ) unless arg.nil?
486
- rescue NameError
487
- super
488
- end
489
- else
490
- super
491
- end
435
+ begin
436
+ method_missing_try_create_fields( method_name, *args )
437
+ dispatched = true
438
+ rescue NameError; end
492
439
  end
440
+ super unless dispatched
493
441
  end
494
442
 
495
- def self.only; all.only; end
496
-
497
- def self.domain_class #:nodoc:
498
- self
443
+ def self.method_missing_try_create_field( method_name, *args ) # :nodoc:
444
+ maybe_field_class_name = method_name.underscore_to_camel_case + 'Field'
445
+ field_class = Lafcadio.const_get( maybe_field_class_name )
446
+ create_field( field_class, *args )
499
447
  end
448
+
449
+ def self.method_missing_try_create_fields( method_name, *args ) # :nodoc:
450
+ singular = method_name.singular
451
+ if singular
452
+ maybe_field_class_name = singular.underscore_to_camel_case + 'Field'
453
+ field_class = Lafcadio.const_get( maybe_field_class_name )
454
+ create_fields( field_class, *args )
455
+ else
456
+ raise NameError
457
+ end
458
+ end
459
+
460
+ def self.only; all.only; end
500
461
 
501
462
  def self.require_domain_file( typeString )
502
463
  typeString =~ /([^\:]*)$/
503
464
  fileName = $1
504
- get_domain_dirs.each { |domainDir|
465
+ domain_dirs.each { |domainDir|
505
466
  if Dir.entries(domainDir).index("#{fileName}.rb")
506
467
  require "#{ domainDir }#{ fileName }"
507
468
  end
@@ -515,8 +476,9 @@ module Lafcadio
515
476
  def self.self_and_concrete_superclasses # :nodoc:
516
477
  classes = [ ]
517
478
  a_domain_class = self
518
- until( a_domain_class == DomainObject ||
519
- abstract_subclasses.index( a_domain_class ) != nil )
479
+ until(
480
+ a_domain_class == DomainObject || abstract_subclass?( a_domain_class )
481
+ )
520
482
  classes << a_domain_class
521
483
  a_domain_class = a_domain_class.superclass
522
484
  end
@@ -526,24 +488,24 @@ module Lafcadio
526
488
  def self.singleton_method_added( symbol )
527
489
  if symbol.id2name == 'sql_primary_key_name' && self < DomainObject
528
490
  begin
529
- get_field( 'pk_id' ).db_field_name = self.send( symbol )
491
+ field( 'pk_id' ).db_field_name = self.send( symbol )
530
492
  rescue NameError
531
- @@sql_primary_keys[self] = self.send( symbol )
493
+ subclass_record.sql_primary_key = self.send( symbol )
532
494
  end
533
495
  end
534
496
  end
535
497
 
536
498
  # Returns the name of the primary key in the database, retrieving it from
537
499
  # the class definition XML if necessary.
538
- def self.sql_primary_key_name( set_sql_primary_key_name = nil )
539
- if set_sql_primary_key_name
540
- get_field( 'pk_id' ).db_field_name = set_sql_primary_key_name
541
- end
542
- get_field( 'pk_id' ).db_field_name
500
+ def self.sql_primary_key_name( set_db_field_name = nil )
501
+ field( 'pk_id' ).db_field_name = set_db_field_name if set_db_field_name
502
+ field( 'pk_id' ).db_field_name
543
503
  end
504
+
505
+ def self.subclass_record; @@subclass_records[self]; end
544
506
 
545
507
  def self.subclasses #:nodoc:
546
- @@subclassHash.keys
508
+ @@subclass_records.keys
547
509
  end
548
510
 
549
511
  # Returns the table name, which is assumed to be the domain class name
@@ -570,18 +532,17 @@ module Lafcadio
570
532
  dirName = LafcadioConfig.new['classDefinitionDir']
571
533
  xmlFileName = self.basename + '.xml'
572
534
  xmlPath = File.join( dirName, xmlFileName )
573
- xml = ''
574
535
  begin
575
- File.open( xmlPath ) { |file| xml = file.readlines.join }
536
+ xml = File.open( xmlPath ) do |f| f.gets( nil ); end
576
537
  ClassDefinitionXmlParser.new( self, xml )
577
538
  rescue Errno::ENOENT
578
539
  # no xml file, so no @xmlParser
579
540
  end
580
541
  end
581
542
 
582
- attr_accessor :error_messages, :last_commit, :fields, :fields_set
543
+ attr_accessor :fields_set, :field_values, :last_commit_type
583
544
  attr_reader :delete
584
- protected :fields, :fields_set
545
+ protected :fields_set, :field_values
585
546
 
586
547
  # fieldHash should contain key-value associations for the different
587
548
  # fields of this domain class. For example, instantiating a User class
@@ -598,26 +559,10 @@ module Lafcadio
598
559
  # If you're creating mock objects for unit tests, you can explicitly set
599
560
  # the +pk_id+ to represent objects that already exist in the database.
600
561
  def initialize(fieldHash)
601
- if fieldHash.respond_to? :keys
602
- fieldHash.keys.each { |key|
603
- key = key.to_s
604
- begin
605
- self.class.get_field( key )
606
- rescue MissingError
607
- raise ArgumentError, "Invalid field name #{ key }"
608
- end
609
- }
610
- end
611
- if fieldHash.is_a? Hash
612
- @fieldHash = {}
613
- fieldHash.each do |k, v| @fieldHash[k.to_s] = v; end
614
- else
615
- @fieldHash = fieldHash
616
- end
617
- @error_messages = []
618
- @fields = {}
562
+ fieldHash = preprocess_field_hash fieldHash
563
+ @field_values = {}
619
564
  @fields_set = []
620
- @original_values = OriginalValuesHash.new( @fieldHash )
565
+ @original_values = ReadOnlyHash.new @fieldHash
621
566
  check_fields = LafcadioConfig.new()['checkFields']
622
567
  verify if %w( onInstantiate onAllStates ).include?( check_fields )
623
568
  end
@@ -625,7 +570,7 @@ module Lafcadio
625
570
  # Returns a clone, with all of the fields copied.
626
571
  def clone
627
572
  copy = super
628
- copy.fields = @fields.clone
573
+ copy.field_values = @field_values.clone
629
574
  copy.fields_set = @fields_set.clone
630
575
  copy
631
576
  end
@@ -649,38 +594,29 @@ module Lafcadio
649
594
  commit
650
595
  end
651
596
 
652
- def get_field( field ) #:nodoc:
597
+ # Returns the subclass of DomainObject that this instance represents.
598
+ # Because of the way that proxying works, clients should call this method
599
+ # instead of Object.class.
600
+ def domain_class
601
+ self.class.domain_class
602
+ end
603
+
604
+ def field_value( field ) #:nodoc:
653
605
  unless @fields_set.include?( field )
654
- set_field( field, @fieldHash[field.name] )
606
+ set_field_value( field, @fieldHash[field.name] )
655
607
  end
656
- @fields[field.name]
608
+ @field_values[field.name]
657
609
  end
658
610
 
659
- def get_getter_field( methId ) #:nodoc:
660
- begin
661
- self.class.get_field( methId.id2name )
662
- rescue MissingError
663
- nil
664
- end
611
+ def getter_field( methId ) #:nodoc:
612
+ self.class.field methId.id2name
665
613
  end
666
614
 
667
- def get_setter_field( methId ) #:nodoc:
668
- if methId.id2name =~ /(.*)=$/
669
- begin
670
- self.class.get_field( $1 )
671
- rescue MissingError
672
- nil
673
- end
674
- else
675
- nil
676
- end
677
- end
678
-
679
615
  def method_missing( methId, *args ) #:nodoc:
680
- if ( field = get_setter_field( methId ) )
681
- set_field( field, args.first )
682
- elsif ( field = get_getter_field( methId ) )
683
- get_field( field )
616
+ if ( field = setter_field( methId ) )
617
+ set_field_value( field, args.first )
618
+ elsif ( field = getter_field( methId ) )
619
+ field_value( field )
684
620
  else
685
621
  new_symbol = ( 'get_' + methId.id2name ).to_sym
686
622
  object_store = ObjectStore.get_object_store
@@ -693,55 +629,93 @@ module Lafcadio
693
629
  end
694
630
  end
695
631
 
696
- # Returns the subclass of DomainObject that this instance represents.
697
- # Because of the way that proxying works, clients should call this method
698
- # instead of Object.class.
699
- def domain_class
700
- self.class.domain_class
701
- end
702
-
703
632
  # This template method is called before every commit. Subclasses can
704
633
  # override it to ensure code is executed before a commit.
705
634
  def pre_commit_trigger
706
635
  nil
707
636
  end
708
637
 
638
+ def preprocess_field_hash( fieldHash )
639
+ if fieldHash.is_a? Hash
640
+ fieldHash.keys.each { |key|
641
+ if self.class.field( key.to_s ).nil?
642
+ raise ArgumentError, "Invalid field name #{ key.to_s }"
643
+ end
644
+ }
645
+ @fieldHash = {}
646
+ fieldHash.each do |k, v| @fieldHash[k.to_s] = v; end
647
+ else
648
+ @fieldHash = fieldHash
649
+ end
650
+ end
651
+
709
652
  # This template method is called after every commit. Subclasses can
710
653
  # override it to ensure code is executed after a commit.
711
654
  def post_commit_trigger
712
655
  nil
713
656
  end
714
657
 
715
- def set_field( field, value ) #:nodoc:
716
- if field.class <= DomainObjectField
717
- if value.class != DomainObjectProxy && value
718
- value = DomainObjectProxy.new(value)
719
- end
658
+ def set_field_value( field, value ) #:nodoc:
659
+ if (
660
+ field.is_a?( DomainObjectField ) and
661
+ !value.is_a?( DomainObjectProxy ) and value
662
+ )
663
+ value = DomainObjectProxy.new(value)
720
664
  end
721
665
  if ( LafcadioConfig.new()['checkFields'] == 'onAllStates' &&
722
666
  !field.instance_of?( PrimaryKeyField ) )
723
667
  field.verify( value, pk_id )
724
668
  end
725
- @fields[field.name] = value
669
+ @field_values[field.name] = value
726
670
  @fields_set << field
727
671
  end
728
672
 
673
+ def setter_field( methId ) #:nodoc:
674
+ if methId.id2name =~ /(.*)=$/
675
+ self.class.field $1
676
+ else
677
+ nil
678
+ end
679
+ end
680
+
729
681
  def update!( changes )
730
682
  changes.each do |sym, value| self.send( sym.to_s + '=', value ); end
731
683
  commit
732
684
  end
733
685
 
734
686
  def verify
735
- if ObjectStore.get_object_store.mock?
687
+ if ObjectStore.mock?
736
688
  self.class.get_class_fields.each { |field|
737
689
  field.verify( self.send( field.name ), self.pk_id )
738
690
  }
739
691
  end
740
692
  end
741
693
 
742
- class OriginalValuesHash < DelegateClass( Hash )
694
+ class ReadOnlyHash < DelegateClass( Hash )
743
695
  def []=( key, val ); raise NoMethodError; end
744
696
  end
697
+
698
+ class SubclassRecord
699
+ attr_accessor :default_field_setup_hash, :fields, :sql_primary_key
700
+
701
+ def initialize( subclass )
702
+ @subclass = subclass
703
+ @default_field_setup_hash = Hash.new( {} )
704
+ @sql_primary_key = 'pk_id'
705
+ end
706
+
707
+ def maybe_init_fields
708
+ self.fields = [ pk_field ] if self.fields.nil?
709
+ end
710
+
711
+ def pk_field
712
+ if @pk_field.nil?
713
+ @pk_field = PrimaryKeyField.new @subclass
714
+ @pk_field.db_field_name = sql_primary_key
715
+ end
716
+ @pk_field
717
+ end
718
+ end
745
719
  end
746
720
 
747
721
  # Any domain class that is used mostly to map between two other domain
@@ -750,12 +724,9 @@ module Lafcadio
750
724
  # the domain classes that the map object maps between.
751
725
  class MapObject < DomainObject
752
726
  def self.other_mapped_type(firstType) #:nodoc:
753
- types = mappedTypes
754
- if types.index(firstType) == 0
755
- types[1]
756
- else
757
- types[0]
758
- end
727
+ mt = mappedTypes.clone
728
+ mt.delete firstType
729
+ mt.only
759
730
  end
760
731
 
761
732
  def self.subsidiary_map #:nodoc: