From: sera@... Date: 2005-07-05T08:35:46+09:00 Subject: Re: ANN: ContextualService 0.1.0 Sure, though I should point out, in case there's any confusion, that ContextualService doesn't require Lafcadio. Lafcadio requires ContextualService, not the other way around. I haven't used ActiveRecord myself, but I've had lots of discussions with Rails users that have informed my undertstanding of how AR works. Any corrections to what I say below are welcome. There are a handful of significant differences between AR and Lafcadio. First off is that Lafcadio currently only works with MySQL, because that's the DB I use and I haven't needed to extend it to anything else. Second, Lafcadio works harder at making applications testable in memory. For example, it includes a MockObjectStore which recreates a database entirely in memory, so that tests of DB-dependent logic can take place in memory, which will be significantly faster than tests which have to dump tables, recreate tables, fill rows, etc. I use Lafcadio at Rhizome.org, the website which is my day job. It has 500 unit tests, which run in about 120 seconds. If they hit a staging database, I imagine they would take five times as long. It's my belief that if your tests are slow, you'll run them less often, and write less of them. And programmers don't need any excuses to do less testing. This decision has a number of side-effects in the design. For one thing, you can't pass in SQL in advanced queries, because then you'd have to write a SQL parser to handle those queries with the MockObjectStore. So Lafcadio comes with a light query-inference language, inspired by Criteria but intended to be simpler than Criteria. (I find some of Criteria to be sort of scary voodoo, myself.) So, where in AR you might write: next_angle = Company.find_first "name = 'Next Angle'" In Lafcadio you'd write next_angle = object_store.get_companies { |c| c.name.equals( 'Next Angle' ) }.first .... which is more typing than in AR, but it works well in two contexts: against a real database during a production run, or against a mock database running in memory during a test. Query inference has a few other cool applications. For one thing, Lafcadio caches those query results in memory, and can do some simple calculations to see if one query is a subset of a previous one. So, if you run these three queries: object_store.get_users { |u| u.lastname.equals( 'Smith' ) } object_store.get_users { |u| Query.And( u.lastname.equals( 'Smith' ), u.fname.like( /John/ ) ) } object_store.get_users { |u| Query.And( u.lastname.equals( 'Smith' ), u.email.like( /hotmail/ ) ) } Lafcadio only hits the database the first time, and resolves the 2nd and 3rd queries in memory. Hope that answers your question, f. Dema wrote: > Could you contrast Lafcadio to ActiveRecord? > > thx > > sera@fhwang.net wrote: > > Here's another library that comes from Lafcadio: ContextualService. > > > > The ContextualService library makes it easy to manage and set services > > to a single, global resource such as a database or file system. By > > defining a service to be a child of ContextualService::Service, you > > * lock off access to instantiation, so all access have to go through > > the service's get method, > > * and provide a simple way to set the instance, most likely for > > testing. > > > > For example, Lafcadio ( http://lafcadio.rubyforge.org ) uses > > ContextualService to manage access to the ObjectStore, which controls > > access to a database. > > > > class Lafcadio::ObjectStore < ContextualService::Service > > def initialize > > ... > > end > > end > > > > ObjectStore.new will raise an error. To get an instance of ObjectStore, > > you call ObjectStore.get_object_store. If no instance has been > > previously set, this will instantiate the ObjectStore and save the > > instance for future accesses. If a mock instance has been previously > > set > > with set_object_store, it will simply return that mock instance. > > > > Since access to the global service is attached to the class, there's no > > need to pass in a mock service as an argument to any method that needs > > it. This can simplify testing considerably if you've got highly > > decomposed code that needs to access a global service from many places. > > Also, since the Context object is hidden, clients of the service don't > > have to be conscious of it. > > > > def some_method > > os = Lafcadio::ObjectStore.get_object_store > > # do something with the object store here > > some_other_method > > end > > > > def some_other_method > > os = Lafcadio::ObjectStore.get_object_store > > # do something with the object store here, too > > end > > > > class TestSomeMethod < Test::Unit::TestCase > > def test1 > > mock_object_store = Lafcadio::MockObjectStore.new > > Lafcadio::ObjectStore.set_object_store mock_object_store > > some_method > > # assert something happened > > end > > end > > > > The project page can be found at > > http://rubyforge.org/projects/contxtlservice .