Class: Lafcadio::DomainObject

Inherits:
Object
  • Object
show all
Includes:
DomainComparable
Defined in:
lib/lafcadio/domain.rb,
lib/lafcadio/test.rb

Overview

)

pk_id and committing

Lafcadio requires that each table has a numeric primary key. It assumes that this key is named pk_id in the database, though that can be overridden.

When you create a domain object by calling DomainObject.new, you should not assign a pk_id to the new instance. The pk_id will automatically be set when you commit the object by calling DomainObject#commit.

However, you may want to manually set pk_id when setting up a test case, so you can ensure that a domain object has a given primary key.

Naming assumptions, and how to override them

By default, Lafcadio assumes that every domain object is indexed by the field pk_id in the database schema. If you’re dealing with a table that uses a different field name, call DomainObject.sql_primary_key_name. However, you will always use pk_id in your Ruby code.

Lafcadio assumes that a domain class corresponds to a table whose name is the pluralized, lower-case, underscored version of the class name. A User class is assumed to be stored in a “users” table, while a ProductCategory class is assumed to be stored in a “product_categories” table. Call DomainObject.table_name to override this behavior.

class LegacyThing < Lafcadio::DomainObject
  string 'some_field'
  sql_primary_key_name 'some_legacy_id'
  table_name 'some_legacy_table'
end
thing = LegacyThing[9909]
thing.pk_id # => 9909

Inheritance

Domain classes can inherit from other domain classes; they have all the fields of any concrete superclasses plus any new fields defined for themselves. You can use normal inheritance to define this:

class User < Lafcadio::DomainObject
  ...
end

class Administrator < User
  ...
end

Lafcadio assumes that each concrete class has a corresponding table, and that each table has a pk_id field that is used to match rows between different levels.

Direct Known Subclasses

MapObject

Defined Under Namespace

Classes: ReadOnlyHash, SubclassRecord

Constant Summary collapse

@@subclass_records =
Hash.new do |h, k| h[k] = SubclassRecord.new( k ); end
@@default_mock_available =
Hash.new true
@@default_arg_directives =
Hash.new { |hash, a_class|
  default_args = {}
  a_class.all_fields.each do |field|
    default_args[field.name] = field.default_mock_value
  end
  hash[a_class] = default_args
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from DomainComparable

#<=>, #eql?, #hash

Constructor Details

#initialize(field_hash) ⇒ DomainObject

field_hash should contain key-value associations for the different fields of this domain class. For example, instantiating a User class might look like:

User.new(
  'firstNames' => 'John', 'lastName' => 'Doe',
  'email' => '[email protected]', 'password' => 'l33t'
)

In normal usage any code you write that creates a domain object will not define the pk_id field. The system assumes that a domain object with an undefined pk_id has yet to be inserted into the database, and when you commit the domain object a pk_id will automatically be assigned.

If you’re creating mock objects for unit tests, you can explicitly set the pk_id to represent objects that already exist in the database.



680
681
682
683
684
685
686
687
# File 'lib/lafcadio/domain.rb', line 680

def initialize( field_hash )
  field_hash = preprocess_field_hash field_hash
  @field_values = {}
  @fields_set = []
  reset_original_values_hash @fieldHash
  check_fields = LafcadioConfig.new()['checkFields']
  verify if %w( onInstantiate onAllStates ).include?( check_fields )
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(methId, *args) ⇒ Object

:nodoc:



736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
# File 'lib/lafcadio/domain.rb', line 736

def method_missing( methId, *args ) #:nodoc:
  if ( field = setter_field( methId ) )
    set_field_value( field, args.first )
  elsif ( field = getter_field( methId ) )
    field_value( field )
  else
    object_store = ObjectStore.get_object_store
    if object_store.respond_to? methId
      args = [ self ].concat args
      object_store.send( methId, *args )
    else
      super( methId, *args )
    end
  end
end

Instance Attribute Details

#deleteObject

Returns the value of attribute delete.



661
662
663
# File 'lib/lafcadio/domain.rb', line 661

def delete
  @delete
end

#field_values=(value) ⇒ Object

Sets the attribute field_values

Parameters:

  • value

    the value to set the attribute field_values to.



660
661
662
# File 'lib/lafcadio/domain.rb', line 660

def field_values=(value)
  @field_values = value
end

#fields_set=(value) ⇒ Object

Sets the attribute fields_set

Parameters:

  • value

    the value to set the attribute fields_set to.



660
661
662
# File 'lib/lafcadio/domain.rb', line 660

def fields_set=(value)
  @fields_set = value
end

#last_commit_typeObject

Returns the value of attribute last_commit_type.



660
661
662
# File 'lib/lafcadio/domain.rb', line 660

def last_commit_type
  @last_commit_type
end

Class Method Details

.[](pk_id) ⇒ Object

Shortcut method for retrieving one domain object.

User[7356] # => user with the pk_id 7536


288
# File 'lib/lafcadio/domain.rb', line 288

def self.[]( pk_id ); get( pk_id ); end

.abstract_subclass?(a_class) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


290
291
292
# File 'lib/lafcadio/domain.rb', line 290

def self.abstract_subclass?( a_class ) #:nodoc:
  abstract_subclasses.include? a_class
end

.abstract_subclassesObject

:nodoc:



294
295
296
# File 'lib/lafcadio/domain.rb', line 294

def self.abstract_subclasses #:nodoc:
  [ MapObject ]
end

.all(opts = {}) ⇒ Object

Returns every committed instance of the domain class.

User.all # => all users


301
302
303
# File 'lib/lafcadio/domain.rb', line 301

def self.all( opts = {} )
  ObjectStore.get_object_store.all( self, opts )
end

.all_fieldsObject

:nodoc:



305
306
307
308
309
# File 'lib/lafcadio/domain.rb', line 305

def self.all_fields # :nodoc:
  self_and_concrete_superclasses.map { |a_class|
    a_class.class_fields
  }.flatten
end

.class_field(fieldName) ⇒ Object

:nodoc:



311
312
313
# File 'lib/lafcadio/domain.rb', line 311

def self.class_field(fieldName) #:nodoc:
  self.class_fields.find { |field| field.name == fieldName.to_s }
end

.class_fieldsObject

:nodoc:



315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/lafcadio/domain.rb', line 315

def self.class_fields #:nodoc:
  unless subclass_record.fields
    subclass_record.fields = self.get_class_fields
    subclass_record.fields.each do |class_field|
      begin
        undef_method class_field.name.to_sym
      rescue NameError
        # not defined globally or in an included Module, skip it
      end
    end
  end
  subclass_record.fields
end

.commit_mock(args, caller = nil) ⇒ Object

:nodoc:



133
134
135
136
137
138
139
140
141
142
# File 'lib/lafcadio/test.rb', line 133

def self.commit_mock( args, caller = nil ) #:nodoc:
  dobj = self.new( args )
  link_fields = all_fields.select { |field| field.is_a? DomainObjectField }
  link_fields.each do |field|
    val = dobj.send( field.name )
    maybe_call_default_mock( field, caller ) if ( val and val.pk_id == 1 )
  end
  dobj.commit
  dobj
end

.create_field(field_class, *args) ⇒ Object

:nodoc:



329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/lafcadio/domain.rb', line 329

def self.create_field( field_class, *args ) #:nodoc:
  subclass_record.maybe_init_fields
  att_hash = create_field_att_hash( field_class, *args )
  field = field_class.create_with_args( self, att_hash )
  unless class_field( field.name )
    att_hash.each { |field_name, value|
      setter = field_name + '='
      field.send( setter, value ) if field.respond_to?( setter )
    }
    class_fields << field
  end
end

.create_field_att_hash(field_class, *args) ⇒ Object

:nodoc:



342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/lafcadio/domain.rb', line 342

def self.create_field_att_hash( field_class, *args ) #:nodoc:
  att_hash = args.last
  unless att_hash.is_a? Hash
    att_hash = subclass_record.default_field_setup_hash[field_class].clone
  end
  if field_class == DomainObjectField
    att_hash['linked_type'] = args.first
    att_hash['name'] = args[1] if args[1] and !args[1].is_a? Hash
  else
    att_hash['name'] = args.first.to_s
  end
  att_hash
end

.create_fields(field_class, *args) ⇒ Object

:nodoc:



356
357
358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/lafcadio/domain.rb', line 356

def self.create_fields( field_class, *args ) #:nodoc:
  arg = args.shift
  until args.empty?
    next_arg = args.shift
    if next_arg.is_a? String or next_arg.is_a? Symbol
      create_field( field_class, arg )
      arg = next_arg
    else
      create_field( field_class, arg, next_arg )
      arg = args.shift
    end
  end
  create_field( field_class, arg ) unless arg.nil?
end

.custom_mock(custom_args = nil) ⇒ Object

Commits and returns a custom mock object of the given domain class. All the field values are set to defaults, except for the fields passed in through custom_args. This mock object will have a pk_id greater than 1, and each successive call to DomainObject.custom_mock will return an object with a unique pk_id.

This class method is only visible if you include lafcadio/test.rb.

class User < Lafcadio::DomainObject
  strings :fname, :lname, :email
end
u1 = User.custom_mock
u1.fname # => 'test text'
u1.lname # => 'test text'
u1.email # => 'test text'
u1.pk_id # => probably 2, guaranteed to be greater than 1
u2 = User.custom_mock( 'fname' => 'Francis', 'lname' => 'Hwang' )
u2.fname # => 'Francis'
u2.lname # => 'Hwang'
u2.email # => 'test text'
u2.pk_id # => probably 3, guaranteed to not be u1.pk_id and to be
         #    greater than 1


167
168
169
170
171
172
173
174
175
176
177
# File 'lib/lafcadio/test.rb', line 167

def self.custom_mock( custom_args = nil )
  dobj_args = default_args
  object_store = ObjectStore.get_object_store
  dbb = object_store.db_bridge
  dbb.set_next_pk_id( self, 2 ) if dbb.next_pk_id( self ) == 1
  dobj_args['pk_id'] = nil
  if custom_args.is_a? Hash
    custom_args.each do |k, v| dobj_args[k.to_s] = v; end
  end
  commit_mock( dobj_args )
end

.default_argsObject

Returns a hash of default arguments for mock instances of this domain class. DomainObject.default_mock uses exactly these arguments to create the default mock for a given domain class, and DomainObject.custom_mock overrides some of the field arguments based on its custom arguments.

By default this will retrieve simple values based on the field type:

  • BooleanField: true

  • DateField: Date.today

  • DateTimeField: Time.now

  • DomainObjectField: The instance of the domain class with pk_id 1

  • EmailField: “[email protected]

  • FloatField: 0.0

  • IntegerField: 1

  • StringField: “test text”

  • TextListField: [ ‘a’, ‘b’, ‘c’ ]

You can override this method, if you like. However, it will probably be simpler just to call DomainObject.mock_value.

This class method is only visible if you include lafcadio/test.rb.



200
201
202
203
204
205
206
207
208
209
210
# File 'lib/lafcadio/test.rb', line 200

def self.default_args
  default_args = {}
  @@default_arg_directives[self].each do |name, value_or_proc|
    if value_or_proc.is_a? Proc
      default_args[name] = value_or_proc.call
    else
      default_args[name] = value_or_proc
    end
  end
  default_args
end

.default_field_setup_hash(field_class, hash) ⇒ Object

Sets a default setup hash for a field of a certain class. Useful for mapping domain classes with lots of fields and unusual field configurations.

class LotsOfBooleans < Lafcadio::DomainObject
  default_field_setup_hash(
    Lafcadio::BooleanField, { 'enum_type' => :capital_yes_no }
  )
  booleans 'this', 'that', 'the_other', 'and_another_one',
           'this_one_too'
end


382
383
384
# File 'lib/lafcadio/domain.rb', line 382

def self.default_field_setup_hash( field_class, hash )
  subclass_record.default_field_setup_hash[field_class] = hash
end

.default_mock(calling_class = nil) ⇒ Object

Commits and returns a mock object of the given domain class. All the field values are set to defaults. This mock object will have a pk_id of 1. Successive calls to DomainObject.default_mock will always return the same mock object.

This class method is only visible if you include lafcadio/test.rb.

class User < Lafcadio::DomainObject
  strings :fname, :lname, :email
end
u1 = User.default_mock
u1.fname # => 'test text'
u1.lname # => 'test text'
u1.email # => 'test text'
u1.pk_id # => 1


228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/lafcadio/test.rb', line 228

def self.default_mock( calling_class = nil )
  if @@default_mock_available[self]
    begin
      dobj = ObjectStore.get_object_store.get( self, 1 )
      dobj
    rescue DomainObjectNotFoundError
      dbb = ObjectStore.get_object_store.db_bridge
      dbb.set_next_pk_id( self, 1 ) if dbb.next_pk_id( self ) > 1
      commit_mock( default_args, calling_class )
    end
  else
    raise( TypeError, self.name + ".default_mock not allowed", caller )
  end
end

.default_mock_available(is_avail) ⇒ Object

:nodoc:



243
244
245
# File 'lib/lafcadio/test.rb', line 243

def self.default_mock_available( is_avail ) #:nodoc:
  @@default_mock_available[self] = is_avail
end

.dependent_classesObject

:nodoc:



386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/lafcadio/domain.rb', line 386

def self.dependent_classes #:nodoc:
  dependent_classes = {}
  DomainObject.subclasses.each { |aClass|
    if (
      !abstract_subclass?( aClass ) and
      ( field = aClass.link_field( self ) )
    )
      dependent_classes[aClass] = field
    end
  }
  dependent_classes
end

.domain_classObject

:nodoc:



399
400
401
# File 'lib/lafcadio/domain.rb', line 399

def self.domain_class #:nodoc:
  self
end

.domain_dirsObject

:nodoc:



403
404
405
406
407
408
409
# File 'lib/lafcadio/domain.rb', line 403

def self.domain_dirs #:nodoc:
  if ( domainDirStr = LafcadioConfig.new['domainDirs'] )
    domainDirStr.split(',')
  else
    []
  end
end

.exist?(search_term, field_name = :pk_id) ⇒ Boolean

Tests whether a given domain object exists.

User.exist?( 8280 ) # => returns true iff there's a User 8280
User.exist?( 'Hwang', :lastName ) # => returns true iff there's a User
                                  #    with the last name 'Hwang'

Returns:

  • (Boolean)


416
417
418
419
420
421
# File 'lib/lafcadio/domain.rb', line 416

def self.exist?( search_term, field_name = :pk_id )
  query = Query.infer( self ) { |dobj|
    dobj.send( field_name ).equals( search_term )
  }
  !ObjectStore.get_object_store.query( query ).empty?
end

.field(fieldName) ⇒ Object

:nodoc:



423
424
425
426
427
428
429
430
431
# File 'lib/lafcadio/domain.rb', line 423

def self.field( fieldName ) #:nodoc:
  aDomainClass = self
  field = nil
  while aDomainClass < DomainObject && !field
    field = aDomainClass.class_field( fieldName )
    aDomainClass = aDomainClass.superclass
  end
  field
end

.firstObject

Returns the first domain object it can find.

very_first_user = User.first


436
# File 'lib/lafcadio/domain.rb', line 436

def self.first; all.first; end

.get(*args) ⇒ Object

This class method has a few uses, depending on how you use it.

  1. Pass DomainObject.get a block in order to run a complex query:

    adult_hwangs = User.get { |user|
      user.lastName.equals( 'Hwang' ) &
        user.birthday.lte( Date.today - ( 365 * 18 ) )
    }
    

    See query.rb for more information about how to form these queries.

  2. Pass it a simple search term and a field name to retrieve every domain object matching the term. The field name will default to pk_id.

    hwangs = User.get( 'Hwang', :lastName )
    user123 = User.get( 123 ).first
    
  3. Pass it a { :group => :count } hash to retrieve a count result hash. This interface is fairly new and may be refined (that is, changed) in the future.

    num_users = User.get( :group => :count ).first[:count]
    


454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
# File 'lib/lafcadio/domain.rb', line 454

def self.get( *args )
  if block_given?
    query = Query.infer( self ) { |dobj| yield( dobj ) }
    ObjectStore.get_object_store.query( query )
  elsif args.size == 1
    arg = args.first
    if arg.is_a? Fixnum
      ObjectStore.get_object_store.get( self, *args )
    else
      qry = Query.new( self, :group_functions => [ :count ] )
      ObjectStore.get_object_store.group_query qry
    end
  else
    search_term = args.shift
    field_name = (
      args.shift or link_field( search_term.domain_class ).name
    )
    qry = Query::Equals.new( field_name, search_term, self )
    ObjectStore.get_object_store.query qry
  end
end

.get_class_fieldsObject

Returns an Array of ObjectField instances for this domain class, parsing them from XML if necessary. You can override this to define a domain class’ fields, though it will probably just be simpler to use one-line class methods instead.



480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
# File 'lib/lafcadio/domain.rb', line 480

def self.get_class_fields
  if self.methods( false ).include?( 'get_class_fields' )
    [ subclass_record.pk_field ]
  elsif abstract_subclass?( self )
    []
  else
    xmlParser = try_load_xml_parser
    if xmlParser
      xmlParser.get_class_fields     
    else
      error_msg = "Couldn't find either an XML class description file " +
                  "or get_class_fields method for " + self.name
      raise MissingError, error_msg, caller
    end
  end
end

.is_child_domain_class?Boolean

:nodoc:

Returns:

  • (Boolean)


497
498
499
# File 'lib/lafcadio/domain.rb', line 497

def self.is_child_domain_class? #:nodoc:
  superclass != DomainObject and !abstract_subclass?( superclass )
end

.lastObject

Returns the last domain object.

last_msg = Message.last


504
# File 'lib/lafcadio/domain.rb', line 504

def self.last; all.last; end

:nodoc:



506
507
508
509
510
511
# File 'lib/lafcadio/domain.rb', line 506

def self.link_field( linked_domain_class ) # :nodoc:
  class_fields.find { |field|
    field.is_a? DomainObjectField and
    field.linked_type == linked_domain_class
  }
end

.maybe_call_default_mock(field, caller) ⇒ Object

:nodoc:



247
248
249
250
251
252
253
254
255
256
# File 'lib/lafcadio/test.rb', line 247

def self.maybe_call_default_mock( field, caller ) #:nodoc:
  linked_type = field.linked_type
  begin
    ObjectStore.get_object_store.get( linked_type, 1 )
  rescue DomainObjectNotFoundError
    unless linked_type == caller
      linked_type.send( 'default_mock', self )
    end
  end
end

.method_missing(methodId, *args) ⇒ Object

:nodoc:



513
514
515
516
517
518
519
520
521
522
523
524
525
526
# File 'lib/lafcadio/domain.rb', line 513

def self.method_missing( methodId, *args ) #:nodoc:
  dispatched = false
  method_name = methodId.id2name
  begin
    method_missing_try_create_field( method_name, *args )
    dispatched = true
  rescue NameError
    begin
      method_missing_try_create_fields( method_name, *args )
      dispatched = true
    rescue NameError; end
  end
  super unless dispatched
end

.method_missing_try_create_field(method_name, *args) ⇒ Object

:nodoc:



528
529
530
531
532
# File 'lib/lafcadio/domain.rb', line 528

def self.method_missing_try_create_field( method_name, *args ) # :nodoc:
  maybe_field_class_name = method_name.underscore_to_camel_case + 'Field'
  field_class = Lafcadio.const_get( maybe_field_class_name )
  create_field( field_class, *args )
end

.method_missing_try_create_fields(method_name, *args) ⇒ Object

:nodoc:



534
535
536
537
538
539
540
541
542
543
# File 'lib/lafcadio/domain.rb', line 534

def self.method_missing_try_create_fields( method_name, *args ) # :nodoc:
  singular = method_name.singular
  if singular
    maybe_field_class_name = singular.underscore_to_camel_case + 'Field'
    field_class = Lafcadio.const_get( maybe_field_class_name )
    create_fields( field_class, *args )
  else
    raise NameError
  end
end

.mock_value(field_sym, value) ⇒ Object

Sets the mock value for the given field. These mock values are used in DomainObject.default_mock and DomainObject.custom_mock

This class method is only visible if you include lafcadio/test.rb.

class User < Lafcadio::DomainObject
  strings :fname, :lname, :email
end
User.mock_value :fname, 'Bill'
User.mock_value :lname, 'Smith'
u1 = User.default_mock
u1.fname # => 'Bill'
u1.lname # => 'Smith'
u1.email # => 'test text'
u1.pk_id # => 1


274
275
276
# File 'lib/lafcadio/test.rb', line 274

def self.mock_value( field_sym, value )
  @@default_arg_directives[self][field_sym.id2name] = value
end

.mock_values(hash) ⇒ Object

Sets the mock value for the fields in hash. These mock values are used in DomainObject.default_mock and DomainObject.custom_mock

This class method is only visible if you include lafcadio/test.rb.

class User < Lafcadio::DomainObject
  strings :fname, :lname, :email
end
User.mock_values { :fname => 'Bill', :lname => 'Smith' }
u1 = User.default_mock
u1.fname # => 'Bill'
u1.lname # => 'Smith'
u1.email # => 'test text'
u1.pk_id # => 1


293
294
295
# File 'lib/lafcadio/test.rb', line 293

def self.mock_values( hash )
  hash.each do |field_sym, value| mock_value( field_sym, value ); end
end

.onlyObject

Returns the only committed instance of the domain class. Will raise an IndexError if there are 0, or more than 1, domain objects.

the_one_user = User.only


549
# File 'lib/lafcadio/domain.rb', line 549

def self.only; all.only; end

.postgres_pk_id_seqObject



551
552
553
# File 'lib/lafcadio/domain.rb', line 551

def self.postgres_pk_id_seq
  "#{ table_name }_#{ sql_primary_key_name }_seq"
end

.require_domain_file(typeString) ⇒ Object

:nodoc:



555
556
557
558
559
560
561
562
563
564
565
566
567
# File 'lib/lafcadio/domain.rb', line 555

def self.require_domain_file( typeString ) # :nodoc:
  typeString =~ /([^\:]*)$/
  fileName = $1
  domain_dirs.each { |domainDir|
    if Dir.entries(domainDir).index("#{fileName}.rb")
      require "#{ domainDir }#{ fileName }"
    end
  }
  if (domainFiles = LafcadioConfig.new['domainFiles'])
    domainFiles = domainFiles.split( ',' ) if domainFiles.is_a? String
    domainFiles.each { |domainFile| require domainFile }
  end
end

.self_and_concrete_superclassesObject

:nodoc:



569
570
571
572
573
574
575
576
577
578
579
# File 'lib/lafcadio/domain.rb', line 569

def self.self_and_concrete_superclasses # :nodoc:
  classes = [ ]
  a_domain_class = self
  until(
    a_domain_class == DomainObject || abstract_subclass?( a_domain_class )
  )
    classes << a_domain_class
    a_domain_class = a_domain_class.superclass
  end
  classes
end

.singleton_method_added(symbol) ⇒ Object

:nodoc:



581
582
583
584
585
586
587
588
589
# File 'lib/lafcadio/domain.rb', line 581

def self.singleton_method_added( symbol ) # :nodoc:
  if symbol.id2name == 'sql_primary_key_name' && self < DomainObject
    begin
      field( 'pk_id' ).db_field_name = self.send( symbol )
    rescue NameError
      subclass_record.sql_primary_key = self.send( symbol )
    end
  end
end

.sql_primary_key_name(set_db_field_name = nil) ⇒ Object

If set_db_field_name is nil, this will return the sql name of the primary key. If set_db_field_name isn’t nil, it will set the sql name.

class User < Lafcadio::DomainObject
  string 'firstNames'
end
User.sql_primary_key_name # => 'pk_id'
class User < Lafcadio::DomainObject
  sql_primary_key_name 'some_other_id'
end
User.sql_primary_key_name # => 'some_other_id'


602
603
604
605
# File 'lib/lafcadio/domain.rb', line 602

def self.sql_primary_key_name( set_db_field_name = nil )
  field( 'pk_id' ).db_field_name = set_db_field_name if set_db_field_name
  field( 'pk_id' ).db_field_name
end

.subclass_recordObject

:nodoc:



607
608
609
# File 'lib/lafcadio/domain.rb', line 607

def self.subclass_record # :nodoc:
  @@subclass_records.synchronize { @@subclass_records[self] }
end

.subclassesObject

:nodoc:



611
612
613
# File 'lib/lafcadio/domain.rb', line 611

def self.subclasses #:nodoc:
  @@subclass_records.keys
end

.table_name(set_table_name = nil) ⇒ Object

If set_table_name is nil, DomainObject.table_name will return the table name. Lafcadio assumes that a domain class corresponds to a table whose name is the pluralized, lower-case, underscored version of the class name. A User class is assumed to be stored in a “users” table, while a ProductCategory class is assumed to be stored in a “product_categories” table.

If set_table_name is not nil, this will set the table name.

class User < Lafcadio::DomainObject
  string 'firstNames'
end
User.table_name # => 'users'
class User < Lafcadio::DomainObject
  table_name 'some_table'
end
User.table_name # => 'some_table'


632
633
634
635
636
637
638
639
640
641
642
643
644
645
# File 'lib/lafcadio/domain.rb', line 632

def self.table_name( set_table_name = nil )
  if set_table_name
    @table_name = set_table_name
  elsif @table_name
    @table_name
  else
    xmlParser = try_load_xml_parser
    if (!xmlParser.nil? && table_name = xmlParser.table_name)
      table_name
    else
      self.basename.camel_case_to_underscore.plural
    end
  end
end

.try_load_xml_parserObject

:nodoc:



647
648
649
650
651
652
653
654
655
656
657
658
# File 'lib/lafcadio/domain.rb', line 647

def self.try_load_xml_parser # :nodoc:
  if ( dirName = LafcadioConfig.new['classDefinitionDir'] )
    xmlFileName = self.basename + '.xml'
    xmlPath = File.join( dirName, xmlFileName )
    begin
      xml = File.open( xmlPath ) do |f| f.gets( nil ); end
      ClassDefinitionXmlParser.new( self, xml )
    rescue Errno::ENOENT
      # no xml file, so no @xmlParser
    end
  end
end

Instance Method Details

#cloneObject

Returns a clone, with all of the fields copied.



690
691
692
693
694
695
# File 'lib/lafcadio/domain.rb', line 690

def clone
  copy = super
  copy.field_values = @field_values.clone
  copy.fields_set = @fields_set.clone
  copy
end

#commitObject

Commits this domain object to the database.



698
699
700
# File 'lib/lafcadio/domain.rb', line 698

def commit
  ObjectStore.get_object_store.commit self
end

#delete!Object

Deletes a domain object, committing the delete to the database immediately.



713
714
715
716
# File 'lib/lafcadio/domain.rb', line 713

def delete!
  self.delete = true
  commit
end

#domain_classObject

Returns the subclass of DomainObject that this instance represents. Because of the way that proxying works, clients should call this method instead of Object.class.



721
722
723
# File 'lib/lafcadio/domain.rb', line 721

def domain_class
  self.class.domain_class
end

#field_value(field) ⇒ Object

:nodoc:



725
726
727
728
729
730
# File 'lib/lafcadio/domain.rb', line 725

def field_value( field ) #:nodoc:
  unless @fields_set.include?( field )
    set_field_value( field, @fieldHash[field.name] )
  end
  @field_values[field.name]
end

#getter_field(methId) ⇒ Object

:nodoc:



732
733
734
# File 'lib/lafcadio/domain.rb', line 732

def getter_field( methId ) #:nodoc:
  self.class.field methId.id2name
end

#post_commit_triggerObject

This template method is called after every commit. Subclasses can override it to ensure code is executed after a commit.



774
775
776
# File 'lib/lafcadio/domain.rb', line 774

def post_commit_trigger
  nil
end

#pre_commit_triggerObject

This template method is called before every commit. Subclasses can override it to ensure code is executed before a commit.



754
755
756
# File 'lib/lafcadio/domain.rb', line 754

def pre_commit_trigger
  nil
end

#preprocess_field_hash(fieldHash) ⇒ Object

:nodoc:



758
759
760
761
762
763
764
765
766
767
768
769
770
# File 'lib/lafcadio/domain.rb', line 758

def preprocess_field_hash( fieldHash ) # :nodoc:
  if fieldHash.is_a? Hash
    fieldHash.keys.each { |key|
      if self.class.field( key.to_s ).nil?
        raise ArgumentError, "Invalid field name #{ key.to_s }"
      end
    }
    @fieldHash = {}
    fieldHash.each do |k, v| @fieldHash[k.to_s] = v; end
  else
    @fieldHash = fieldHash
  end
end

#reset_original_values_hash(f = @field_values) ⇒ Object

:nodoc:



778
779
780
# File 'lib/lafcadio/domain.rb', line 778

def reset_original_values_hash( f = @field_values ) #:nodoc:
  @original_values = ReadOnlyHash.new( f.clone )
end

#set_field_value(field, value) ⇒ Object

:nodoc:



782
783
784
785
786
787
788
789
790
791
792
793
794
795
# File 'lib/lafcadio/domain.rb', line 782

def set_field_value( field, value ) #:nodoc:
  if (
    field.is_a?( DomainObjectField ) and
    !value.is_a?( DomainObjectProxy ) and value
  )
    value = DomainObjectProxy.new(value)
  end
  if ( LafcadioConfig.new()['checkFields'] == 'onAllStates' &&
       !field.instance_of?( PrimaryKeyField ) )
    field.verify( value, pk_id )
  end
  @field_values[field.name] = value
  @fields_set << field
end

#setter_field(methId) ⇒ Object

:nodoc:



797
798
799
800
801
802
803
# File 'lib/lafcadio/domain.rb', line 797

def setter_field( methId ) #:nodoc:
  if methId.id2name =~ /(.*)=$/
    self.class.field $1
  else
    nil
  end
end

#update!(changes) ⇒ Object

Updates a domain object and commits the changes to the database immediately.

user99 = User[99]
user99.update!( :firstNames => 'Bill', :password => 'n3wp4ssw0rd' )


810
811
812
813
# File 'lib/lafcadio/domain.rb', line 810

def update!( changes )
  changes.each do |sym, value| self.send( sym.to_s + '=', value ); end
  commit
end

#verifyObject

If you’re running against a MockObjectStore, this will verify each field and raise an error if there’s any invalid fields.



817
818
819
820
821
822
823
# File 'lib/lafcadio/domain.rb', line 817

def verify
  if ObjectStore.mock?
    self.class.get_class_fields.each { |field|
      field.verify( self.send( field.name ), self.pk_id )
    }
  end
end