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