lafcadio 0.9.0 → 0.9.1

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