[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

ANN: ContextualService 0.1.0

Francis Hwang

7/4/2005 4:55:00 PM

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.rub... ) 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/cont... .

4 Answers

Dema

7/4/2005 5:31:00 PM

0

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.rub... ) 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/cont... .

Francis Hwang

7/4/2005 11:33:00 PM

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.rub... ) 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/cont... .

Jeremy Kemper

7/5/2005 2:52:00 AM

0

On Jul 4, 2005, at 4:35 PM, sera@fhwang.net wrote:
> So, where in AR you might write:
>
> next_angle = Company.find_first "name = 'Next Angle'"

The dynamic finder methods are more easily mockable. E.g.
Company.find_by_name 'Next Angle'

I run my unit tests against an in-memory SQLite database rather than
mock everything; I wish that could be improved. Tests run in under
10 sec, but that's still too long.

> 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:

Cool! This assumes transactions are serialized (no dirty reads); can
you detect whether caching is safe?

Best,
jeremy


Francis Hwang

7/5/2005 12:25:00 PM

0

Jeremy Kemper wrote:
> On Jul 4, 2005, at 4:35 PM, sera@fhwang.net wrote:
> > 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:
>
> Cool! This assumes transactions are serialized (no dirty reads); can
> you detect whether caching is safe?

No, Lafcadio doesn't support transactions, since I haven't worked on
any app where I feel it's necessary. (I would not currently recommend
that you use Lafcadio to handle, say, financial trading or medical
records.) Its understanding of caching and transactions is fairly
simple-minded, and I don't imagine it's thread-safe, either. (I don't
use concurrency, either.)

It seems there are some regular Lafcadio users besides myself, but I
still largely see it as a project driven by my own ORM needs. In
particular, I'm reluctant to introduce features that I myself never
use, because I can't then guarantee if that feature is any good.
However, I'm open to adding features such as transaction support or
Postgres support if I can be assured that somebody is going to be
testing it out on real code.

f.