Class: Lafcadio::DomainObject

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

Overview

pkId and committing

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

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

However, you may want to manually set pkId 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 pkId in the database schema. If you’re dealing with a table that uses a different field name, override DomainObject.sqlPrimaryKeyName. However, you will always use pkId in your Ruby code.

Lafcadio assumes that a domain class corresponds to a table whose name is the plural of the class name, and whose first letter is lowercase. A User class is assumed to be stored in a “users” table, while a ProductCategory class is assumed to be stored in a “productCategories” table. Override DomainObject.tableName to override this behavior.

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 < DomainObject
  ...
end

class Administrator < User
  ...
end

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

Direct Known Subclasses

MapObject

Constant Summary collapse

COMMIT_ADD =
1
COMMIT_EDIT =
2
COMMIT_DELETE =
3
@@subclassHash =
{}
@@classFields =
{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from DomainComparable

#<=>, #eql?, #hash

Constructor Details

#initialize(fieldHash) ⇒ DomainObject

fieldHash 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 pkId field. The system assumes that a domain object with an undefined pkId has yet to be inserted into the database, and when you commit the domain object a pkId will automatically be assigned.

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



271
272
273
274
275
276
277
278
# File 'lib/lafcadio/domain/DomainObject.rb', line 271

def initialize(fieldHash)
	@fieldHash = fieldHash
	@pkId = fieldHash['pkId']
	@pkId = @pkId.to_i unless @pkId.nil?
	@errorMessages = []
	@fields = {}
	@fields_set = []
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(methId, *args) ⇒ Object

:nodoc:



280
281
282
283
284
285
286
287
288
# File 'lib/lafcadio/domain/DomainObject.rb', line 280

def method_missing( methId, *args ) #:nodoc:
	if ( field = get_setter_field( methId ) )
		set_field( field, args.first )
	elsif ( field = get_getter_field( methId ) )
		get_field( field )
	else
		super( methId, *args )
	end
end

Instance Attribute Details

#deleteObject

Returns the value of attribute delete.



254
255
256
# File 'lib/lafcadio/domain/DomainObject.rb', line 254

def delete
  @delete
end

#errorMessagesObject

Returns the value of attribute errorMessages.



253
254
255
# File 'lib/lafcadio/domain/DomainObject.rb', line 253

def errorMessages
  @errorMessages
end

#fields=(value) ⇒ Object

Sets the attribute fields

Parameters:

  • value

    the value to set the attribute fields to.



253
254
255
# File 'lib/lafcadio/domain/DomainObject.rb', line 253

def fields=(value)
  @fields = value
end

#fields_set=(value) ⇒ Object

Sets the attribute fields_set

Parameters:

  • value

    the value to set the attribute fields_set to.



253
254
255
# File 'lib/lafcadio/domain/DomainObject.rb', line 253

def fields_set=(value)
  @fields_set = value
end

#lastCommitObject

Returns the value of attribute lastCommit.



253
254
255
# File 'lib/lafcadio/domain/DomainObject.rb', line 253

def lastCommit
  @lastCommit
end

#pkIdObject

Returns the value of attribute pkId.



253
254
255
# File 'lib/lafcadio/domain/DomainObject.rb', line 253

def pkId
  @pkId
end

Class Method Details

.abstractSubclassesObject

:nodoc:



122
123
124
125
# File 'lib/lafcadio/domain/DomainObject.rb', line 122

def DomainObject.abstractSubclasses #:nodoc:
	require 'lafcadio/domain'
	[ MapObject ]
end

.allFieldsObject

Returns an array of all fields defined for this class and all concrete superclasses.



192
193
194
195
196
197
198
# File 'lib/lafcadio/domain/DomainObject.rb', line 192

def DomainObject.allFields
	allFields = []
	selfAndConcreteSuperclasses.each { |aClass|
		aClass.classFields.each { |field| allFields << field }
	}
	allFields
end

.classFieldsObject

:nodoc:



113
114
115
116
117
118
119
120
# File 'lib/lafcadio/domain/DomainObject.rb', line 113

def DomainObject.classFields #:nodoc:
	classFields = @@classFields[self]
	unless classFields
		@@classFields[self] = self.getClassFields
		classFields = @@classFields[self]
	end
	classFields
end

.dependentClassesObject

:nodoc:



171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/lafcadio/domain/DomainObject.rb', line 171

def DomainObject.dependentClasses #:nodoc:
	dependentClasses = {}
	DomainObject.subclasses.each { |aClass|
		if aClass != DomainObjectProxy &&
				(!DomainObject.abstractSubclasses.index(aClass))
			aClass.classFields.each { |field|
				if field.class <= LinkField && field.linkedType == self.objectType
					dependentClasses[aClass] = field
				end
			}
		end
	}
	dependentClasses
end

.getClassField(fieldName) ⇒ Object

:nodoc:



143
144
145
146
147
148
149
# File 'lib/lafcadio/domain/DomainObject.rb', line 143

def DomainObject.getClassField(fieldName) #:nodoc:
	field = nil
	self.classFields.each { |aField|
		field = aField if aField.name == fieldName
	}
	field
end

.getClassFieldByDbFieldName(fieldName) ⇒ Object

:nodoc:



151
152
153
# File 'lib/lafcadio/domain/DomainObject.rb', line 151

def DomainObject.getClassFieldByDbFieldName( fieldName ) #:nodoc:
	self.classFields.find { |field| field.dbFieldName == fieldName }
end

.getDomainDirsObject

:nodoc:



216
217
218
219
220
221
222
223
224
225
# File 'lib/lafcadio/domain/DomainObject.rb', line 216

def self.getDomainDirs #:nodoc:
	config = LafcadioConfig.new
	classPath = config['classpath']
	domainDirStr = config['domainDirs']
	if domainDirStr
		domainDirs = domainDirStr.split(',')
	else
		domainDirs = [ classPath + 'domain/' ]
	end
end

.getField(fieldName) ⇒ Object

:nodoc:



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/lafcadio/domain/DomainObject.rb', line 155

def DomainObject.getField( fieldName ) #:nodoc:
	aDomainClass = self
	field = nil
	while aDomainClass < DomainObject && !field
		field = aDomainClass.getClassField( fieldName )
		aDomainClass = aDomainClass.superclass
	end
	if field
		field
	else
		errStr = "Couldn't find field \"#{ field }\" in " +
						 "#{ self } domain class"
		raise( MissingError, errStr, caller )
	end
end

.getObjectTypeFromString(typeString) ⇒ Object

:nodoc:



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/lafcadio/domain/DomainObject.rb', line 227

def self.getObjectTypeFromString(typeString) #:nodoc:
	require 'lafcadio/objectStore/CouldntMatchObjectTypeError'
	objectType = nil
	typeString =~ /([^\:]*)$/
	fileName = $1
	getDomainDirs.each { |domainDir|
		if Dir.entries(domainDir).index("#{fileName}.rb")
			require "#{ domainDir }#{ fileName }"
		end
	}
	if (domainFilesStr = LafcadioConfig.new['domainFiles'])
		domainFilesStr.split(',').each { |domainFile|
			require domainFile
		}
	end
	subclasses.each { |subclass|
		objectType = subclass if subclass.to_s == typeString
	}
	if objectType
		objectType
	else
		raise CouldntMatchObjectTypeError,
				"couldn't match objectType #{typeString}", caller
	end
end

.inherited(subclass) ⇒ Object

:nodoc:



200
201
202
# File 'lib/lafcadio/domain/DomainObject.rb', line 200

def DomainObject.inherited(subclass) #:nodoc:
	@@subclassHash[subclass] = true
end

.isBasedOn?Boolean

:nodoc:

Returns:

  • (Boolean)


212
213
214
# File 'lib/lafcadio/domain/DomainObject.rb', line 212

def DomainObject.isBasedOn? #:nodoc:
  self.superclass.isConcrete?
end

.isConcrete?Boolean

:nodoc:

Returns:

  • (Boolean)


208
209
210
# File 'lib/lafcadio/domain/DomainObject.rb', line 208

def DomainObject.isConcrete? #:nodoc:
  (self != DomainObject && abstractSubclasses.index(self).nil?)
end

.method_missing(methodId) ⇒ Object

:nodoc:



138
139
140
141
# File 'lib/lafcadio/domain/DomainObject.rb', line 138

def DomainObject.method_missing(methodId) #:nodoc:
	require 'lafcadio/domain'
	ObjectType.getObjectType( self ).send( methodId.id2name )
end

.objectTypeObject

:nodoc:



186
187
188
# File 'lib/lafcadio/domain/DomainObject.rb', line 186

def DomainObject.objectType #:nodoc:
	self
end

.selfAndConcreteSuperclassesObject

:nodoc:



127
128
129
130
131
132
133
134
135
136
# File 'lib/lafcadio/domain/DomainObject.rb', line 127

def DomainObject.selfAndConcreteSuperclasses # :nodoc:
	classes = [ ]
	anObjectType = self
	until(anObjectType == DomainObject ||
			abstractSubclasses.index(anObjectType) != nil)
		classes << anObjectType
		anObjectType = anObjectType.superclass
	end
	classes
end

.subclassesObject

:nodoc:



204
205
206
# File 'lib/lafcadio/domain/DomainObject.rb', line 204

def DomainObject.subclasses #:nodoc:
	@@subclassHash.keys
end

Instance Method Details

#cloneObject

Returns a clone, with all of the fields copied.



362
363
364
365
366
367
# File 'lib/lafcadio/domain/DomainObject.rb', line 362

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

#commitObject

Commits this domain object to the database.



370
371
372
373
# File 'lib/lafcadio/domain/DomainObject.rb', line 370

def commit
	require 'lafcadio/objectStore/ObjectStore'
	ObjectStore.getObjectStore.commit self
end

#get_field(field) ⇒ Object

:nodoc:



310
311
312
313
314
315
# File 'lib/lafcadio/domain/DomainObject.rb', line 310

def get_field( field ) #:nodoc:
	unless @fields_set.include?( field )
		set_field( field, @fieldHash[field.name] )
	end
	@fields[field.name]
end

#get_getter_field(methId) ⇒ Object

:nodoc:



290
291
292
293
294
295
296
# File 'lib/lafcadio/domain/DomainObject.rb', line 290

def get_getter_field( methId ) #:nodoc:
	begin
		self.class.getField( methId.id2name )
	rescue MissingError
		nil
	end
end

#get_setter_field(methId) ⇒ Object

:nodoc:



298
299
300
301
302
303
304
305
306
307
308
# File 'lib/lafcadio/domain/DomainObject.rb', line 298

def get_setter_field( methId ) #:nodoc:
	if methId.id2name =~ /(.*)=$/
		begin
			self.class.getField( $1 )
		rescue MissingError
			nil
		end
	else
		nil
	end
end

#objectTypeObject

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.



330
331
332
# File 'lib/lafcadio/domain/DomainObject.rb', line 330

def objectType
	self.class.objectType
end

#postCommitTriggerObject

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



342
343
344
# File 'lib/lafcadio/domain/DomainObject.rb', line 342

def postCommitTrigger
	nil
end

#preCommitTriggerObject

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



336
337
338
# File 'lib/lafcadio/domain/DomainObject.rb', line 336

def preCommitTrigger
	nil
end

#set_field(field, value) ⇒ Object

:nodoc:



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

def set_field( field, value ) #:nodoc:
	if field.class <= LinkField
		if value.class != DomainObjectProxy && value
			value = DomainObjectProxy.new(value)
		end
	end
	@fields[field.name] = value
	@fields_set << field
end

#to_sObject

By default, to_s is considered an invalid operation for domain objects, and will raise an error. This behavior can be overridden by subclasses.



357
358
359
# File 'lib/lafcadio/domain/DomainObject.rb', line 357

def to_s
	raise "Don't make me into a string unless the type asks"
end