lafcadio 0.8.1 → 0.8.2

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