Class: Lafcadio::ObjectStore

Inherits:
ContextualService::Service
  • Object
show all
Defined in:
lib/lafcadio/objectStore.rb,
lib/lafcadio/mock.rb

Overview

The ObjectStore represents the database in a Lafcadio application.

Configuring the ObjectStore

The ObjectStore depends on a few values being set correctly in the LafcadioConfig file:

dbuser

The database username.

dbpassword

The database password.

dbname

The database name.

dbhost

The database host.

Instantiating ObjectStore

You can’t get an instance of ObjectStore by calling ObjectStore.new. Instead, you should call ObjectStore.get_object_store.

Dynamic method calls

ObjectStore uses reflection to provide a lot of convenience methods for querying domain objects in a number of ways.

ObjectStore#< domain class >( pk_id )

Retrieves one domain object by pk_id. For example,

ObjectStore#user( 100 )

will return User 100. Note that you can also just user DomainObject.[]:

User[100]
ObjectStore#< plural of domain class >( searchTerm, fieldName = nil )

Returns a collection of all instances of that domain class matching that search term. For example,

ObjectStore#products( a_product_category )

queries MySQL for all products that belong to that product category. You can omit fieldName if searchTerm is a non-nil domain object, and the field connecting the first domain class to the second is named after the domain class. (For example, the above line assumes that Product has a field named “product_category”.) Otherwise, it’s best to include fieldName:

ObjectStore#users( "Jones", "lastName" )

Note that these can also be accessed through DomainObject.get:

Product.get( a_product_category )
User.get( "Jones", "lastName" )

Querying

ObjectStore can also be used to generate complex, ad-hoc queries which emulate much of the functionality you’d get from writing the SQL yourself. Furthermore, these queries can be run against in-memory data stores, which is particularly useful for tests.

date = Date.new( 2003, 1, 1 )
ObjectStore#invoices { |invoice|
  invoice.date.gte( date ) & invoice.rate.equals( 10 ) &
             invoice.hours.equals( 10 )
}

is the same as

select * from invoices
where (date >= '2003-01-01' and rate = 10 and hours = 10)

Note that you can also use DomainObject.get:

Invoice.get { |invoice|
  invoice.date.gte( date ) & invoice.rate.equals( 10 ) &
             invoice.hours.equals( 10 )
}

See lafcadio/query.rb for more on the query inference syntax.

SQL Logging

Lafcadio uses log4r to log all of its SQL statements. The simplest way to turn on logging is to set the following values in the LafcadioConfig file:

logSql

Should be set to “y” to turn on logging.

logdir

The directory where log files should be written. Required if logSql is “y”

sqlLogFile

The name of the file (not including its directory) where SQL should be logged. Default is “sql”.

Triggers

Domain classes can be set to fire triggers either before or after commits. Since these triggers are executed in Ruby, they’re easy to test. See DomainObject#pre_commit_trigger and DomainObject#post_commit_trigger for more.

Direct Known Subclasses

MockObjectStore

Defined Under Namespace

Classes: Cache, CommitSqlStatementsAndBinds, DbBridge, DbConnection, MethodDispatch, MockDbBridge, RollbackError, SqlToRubyValues

Constant Summary collapse

@@db_bridge =
nil
@@db_type =
'Mysql'

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeObjectStore

:nodoc:



168
169
170
# File 'lib/lafcadio/objectStore.rb', line 168

def initialize #:nodoc:
	@cache = ObjectStore::Cache.new self.class.db_bridge
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(methodId, *args) ⇒ Object

:nodoc:



222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/lafcadio/objectStore.rb', line 222

def method_missing(methodId, *args) #:nodoc:
	if [ :commit, :flush, :last_commit_time ].include?( methodId )
		@cache.send( methodId, *args )
	else
		proc = block_given? ? ( proc { |obj| yield( obj ) } ) : nil
		dispatch = MethodDispatch.new( methodId, proc, *args )
		if dispatch.symbol
			dispatch.dispatch self
		else
			super
		end
	end
end

Class Method Details

.db_bridgeObject

Returns the DbBridge; this is useful in case you need to use raw SQL for a specific query.



155
# File 'lib/lafcadio/objectStore.rb', line 155

def self.db_bridge; @@db_bridge ||= DbBridge.new; end

.db_name=(dbName) ⇒ Object



157
158
159
# File 'lib/lafcadio/objectStore.rb', line 157

def self.db_name= (dbName)
	DbConnection.db_name= dbName
end

.db_typeObject



161
# File 'lib/lafcadio/objectStore.rb', line 161

def self.db_type; @@db_type; end

.db_type=(dbt) ⇒ Object



163
# File 'lib/lafcadio/objectStore.rb', line 163

def self.db_type=( dbt ); @@db_type = dbt; end

.mock?Boolean

Returns true if the current stored instance is a MockObjectStore.

Returns:

  • (Boolean)


166
# File 'lib/lafcadio/objectStore.rb', line 166

def self.mock?; get_object_store.mock?; end

Instance Method Details

#all(domain_class, opts = {}) ⇒ Object

Returns all domain objects for the given domain class.



173
174
175
# File 'lib/lafcadio/objectStore.rb', line 173

def all( domain_class, opts = {} )
	@cache.get_by_query( Query.new( domain_class, opts ) )
end

#db_bridgeObject

Returns the DbBridge; this is useful in case you need to use raw SQL for a specific query.



179
# File 'lib/lafcadio/objectStore.rb', line 179

def db_bridge; @cache.db_bridge; end

#get(domain_class, pk_id) ⇒ Object

Returns the domain object corresponding to the domain class and pk_id.



182
183
184
185
186
187
188
189
190
# File 'lib/lafcadio/objectStore.rb', line 182

def get( domain_class, pk_id )
	qry = Query.new( domain_class, :pk_id => pk_id )
	@cache.get_by_query( qry ).first or (
		raise(
			DomainObjectNotFoundError, "Can't find #{domain_class} #{pk_id}",
			caller
		)
	)
end

#get_map_object(domain_class, map1, map2) ⇒ Object

:nodoc:



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/lafcadio/objectStore.rb', line 192

def get_map_object( domain_class, map1, map2 ) #:nodoc:
	unless map1 && map2
		raise ArgumentError,
				"ObjectStore#get_map_object needs two non-nil keys", caller
	end
	query = Query.infer( domain_class ) { |dobj|
		dobj.send(
			map1.domain_class.basename.camel_case_to_underscore
		).equals( map1 ) &
		dobj.send(
			map2.domain_class.basename.camel_case_to_underscore
		).equals( map2 )
	}
	query( query ).first
end

#group_query(query) ⇒ Object

:nodoc:



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

def group_query( query ) #:nodoc:
	@cache.group_query( query )
end

#max(domain_class, field_name = 'pk_id') ⇒ Object

Retrieves the maximum value across all instances of one domain class.

ObjectStore#max( Client )

returns the highest pk_id in the clients table.

ObjectStore#max( Invoice, "rate" )

will return the highest rate for all invoices.



217
218
219
220
# File 'lib/lafcadio/objectStore.rb', line 217

def max( domain_class, field_name = 'pk_id' )
	qry = Query::Max.new( domain_class, field_name )
	@cache.group_query( qry ).only[:max]
end

#mock?Boolean

:nodoc:

Returns:

  • (Boolean)


236
237
238
# File 'lib/lafcadio/objectStore.rb', line 236

def mock? #:nodoc:
	false
end

#query(conditionOrQuery) ⇒ Object

Passes a query and selects with it.

qry = Query.infer( User ) { |user| user.fname.equals( 'Francis' ) }
francises = ObjectStore.get_object_store.query( qry )


243
244
245
246
247
248
249
250
251
# File 'lib/lafcadio/objectStore.rb', line 243

def query(conditionOrQuery)
	if conditionOrQuery.class <= Query::Condition
		condition = conditionOrQuery
		query = Query.new( condition.domain_class, :condition => condition )
	else
		query = conditionOrQuery
	end
	@cache.get_by_query( query )
end

#respond_to?(symbol, include_private = false) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


253
254
255
256
257
258
259
# File 'lib/lafcadio/objectStore.rb', line 253

def respond_to?( symbol, include_private = false ) #:nodoc:
	if MethodDispatch.new( symbol ).symbol
		true
	else
		super
	end
end

#transaction(&action) ⇒ Object

As long as the underlying database table sorts transactions, you can use this to run transactional logic. These transactions will auto commit at the end of the block, and can be rolled back.

ObjectStore.get_object_store.transaction do |tr|
  Client.new( 'name' => 'Big Co.' ).commit
  tr.rollback
end   # the client will not be saved to the DB


268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/lafcadio/objectStore.rb', line 268

def transaction( &action )
	old_cache = @cache
	@cache = @cache.transactional_clone
	begin
		@cache.transaction action
	rescue
		err_to_raise = $!
		@cache.rollback unless $!.is_a? RollbackError
		@cache = old_cache
		raise err_to_raise unless $!.is_a? RollbackError
	end
end