lafcadio 0.8.1 → 0.8.2

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.
@@ -16,7 +16,7 @@
16
16
  # http://lafcadio.rubyforge.org/tutorial.html.
17
17
 
18
18
  module Lafcadio
19
- Version = "0.8.1"
19
+ Version = "0.8.2"
20
20
 
21
21
  require 'lafcadio/depend'
22
22
  require 'lafcadio/domain'
@@ -16,7 +16,7 @@
16
16
  # http://lafcadio.rubyforge.org/tutorial.html.
17
17
 
18
18
  module Lafcadio
19
- Version = "0.8.0"
19
+ Version = "0.8.1"
20
20
 
21
21
  require 'lafcadio/depend'
22
22
  require 'lafcadio/domain'
@@ -605,7 +605,7 @@ module Lafcadio
605
605
  @error_messages = []
606
606
  @fields = {}
607
607
  @fields_set = []
608
- @original_values = OriginalValuesHash.new( @fieldHash )
608
+ reset_original_values_hash @fieldHash
609
609
  check_fields = LafcadioConfig.new()['checkFields']
610
610
  verify if %w( onInstantiate onAllStates ).include?( check_fields )
611
611
  end
@@ -699,6 +699,10 @@ module Lafcadio
699
699
  def post_commit_trigger
700
700
  nil
701
701
  end
702
+
703
+ def reset_original_values_hash( f = @fields ) #:nodoc:
704
+ @original_values = OriginalValuesHash.new( f.clone )
705
+ end
702
706
 
703
707
  def set_field( field, value ) #:nodoc:
704
708
  if field.class <= DomainObjectField
@@ -0,0 +1,753 @@
1
+ require 'extensions/module'
2
+ require 'lafcadio/objectField'
3
+ require 'lafcadio/util'
4
+ require 'rexml/document'
5
+
6
+ module Lafcadio
7
+ class ClassDefinitionXmlParser # :nodoc: all
8
+ def initialize( domain_class, xml )
9
+ @domain_class = domain_class
10
+ @xmlDocRoot = REXML::Document.new( xml ).root
11
+ @namesProcessed = {}
12
+ end
13
+
14
+ def get_class_field( fieldElt )
15
+ className = fieldElt.attributes['class'].to_s
16
+ name = fieldElt.attributes['name']
17
+ if className != ''
18
+ fieldClass = Class.by_name( 'Lafcadio::' + className )
19
+ register_name( name )
20
+ field = fieldClass.instantiate_from_xml( @domain_class, fieldElt )
21
+ set_field_attributes( field, fieldElt )
22
+ else
23
+ msg = "Couldn't find field class '#{ className }' for field " +
24
+ "'#{ name }'"
25
+ raise( MissingError, msg, caller )
26
+ end
27
+ field
28
+ end
29
+
30
+ def get_class_fields
31
+ namesProcessed = {}
32
+ pk_field = PrimaryKeyField.new( @domain_class )
33
+ if ( spkn = @xmlDocRoot.attributes['sql_primary_key_name'] )
34
+ pk_field.db_field_name = spkn
35
+ end
36
+ fields = [ pk_field ]
37
+ @xmlDocRoot.elements.each('field') { |fieldElt|
38
+ fields << get_class_field( fieldElt )
39
+ }
40
+ fields
41
+ end
42
+
43
+ def possible_field_attributes
44
+ fieldAttr = []
45
+ fieldAttr << FieldAttribute.new( 'size', FieldAttribute::INTEGER )
46
+ fieldAttr << FieldAttribute.new( 'unique', FieldAttribute::BOOLEAN )
47
+ fieldAttr << FieldAttribute.new( 'not_null', FieldAttribute::BOOLEAN )
48
+ fieldAttr << FieldAttribute.new( 'enum_type', FieldAttribute::ENUM,
49
+ BooleanField )
50
+ fieldAttr << FieldAttribute.new( 'enums', FieldAttribute::HASH )
51
+ fieldAttr << FieldAttribute.new( 'range', FieldAttribute::ENUM,
52
+ DateField )
53
+ fieldAttr << FieldAttribute.new( 'large', FieldAttribute::BOOLEAN )
54
+ end
55
+
56
+ def register_name( name )
57
+ raise InvalidDataError if @namesProcessed[name]
58
+ @namesProcessed[name] = true
59
+ end
60
+
61
+ def set_field_attributes( field, fieldElt )
62
+ possible_field_attributes.each { |fieldAttr|
63
+ fieldAttr.maybe_set_field_attr( field, fieldElt )
64
+ }
65
+ end
66
+
67
+ def table_name
68
+ @xmlDocRoot.attributes['table_name']
69
+ end
70
+
71
+ class FieldAttribute
72
+ INTEGER = 1
73
+ BOOLEAN = 2
74
+ ENUM = 3
75
+ HASH = 4
76
+
77
+ attr_reader :name, :value_class
78
+
79
+ def initialize( name, value_class, objectFieldClass = nil )
80
+ @name = name; @value_class = value_class
81
+ @objectFieldClass = objectFieldClass
82
+ end
83
+
84
+ def maybe_set_field_attr( field, fieldElt )
85
+ setterMethod = "#{ name }="
86
+ if field.respond_to?( setterMethod )
87
+ if value_class != FieldAttribute::HASH
88
+ if ( attrStr = fieldElt.attributes[name] )
89
+ field.send( setterMethod, value_from_string( attrStr ) )
90
+ end
91
+ else
92
+ if ( attrElt = fieldElt.elements[name] )
93
+ field.send( setterMethod, value_from_elt( attrElt ) )
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ def value_from_elt( elt )
100
+ hash = {}
101
+ elt.elements.each( @name.singular ) { |subElt|
102
+ key = subElt.attributes['key'] == 'true'
103
+ value = subElt.text.to_s
104
+ hash[key] = value
105
+ }
106
+ hash
107
+ end
108
+
109
+ def value_from_string( valueStr )
110
+ if @value_class == INTEGER
111
+ valueStr.to_i
112
+ elsif @value_class == BOOLEAN
113
+ valueStr == 'y'
114
+ elsif @value_class == ENUM
115
+ eval "#{ @objectFieldClass.name }::#{ valueStr }"
116
+ end
117
+ end
118
+ end
119
+
120
+ class InvalidDataError < ArgumentError; end
121
+ end
122
+
123
+ module DomainComparable
124
+ include Comparable
125
+
126
+ # A DomainObject or DomainObjectProxy is compared by +domain_class+ and by
127
+ # +pk_id+.
128
+ def <=>(anOther)
129
+ if anOther.respond_to?( 'domain_class' )
130
+ if self.domain_class == anOther.domain_class
131
+ self.pk_id <=> anOther.pk_id
132
+ else
133
+ self.domain_class.name <=> anOther.domain_class.name
134
+ end
135
+ else
136
+ nil
137
+ end
138
+ end
139
+
140
+ def eql?(otherObj)
141
+ self == otherObj
142
+ end
143
+
144
+ def hash; "#{ self.class.name } #{ pk_id }".hash; end
145
+ end
146
+
147
+ # All classes that correspond to a table in the database need to be children
148
+ # of DomainObject.
149
+ #
150
+ # = Defining fields
151
+ # There are two ways to define the fields of a DomainObject subclass.
152
+ # 1. Defining fields in an XML file. To do this,
153
+ # 1. Set one directory to contain all your XML files, by setting
154
+ # +classDefinitionDir+ in your LafcadioConfig file.
155
+ # 2. Write one XML file per domain class. For example, a User.xml file
156
+ # might look like:
157
+ # <lafcadio_class_definition name="User">
158
+ # <field name="lastName" class="StringField"/>
159
+ # <field name="email" class="StringField"/>
160
+ # <field name="password" class="StringField"/>
161
+ # <field name="birthday" class="DateField"/>
162
+ # </lafcadio_class_definition>
163
+ # 2. Overriding DomainObject.get_class_fields. The method should return an Array
164
+ # of instances of ObjectField or its children. The order is unimportant.
165
+ # For example:
166
+ # class User < DomainObject
167
+ # def User.get_class_fields
168
+ # fields = []
169
+ # fields << StringField.new(self, 'firstName')
170
+ # fields << StringField.new(self, 'lastName')
171
+ # fields << StringField.new(self, 'email')
172
+ # fields << StringField.new(self, 'password')
173
+ # fields << DateField.new(self, 'birthday')
174
+ # fields
175
+ # end
176
+ # end
177
+ #
178
+ # = Setting and retrieving fields
179
+ # Once your fields are defined, you can create an instance by passing in a
180
+ # hash of field names and values.
181
+ # john = User.new( 'firstName' => 'John', 'lastName' => 'Doe',
182
+ # 'email' => 'john.doe@email.com',
183
+ # 'password' => 'my_password',
184
+ # 'birthday' => tenYearsAgo )
185
+ #
186
+ # You can read and write these fields like normal instance attributes.
187
+ # john.email => 'john.doe@email.com'
188
+ # john.email = 'john.doe@mail.email.com'
189
+ #
190
+ # If your domain class has fields that refer to other domain classes, or even
191
+ # to another row in the same table, you can use a DomainObjectField to express the
192
+ # relation.
193
+ # <lafcadio_class_definition name="Message">
194
+ # <field name="subject" class="StringField" />
195
+ # <field name="body" class="StringField" />
196
+ # <field name="author" class="DomainObjectField" linked_type="User" />
197
+ # <field name="recipient" class="DomainObjectField" linked_type="User" />
198
+ # <field name="dateSent" class="DateField" />
199
+ # </lafcadio_class_definition>
200
+ #
201
+ # msg = Message.new( 'subject' => 'hi there',
202
+ # 'body' => 'You wanna go to the movies on Saturday?',
203
+ # 'author' => john, 'recipient' => jane,
204
+ # 'dateSent' => Date.today )
205
+ #
206
+ # = pk_id and committing
207
+ # Lafcadio requires that each table has a numeric primary key. It assumes that
208
+ # this key is named +pk_id+ in the database, though that can be overridden.
209
+ #
210
+ # When you create a domain object by calling new, you should not assign a
211
+ # +pk_id+ to the new instance. The pk_id will automatically be set when you
212
+ # commit the object by calling DomainObject#commit.
213
+ #
214
+ # However, you may want to manually set +pk_id+ when setting up a test case, so
215
+ # you can ensure that a domain object has a given primary key.
216
+ #
217
+ # = Naming assumptions, and how to override them
218
+ # By default, Lafcadio assumes that every domain object is indexed by the
219
+ # field +pk_id+ in the database schema. If you're dealing with a table that
220
+ # uses a different field name, override DomainObject.sql_primary_key_name.
221
+ # However, you will always use +pk_id+ in your Ruby code.
222
+ #
223
+ # Lafcadio assumes that a domain class corresponds to a table whose name is
224
+ # the plural of the class name, and whose first letter is lowercase. A User
225
+ # class is assumed to be stored in a "users" table, while a ProductCategory
226
+ # class is assumed to be stored in a "productCategories" table. Override
227
+ # DomainObject.table_name to override this behavior.
228
+ #
229
+ # = Inheritance
230
+ # Domain classes can inherit from other domain classes; they have all the
231
+ # fields of any concrete superclasses plus any new fields defined for
232
+ # themselves. You can use normal inheritance to define this:
233
+ # class User < DomainObject
234
+ # ...
235
+ # end
236
+ #
237
+ # class Administrator < User
238
+ # ...
239
+ # end
240
+ #
241
+ # Lafcadio assumes that each concrete class has a corresponding table, and
242
+ # that each table has a +pk_id+ field that is used to match rows between
243
+ # different levels.
244
+ 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
+ }
257
+
258
+ COMMIT_ADD = 1
259
+ COMMIT_EDIT = 2
260
+ COMMIT_DELETE = 3
261
+
262
+ include DomainComparable
263
+
264
+ def self.[]( pk_id ); get( pk_id ); end
265
+
266
+ def self.abstract_subclasses #:nodoc:
267
+ require 'lafcadio/domain'
268
+ [ MapObject ]
269
+ end
270
+
271
+ def self.all; ObjectStore.get_object_store.get_all( self ); end
272
+
273
+ # Returns an array of all fields defined for this class and all concrete
274
+ # superclasses.
275
+ 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
281
+ end
282
+
283
+ 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|
289
+ begin
290
+ undef_method class_field.name.to_sym
291
+ rescue NameError
292
+ # not defined globally or in an included Module, skip it
293
+ end
294
+ end
295
+ end
296
+ class_fields
297
+ end
298
+
299
+ 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
305
+ end
306
+ if class_fields.nil?
307
+ class_fields = [ @@pk_fields[self] ]
308
+ @@class_fields[self] = class_fields
309
+ end
310
+ if field_class == DomainObjectField
311
+ att_hash['linked_type'] = args.first
312
+ att_hash['name'] = args[1] if args[1] and !args[1].is_a? Hash
313
+ else
314
+ name = args.first
315
+ att_hash['name'] = name == String ? name : name.to_s
316
+ 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
324
+ end
325
+ end
326
+
327
+ def self.default_field_setup_hash( field_class, hash )
328
+ @@default_field_setup_hashes[self][field_class] = hash
329
+ end
330
+
331
+ def self.dependent_classes #:nodoc:
332
+ dependent_classes = {}
333
+ 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
+ }
342
+ end
343
+ }
344
+ dependent_classes
345
+ end
346
+
347
+ def self.exist?( search_term, field_name = :pk_id )
348
+ query = Query.infer( self ) { |dobj|
349
+ dobj.send( field_name ).equals( search_term )
350
+ }
351
+ !ObjectStore.get_object_store.get_subset( query ).empty?
352
+ end
353
+
354
+ def self.first; all.first; end
355
+
356
+ def self.get_class_field(fieldName) #:nodoc:
357
+ field = nil
358
+ self.class_fields.each { |aField|
359
+ field = aField if aField.name == fieldName
360
+ }
361
+ field
362
+ end
363
+
364
+ def DomainObject.get_class_field_by_db_name( fieldName ) #:nodoc:
365
+ self.class_fields.find { |field| field.db_field_name == fieldName }
366
+ end
367
+
368
+ # Returns an Array of ObjectField instances for this domain class, parsing
369
+ # them from XML if necessary.
370
+ def self.get_class_fields
371
+ if self.methods( false ).include?( 'get_class_fields' )
372
+ [ @@pk_fields[ self ] ]
373
+ elsif abstract_subclasses.include?( self )
374
+ []
375
+ else
376
+ xmlParser = try_load_xml_parser
377
+ if xmlParser
378
+ xmlParser.get_class_fields
379
+ else
380
+ error_msg = "Couldn't find either an XML class description file " +
381
+ "or get_class_fields method for " + self.name
382
+ raise MissingError, error_msg, caller
383
+ end
384
+ end
385
+ end
386
+
387
+ def self.get_domain_dirs #:nodoc:
388
+ if ( domainDirStr = LafcadioConfig.new['domainDirs'] )
389
+ domainDirStr.split(',')
390
+ else
391
+ []
392
+ end
393
+ end
394
+
395
+ def self.get_field( fieldName ) #:nodoc:
396
+ aDomainClass = self
397
+ field = nil
398
+ while aDomainClass < DomainObject && !field
399
+ field = aDomainClass.get_class_field( fieldName )
400
+ aDomainClass = aDomainClass.superclass
401
+ end
402
+ if field
403
+ field
404
+ else
405
+ errStr = "Couldn't find field \"#{ fieldName }\" in " +
406
+ "#{ self } domain class"
407
+ raise( MissingError, errStr, caller )
408
+ end
409
+ end
410
+
411
+ def self.get_domain_class_from_string(typeString) #:nodoc:
412
+ domain_class = nil
413
+ require_domain_file( typeString )
414
+ subclasses.each { |subclass|
415
+ domain_class = subclass if subclass.to_s == typeString
416
+ }
417
+ if domain_class
418
+ domain_class
419
+ else
420
+ raise CouldntMatchDomainClassError,
421
+ "couldn't match domain_class #{typeString}", caller
422
+ end
423
+ end
424
+
425
+ def self.get_link_field( linked_domain_class ) # :nodoc:
426
+ class_fields.find { |field|
427
+ field.is_a? DomainObjectField and field.linked_type == linked_domain_class
428
+ }
429
+ end
430
+
431
+ def self.inherited(subclass) #:nodoc:
432
+ @@subclassHash[subclass] = true
433
+ end
434
+
435
+ def self.is_based_on? #:nodoc:
436
+ self.superclass.is_concrete?
437
+ end
438
+
439
+ def self.is_concrete? #:nodoc:
440
+ (self != DomainObject && abstract_subclasses.index(self).nil?)
441
+ end
442
+
443
+ def self.last; all.last; end
444
+
445
+ def self.method_missing( methodId, *args ) #:nodoc:
446
+ method_name = methodId.id2name
447
+ if method_name == 'get'
448
+ if block_given?
449
+ query = Query.infer( self ) { |dobj| yield( dobj ) }
450
+ ObjectStore.get_object_store.get_subset( query )
451
+ elsif args.size == 1
452
+ ObjectStore.get_object_store.get( self, *args )
453
+ else
454
+ ObjectStore.get_object_store.get_filtered( self.name, *args )
455
+ end
456
+ else
457
+ maybe_field_class_name = method_name.underscore_to_camel_case + 'Field'
458
+ begin
459
+ field_class = Lafcadio.const_get( maybe_field_class_name )
460
+ create_field( field_class, *args )
461
+ rescue NameError
462
+ singular = method_name.singular
463
+ if singular
464
+ maybe_field_class_name = singular.underscore_to_camel_case + 'Field'
465
+ begin
466
+ field_class = Lafcadio.const_get( maybe_field_class_name )
467
+ arg = args.shift
468
+ until args.empty?
469
+ next_arg = args.shift
470
+ if next_arg.is_a? String or next_arg.is_a? Symbol
471
+ create_field( field_class, arg )
472
+ arg = next_arg
473
+ else
474
+ create_field( field_class, arg, next_arg )
475
+ arg = args.shift
476
+ end
477
+ end
478
+ create_field( field_class, arg ) unless arg.nil?
479
+ rescue NameError
480
+ super
481
+ end
482
+ else
483
+ super
484
+ end
485
+ end
486
+ end
487
+ end
488
+
489
+ def self.only; all.only; end
490
+
491
+ def self.domain_class #:nodoc:
492
+ self
493
+ end
494
+
495
+ def self.require_domain_file( typeString )
496
+ typeString =~ /([^\:]*)$/
497
+ fileName = $1
498
+ get_domain_dirs.each { |domainDir|
499
+ if Dir.entries(domainDir).index("#{fileName}.rb")
500
+ require "#{ domainDir }#{ fileName }"
501
+ end
502
+ }
503
+ if (domainFiles = LafcadioConfig.new['domainFiles'])
504
+ domainFiles = domainFiles.split( ',' ) if domainFiles.is_a? String
505
+ domainFiles.each { |domainFile| require domainFile }
506
+ end
507
+ end
508
+
509
+ def self.self_and_concrete_superclasses # :nodoc:
510
+ classes = [ ]
511
+ a_domain_class = self
512
+ until( a_domain_class == DomainObject ||
513
+ abstract_subclasses.index( a_domain_class ) != nil )
514
+ classes << a_domain_class
515
+ a_domain_class = a_domain_class.superclass
516
+ end
517
+ classes
518
+ end
519
+
520
+ def self.singleton_method_added( symbol )
521
+ if symbol.id2name == 'sql_primary_key_name' && self < DomainObject
522
+ begin
523
+ get_field( 'pk_id' ).db_field_name = self.send( symbol )
524
+ rescue NameError
525
+ @@sql_primary_keys[self] = self.send( symbol )
526
+ end
527
+ end
528
+ end
529
+
530
+ # Returns the name of the primary key in the database, retrieving it from
531
+ # the class definition XML if necessary.
532
+ def self.sql_primary_key_name( set_sql_primary_key_name = nil )
533
+ if set_sql_primary_key_name
534
+ get_field( 'pk_id' ).db_field_name = set_sql_primary_key_name
535
+ end
536
+ get_field( 'pk_id' ).db_field_name
537
+ end
538
+
539
+ def self.subclasses #:nodoc:
540
+ @@subclassHash.keys
541
+ end
542
+
543
+ # Returns the table name, which is assumed to be the domain class name
544
+ # pluralized, and with the first letter lowercase. A User class is
545
+ # assumed to be stored in a "users" table, while a ProductCategory class is
546
+ # assumed to be stored in a "productCategories" table.
547
+ def self.table_name( set_table_name = nil )
548
+ if set_table_name
549
+ @table_name = set_table_name
550
+ elsif @table_name
551
+ @table_name
552
+ else
553
+ xmlParser = try_load_xml_parser
554
+ if (!xmlParser.nil? && table_name = xmlParser.table_name)
555
+ table_name
556
+ else
557
+ self.basename.camel_case_to_underscore.plural
558
+ end
559
+ end
560
+ end
561
+
562
+ def self.try_load_xml_parser
563
+ require 'lafcadio/domain'
564
+ dirName = LafcadioConfig.new['classDefinitionDir']
565
+ xmlFileName = self.basename + '.xml'
566
+ xmlPath = File.join( dirName, xmlFileName )
567
+ xml = ''
568
+ begin
569
+ File.open( xmlPath ) { |file| xml = file.readlines.join }
570
+ ClassDefinitionXmlParser.new( self, xml )
571
+ rescue Errno::ENOENT
572
+ # no xml file, so no @xmlParser
573
+ end
574
+ end
575
+
576
+ attr_accessor :error_messages, :last_commit, :fields, :fields_set
577
+ attr_reader :delete
578
+ protected :fields, :fields_set
579
+
580
+ # fieldHash should contain key-value associations for the different
581
+ # fields of this domain class. For example, instantiating a User class
582
+ # might look like:
583
+ #
584
+ # User.new( 'firstNames' => 'John', 'lastName' => 'Doe',
585
+ # 'email' => 'john.doe@email.com', 'password' => 'l33t' )
586
+ #
587
+ # In normal usage any code you write that creates a domain object will not
588
+ # define the +pk_id+ field. The system assumes that a domain object with an
589
+ # undefined +pk_id+ has yet to be inserted into the database, and when you
590
+ # commit the domain object a +pk_id+ will automatically be assigned.
591
+ #
592
+ # If you're creating mock objects for unit tests, you can explicitly set
593
+ # the +pk_id+ to represent objects that already exist in the database.
594
+ def initialize(fieldHash)
595
+ if fieldHash.respond_to? :keys
596
+ fieldHash.keys.each { |key|
597
+ begin
598
+ self.class.get_field( key )
599
+ rescue MissingError
600
+ raise ArgumentError, "Invalid field name #{ key }"
601
+ end
602
+ }
603
+ end
604
+ @fieldHash = fieldHash
605
+ @error_messages = []
606
+ @fields = {}
607
+ @fields_set = []
608
+ @original_values = OriginalValuesHash.new( @fieldHash )
609
+ check_fields = LafcadioConfig.new()['checkFields']
610
+ verify if %w( onInstantiate onAllStates ).include?( check_fields )
611
+ end
612
+
613
+ # Returns a clone, with all of the fields copied.
614
+ def clone
615
+ copy = super
616
+ copy.fields = @fields.clone
617
+ copy.fields_set = @fields_set.clone
618
+ copy
619
+ end
620
+
621
+ # Commits this domain object to the database.
622
+ def commit
623
+ ObjectStore.get_object_store.commit self
624
+ end
625
+
626
+ # Set the delete value to true if you want this domain object to be deleted
627
+ # from the database during its next commit.
628
+ def delete=(value)
629
+ if value && !pk_id
630
+ raise "No point deleting an object that's not already in the DB"
631
+ end
632
+ @delete = value
633
+ end
634
+
635
+ def delete!
636
+ self.delete = true
637
+ commit
638
+ end
639
+
640
+ def get_field( field ) #:nodoc:
641
+ unless @fields_set.include?( field )
642
+ set_field( field, @fieldHash[field.name] )
643
+ end
644
+ @fields[field.name]
645
+ end
646
+
647
+ def get_getter_field( methId ) #:nodoc:
648
+ begin
649
+ self.class.get_field( methId.id2name )
650
+ rescue MissingError
651
+ nil
652
+ end
653
+ end
654
+
655
+ def get_setter_field( methId ) #:nodoc:
656
+ if methId.id2name =~ /(.*)=$/
657
+ begin
658
+ self.class.get_field( $1 )
659
+ rescue MissingError
660
+ nil
661
+ end
662
+ else
663
+ nil
664
+ end
665
+ end
666
+
667
+ def method_missing( methId, *args ) #:nodoc:
668
+ if ( field = get_setter_field( methId ) )
669
+ set_field( field, args.first )
670
+ elsif ( field = get_getter_field( methId ) )
671
+ get_field( field )
672
+ else
673
+ new_symbol = ( 'get_' + methId.id2name ).to_sym
674
+ object_store = ObjectStore.get_object_store
675
+ if object_store.respond_to? new_symbol
676
+ args = [ self ].concat args
677
+ object_store.send( 'get_' + methId.id2name, *args )
678
+ else
679
+ super( methId, *args )
680
+ end
681
+ end
682
+ end
683
+
684
+ # Returns the subclass of DomainObject that this instance represents.
685
+ # Because of the way that proxying works, clients should call this method
686
+ # instead of Object.class.
687
+ def domain_class
688
+ self.class.domain_class
689
+ end
690
+
691
+ # This template method is called before every commit. Subclasses can
692
+ # override it to ensure code is executed before a commit.
693
+ def pre_commit_trigger
694
+ nil
695
+ end
696
+
697
+ # This template method is called after every commit. Subclasses can
698
+ # override it to ensure code is executed after a commit.
699
+ def post_commit_trigger
700
+ nil
701
+ end
702
+
703
+ def set_field( field, value ) #:nodoc:
704
+ if field.class <= DomainObjectField
705
+ if value.class != DomainObjectProxy && value
706
+ value = DomainObjectProxy.new(value)
707
+ end
708
+ end
709
+ if ( LafcadioConfig.new()['checkFields'] == 'onAllStates' &&
710
+ !field.instance_of?( PrimaryKeyField ) )
711
+ field.verify( value, pk_id )
712
+ end
713
+ @fields[field.name] = value
714
+ @fields_set << field
715
+ end
716
+
717
+ def update!( changes )
718
+ changes.each do |sym, value| self.send( sym.to_s + '=', value ); end
719
+ commit
720
+ end
721
+
722
+ def verify
723
+ if ObjectStore.get_object_store.mock?
724
+ self.class.get_class_fields.each { |field|
725
+ field.verify( self.send( field.name ), self.pk_id )
726
+ }
727
+ end
728
+ end
729
+
730
+ class OriginalValuesHash < DelegateClass( Hash )
731
+ def []=( key, val ); raise NoMethodError; end
732
+ end
733
+ end
734
+
735
+ # Any domain class that is used mostly to map between two other domain
736
+ # classes should be a subclass of MapObject. Subclasses of MapObject should
737
+ # override MapObject.mappedTypes, returning a two-element array containing
738
+ # the domain classes that the map object maps between.
739
+ class MapObject < DomainObject
740
+ def self.other_mapped_type(firstType) #:nodoc:
741
+ types = mappedTypes
742
+ if types.index(firstType) == 0
743
+ types[1]
744
+ else
745
+ types[0]
746
+ end
747
+ end
748
+
749
+ def self.subsidiary_map #:nodoc:
750
+ nil
751
+ end
752
+ end
753
+ end