lafcadio 0.6.1 → 0.6.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.
data/lib/lafcadio.rb CHANGED
@@ -16,7 +16,7 @@
16
16
  # http://lafcadio.rubyforge.org/tutorial.html.
17
17
 
18
18
  module Lafcadio
19
- Version = "0.6.1"
19
+ Version = "0.6.2"
20
20
 
21
21
  require 'lafcadio/dateTime'
22
22
  require 'lafcadio/depend'
data/lib/lafcadio.rb~ CHANGED
@@ -16,7 +16,7 @@
16
16
  # http://lafcadio.rubyforge.org/tutorial.html.
17
17
 
18
18
  module Lafcadio
19
- Version = "0.6.0"
19
+ Version = "0.6.1"
20
20
 
21
21
  require 'lafcadio/dateTime'
22
22
  require 'lafcadio/depend'
@@ -1,3 +1,4 @@
1
+ require 'extensions/module'
1
2
  require 'lafcadio/objectField'
2
3
  require 'lafcadio/util'
3
4
  require 'rexml/document'
@@ -348,7 +349,7 @@ module Lafcadio
348
349
  []
349
350
  end
350
351
  end
351
-
352
+
352
353
  def self.get_field( fieldName ) #:nodoc:
353
354
  aDomainClass = self
354
355
  field = nil
@@ -359,7 +360,7 @@ module Lafcadio
359
360
  if field
360
361
  field
361
362
  else
362
- errStr = "Couldn't find field \"#{ field }\" in " +
363
+ errStr = "Couldn't find field \"#{ fieldName }\" in " +
363
364
  "#{ self } domain class"
364
365
  raise( MissingError, errStr, caller )
365
366
  end
@@ -393,8 +394,7 @@ module Lafcadio
393
394
 
394
395
  def self.method_missing( methodId, *args ) #:nodoc:
395
396
  method_name = methodId.id2name
396
- maybe_field_class_name = ( method_name.gsub( /^(.)/ ) { $&.upcase } ) +
397
- 'Field'
397
+ maybe_field_class_name = method_name.underscore_to_camel_case + 'Field'
398
398
  field_class = Lafcadio.const_get( maybe_field_class_name )
399
399
  create_field( field_class, args[0], args[1] || {} )
400
400
  end
@@ -0,0 +1,639 @@
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( English.singular( @name ) ) { |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="TextField"/>
159
+ # <field name="email" class="TextField"/>
160
+ # <field name="password" class="TextField"/>
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 << TextField.new(self, 'firstName')
170
+ # fields << TextField.new(self, 'lastName')
171
+ # fields << TextField.new(self, 'email')
172
+ # fields << TextField.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 LinkField to express the
192
+ # relation.
193
+ # <lafcadio_class_definition name="Message">
194
+ # <field name="subject" class="TextField" />
195
+ # <field name="body" class="TextField" />
196
+ # <field name="author" class="LinkField" linked_type="User" />
197
+ # <field name="recipient" class="LinkField" 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
+
255
+ COMMIT_ADD = 1
256
+ COMMIT_EDIT = 2
257
+ COMMIT_DELETE = 3
258
+
259
+ include DomainComparable
260
+
261
+ def self.abstract_subclasses #:nodoc:
262
+ require 'lafcadio/domain'
263
+ [ MapObject ]
264
+ end
265
+
266
+ # Returns an array of all fields defined for this class and all concrete
267
+ # superclasses.
268
+ def self.all_fields
269
+ all_fields = []
270
+ self_and_concrete_superclasses.each { |aClass|
271
+ aClass.class_fields.each { |field| all_fields << field }
272
+ }
273
+ all_fields
274
+ end
275
+
276
+ def self.class_fields #:nodoc:
277
+ class_fields = @@class_fields[self]
278
+ unless class_fields
279
+ @@class_fields[self] = self.get_class_fields
280
+ class_fields = @@class_fields[self]
281
+ end
282
+ class_fields
283
+ end
284
+
285
+ def self.create_field( field_class, name, att_hash )
286
+ class_fields = @@class_fields[self]
287
+ if class_fields.nil?
288
+ class_fields = [ @@pk_fields[self] ]
289
+ @@class_fields[self] = class_fields
290
+ end
291
+ att_hash['name'] = name
292
+ field = field_class.instantiate_with_parameters( self, att_hash )
293
+ att_hash.each { |field_name, value|
294
+ setter = field_name + '='
295
+ field.send( setter, value ) if field.respond_to?( setter )
296
+ }
297
+ class_fields << field
298
+ end
299
+
300
+ def self.dependent_classes #:nodoc:
301
+ dependent_classes = {}
302
+ DomainObject.subclasses.each { |aClass|
303
+ if aClass != DomainObjectProxy &&
304
+ (!DomainObject.abstract_subclasses.index(aClass))
305
+ aClass.class_fields.each { |field|
306
+ if ( field.is_a?( LinkField ) &&
307
+ field.linked_type == self.domain_class )
308
+ dependent_classes[aClass] = field
309
+ end
310
+ }
311
+ end
312
+ }
313
+ dependent_classes
314
+ end
315
+
316
+ def self.get_class_field(fieldName) #:nodoc:
317
+ field = nil
318
+ self.class_fields.each { |aField|
319
+ field = aField if aField.name == fieldName
320
+ }
321
+ field
322
+ end
323
+
324
+ def DomainObject.get_class_field_by_db_name( fieldName ) #:nodoc:
325
+ self.class_fields.find { |field| field.db_field_name == fieldName }
326
+ end
327
+
328
+ # Returns an Array of ObjectField instances for this domain class, parsing
329
+ # them from XML if necessary.
330
+ def self.get_class_fields
331
+ if self.methods( false ).include?( 'get_class_fields' )
332
+ [ @@pk_fields[ self ] ]
333
+ else
334
+ xmlParser = try_load_xml_parser
335
+ if xmlParser
336
+ xmlParser.get_class_fields
337
+ else
338
+ error_msg = "Couldn't find either an XML class description file " +
339
+ "or get_class_fields method for " + self.name
340
+ raise MissingError, error_msg, caller
341
+ end
342
+ end
343
+ end
344
+
345
+ def self.get_domain_dirs #:nodoc:
346
+ if ( domainDirStr = LafcadioConfig.new['domainDirs'] )
347
+ domainDirStr.split(',')
348
+ else
349
+ []
350
+ end
351
+ end
352
+
353
+ def self.get_field( fieldName ) #:nodoc:
354
+ aDomainClass = self
355
+ field = nil
356
+ while aDomainClass < DomainObject && !field
357
+ field = aDomainClass.get_class_field( fieldName )
358
+ aDomainClass = aDomainClass.superclass
359
+ end
360
+ if field
361
+ field
362
+ else
363
+ errStr = "Couldn't find field \"#{ fieldName }\" in " +
364
+ "#{ self } domain class"
365
+ raise( MissingError, errStr, caller )
366
+ end
367
+ end
368
+
369
+ def self.get_domain_class_from_string(typeString) #:nodoc:
370
+ domain_class = nil
371
+ require_domain_file( typeString )
372
+ subclasses.each { |subclass|
373
+ domain_class = subclass if subclass.to_s == typeString
374
+ }
375
+ if domain_class
376
+ domain_class
377
+ else
378
+ raise CouldntMatchDomainClassError,
379
+ "couldn't match domain_class #{typeString}", caller
380
+ end
381
+ end
382
+
383
+ def self.inherited(subclass) #:nodoc:
384
+ @@subclassHash[subclass] = true
385
+ end
386
+
387
+ def self.is_based_on? #:nodoc:
388
+ self.superclass.is_concrete?
389
+ end
390
+
391
+ def self.is_concrete? #:nodoc:
392
+ (self != DomainObject && abstract_subclasses.index(self).nil?)
393
+ end
394
+
395
+ def self.method_missing( methodId, *args ) #:nodoc:
396
+ method_name = methodId.id2name
397
+ maybe_field_class_name = ( method_name.gsub( /^(.)/ ) { $&.upcase } ) +
398
+ 'Field'
399
+ field_class = Lafcadio.const_get( maybe_field_class_name )
400
+ create_field( field_class, args[0], args[1] || {} )
401
+ end
402
+
403
+ def self.domain_class #:nodoc:
404
+ self
405
+ end
406
+
407
+ def self.require_domain_file( typeString )
408
+ typeString =~ /([^\:]*)$/
409
+ fileName = $1
410
+ get_domain_dirs.each { |domainDir|
411
+ if Dir.entries(domainDir).index("#{fileName}.rb")
412
+ require "#{ domainDir }#{ fileName }"
413
+ end
414
+ }
415
+ if (domainFilesStr = LafcadioConfig.new['domainFiles'])
416
+ domainFilesStr.split(',').each { |domainFile|
417
+ require domainFile
418
+ }
419
+ end
420
+ end
421
+
422
+ def self.self_and_concrete_superclasses # :nodoc:
423
+ classes = [ ]
424
+ a_domain_class = self
425
+ until( a_domain_class == DomainObject ||
426
+ abstract_subclasses.index( a_domain_class ) != nil )
427
+ classes << a_domain_class
428
+ a_domain_class = a_domain_class.superclass
429
+ end
430
+ classes
431
+ end
432
+
433
+ def self.singleton_method_added( symbol )
434
+ if symbol.id2name == 'sql_primary_key_name' && self < DomainObject
435
+ begin
436
+ get_field( 'pk_id' ).db_field_name = self.send( symbol )
437
+ rescue NameError
438
+ @@sql_primary_keys[self] = self.send( symbol )
439
+ end
440
+ end
441
+ end
442
+
443
+ # Returns the name of the primary key in the database, retrieving it from
444
+ # the class definition XML if necessary.
445
+ def self.sql_primary_key_name( set_sql_primary_key_name = nil )
446
+ if set_sql_primary_key_name
447
+ get_field( 'pk_id' ).db_field_name = set_sql_primary_key_name
448
+ end
449
+ get_field( 'pk_id' ).db_field_name
450
+ end
451
+
452
+ def self.subclasses #:nodoc:
453
+ @@subclassHash.keys
454
+ end
455
+
456
+ # Returns the table name, which is assumed to be the domain class name
457
+ # pluralized, and with the first letter lowercase. A User class is
458
+ # assumed to be stored in a "users" table, while a ProductCategory class is
459
+ # assumed to be stored in a "productCategories" table.
460
+ def self.table_name( set_table_name = nil )
461
+ if set_table_name
462
+ @table_name = set_table_name
463
+ elsif @table_name
464
+ @table_name
465
+ else
466
+ xmlParser = try_load_xml_parser
467
+ if (!xmlParser.nil? && table_name = xmlParser.table_name)
468
+ table_name
469
+ else
470
+ table_name = self.basename
471
+ table_name[0] = table_name[0..0].downcase
472
+ English.plural table_name
473
+ end
474
+ end
475
+ end
476
+
477
+ def self.try_load_xml_parser
478
+ require 'lafcadio/domain'
479
+ dirName = LafcadioConfig.new['classDefinitionDir']
480
+ xmlFileName = self.basename + '.xml'
481
+ xmlPath = File.join( dirName, xmlFileName )
482
+ xml = ''
483
+ begin
484
+ File.open( xmlPath ) { |file| xml = file.readlines.join }
485
+ ClassDefinitionXmlParser.new( self, xml )
486
+ rescue Errno::ENOENT
487
+ # no xml file, so no @xmlParser
488
+ end
489
+ end
490
+
491
+ attr_accessor :error_messages, :last_commit, :fields, :fields_set
492
+ attr_reader :delete
493
+ protected :fields, :fields_set
494
+
495
+ # fieldHash should contain key-value associations for the different
496
+ # fields of this domain class. For example, instantiating a User class
497
+ # might look like:
498
+ #
499
+ # User.new( 'firstNames' => 'John', 'lastName' => 'Doe',
500
+ # 'email' => 'john.doe@email.com', 'password' => 'l33t' )
501
+ #
502
+ # In normal usage any code you write that creates a domain object will not
503
+ # define the +pk_id+ field. The system assumes that a domain object with an
504
+ # undefined +pk_id+ has yet to be inserted into the database, and when you
505
+ # commit the domain object a +pk_id+ will automatically be assigned.
506
+ #
507
+ # If you're creating mock objects for unit tests, you can explicitly set
508
+ # the +pk_id+ to represent objects that already exist in the database.
509
+ def initialize(fieldHash)
510
+ @fieldHash = fieldHash
511
+ @error_messages = []
512
+ @fields = {}
513
+ @fields_set = []
514
+ check_fields = LafcadioConfig.new()['checkFields']
515
+ verify if %w( onInstantiate onAllStates ).include?( check_fields )
516
+ end
517
+
518
+ # Returns a clone, with all of the fields copied.
519
+ def clone
520
+ copy = super
521
+ copy.fields = @fields.clone
522
+ copy.fields_set = @fields_set.clone
523
+ copy
524
+ end
525
+
526
+ # Commits this domain object to the database.
527
+ def commit
528
+ ObjectStore.get_object_store.commit self
529
+ end
530
+
531
+ # Set the delete value to true if you want this domain object to be deleted
532
+ # from the database during its next commit.
533
+ def delete=(value)
534
+ if value && !pk_id
535
+ raise "No point deleting an object that's not already in the DB"
536
+ end
537
+ @delete = value
538
+ end
539
+
540
+ def get_field( field ) #:nodoc:
541
+ unless @fields_set.include?( field )
542
+ set_field( field, @fieldHash[field.name] )
543
+ end
544
+ @fields[field.name]
545
+ end
546
+
547
+ def get_getter_field( methId ) #:nodoc:
548
+ begin
549
+ self.class.get_field( methId.id2name )
550
+ rescue MissingError
551
+ nil
552
+ end
553
+ end
554
+
555
+ def get_setter_field( methId ) #:nodoc:
556
+ if methId.id2name =~ /(.*)=$/
557
+ begin
558
+ self.class.get_field( $1 )
559
+ rescue MissingError
560
+ nil
561
+ end
562
+ else
563
+ nil
564
+ end
565
+ end
566
+
567
+ def method_missing( methId, *args ) #:nodoc:
568
+ if ( field = get_setter_field( methId ) )
569
+ set_field( field, args.first )
570
+ elsif ( field = get_getter_field( methId ) )
571
+ get_field( field )
572
+ elsif ( methId.to_s =~ /^get_/ and
573
+ ObjectStore.get_object_store.respond_to?( methId ) )
574
+ args = [ self ].concat( args )
575
+ ObjectStore.get_object_store.send( methId, *args )
576
+ else
577
+ super( methId, *args )
578
+ end
579
+ end
580
+
581
+ # Returns the subclass of DomainObject that this instance represents.
582
+ # Because of the way that proxying works, clients should call this method
583
+ # instead of Object.class.
584
+ def domain_class
585
+ self.class.domain_class
586
+ end
587
+
588
+ # This template method is called before every commit. Subclasses can
589
+ # override it to ensure code is executed before a commit.
590
+ def pre_commit_trigger
591
+ nil
592
+ end
593
+
594
+ # This template method is called after every commit. Subclasses can
595
+ # override it to ensure code is executed after a commit.
596
+ def post_commit_trigger
597
+ nil
598
+ end
599
+
600
+ def set_field( field, value ) #:nodoc:
601
+ if field.class <= LinkField
602
+ if value.class != DomainObjectProxy && value
603
+ value = DomainObjectProxy.new(value)
604
+ end
605
+ end
606
+ if ( LafcadioConfig.new()['checkFields'] == 'onAllStates' &&
607
+ !field.instance_of?( PrimaryKeyField ) )
608
+ field.verify( value, pk_id )
609
+ end
610
+ @fields[field.name] = value
611
+ @fields_set << field
612
+ end
613
+
614
+ def verify
615
+ self.class.get_class_fields.each { |field|
616
+ field.verify( self.send( field.name ), self.pk_id )
617
+ }
618
+ end
619
+ end
620
+
621
+ # Any domain class that is used mostly to map between two other domain
622
+ # classes should be a subclass of MapObject. Subclasses of MapObject should
623
+ # override MapObject.mappedTypes, returning a two-element array containing
624
+ # the domain classes that the map object maps between.
625
+ class MapObject < DomainObject
626
+ def self.other_mapped_type(firstType) #:nodoc:
627
+ types = mappedTypes
628
+ if types.index(firstType) == 0
629
+ types[1]
630
+ else
631
+ types[0]
632
+ end
633
+ end
634
+
635
+ def self.subsidiary_map #:nodoc:
636
+ nil
637
+ end
638
+ end
639
+ end