[elephant-devel] Persistance and Model Validations
Ian Eslick
eslick at csail.mit.edu
Sat Sep 9 18:08:31 UTC 2006
Took me awhile to get back to this:
There are no hooks today other than in the backend, that you could wrap
around
the backend start, abort and commit transaction calls but there would be
no way
to know what is committed because the transactions are at the data
level, not the
object level.
This kind of functionality would probably be easier to add to Rucksack,
but as Robert
says you could write your own protocol and macro that leveraged the
transaction
machinery. Pardon the naming convention:
;; User IF
(defparameter *active-objects* nil)
(def-validator (type)
<validation code>)
(def-fetch (id)
<add obj ref *active-objects* if found>)
(with-validation ()
<use fetch to get object references, then modify normally>)
;; Implementing with-validation
(defmacro with-validation (() &body body)
`(let ((*active-objects* nil))
(declare (special *active-objects*))
(unwind-protect
(progn , at body)
(let ((abort-p nil))
(dolist (obj *active-objects*)
(unless (validate obj)
(setq abort-p t)))
(dolist (obj *active-objects*)
(if abort-p
(delete obj)
(save obj)))
(if abort-p
(abort-transaction)
(commit-transaction)))))
If you've defined a validator for an object, you can use with-validators
to build a transactional function that retrieves one or more objects,
does some computation and then runs validation on all fetched objects
prior to committing them. This requires that you have a way of keeping
track of what was touched (via registration on using 'fetch') so it
would have to build into your application API that required user use
that function so you can guarantee to call validate, save or delete on
all modified objects.
Following this model, a user can implement :before and :after methods
around validate, delete and save specialized on the object classes in
question and then take appropriate action. For example if you wanted to
index an object when it was saved as in Robert's example you would
(defmethod save :after ()). Because of the fine-grained, storage-level
model of data in Elephant, delete and save do not actually do the work
to revert or store the data, but can be used as a change point for user
overloading. Again, Rucksack has the same object-centric view of the
world that I think the Ruby system has so would be easier to adapt to
this worldview.
You can also use :around if you need to get at the results of calling
validate.
(FYI - the above code fragments don't handle other cases requiring
aborts (non-local flow of control, etc) nor does it account for what
happens if a user calls (abort-transaction) manually.)
Ian
Robert L. Read wrote:
> Just a quick comment on this --- Ian may have a better idea.
>
> Yes, if we offered a nice clean place to put all of those, it would
> make it obvious
> where to put validation. (I will certainly think about that in the
> future.)
>
> Here is a technique that I use, that is very effective for me, but not
> generally
> applicable: I use DCM, which is in the contrib directory and
> basically provides
> wrappers for the create/read/update/destory operations, similar to
> those you
> name below. I use :after methods on these operations (specialized on
> Director
> classes, since it is generally class dependedent) to send objects to a
> text indexer
> in a background thread whenever they change. (I was using Lucene-ws
> but now
> use montezuma; it makes sense to do these operations in a back-ground
> thread since
> a web-service call might block the thread that needs to get a response
> on to the
> glass as fast as possible.)
>
> This is an extraordinarily powerful and elegant feature of LISP. If
> you have a method
> that gives you a "change point" (I believe that is what you are asking
> for) then you can
> easily put a :before method in a completely different file that
> performs validation and
> signals an exception or takes some other action that interrupts the
> intended operation.
> One of the beauties of this is that the validation rules can be easily
> kept quite separate from the rest of the code.
>
> I suppose this same technique could be applied even if you do not have
> clearly defined
> methods; but personally I consider this clarity to be one of the
> advantages of the DCM code.
> Of course, one can more or less easily create your own validation
> points, without becoming
> dependeing on the DCM code.
>
> Here's my example code:
> (defmethod register-obj :after ((dir indexing-director) (mo
> managed-object))
> (pub-and-proc (list "indexdocument" dir (k (mid mo))))
> )
>
> (defmethod delete-obj :before ((dir indexing-director) (id key))
> (pub-and-proc (list "removedocument" dir (k id)))
> )
> "register-obj" and "delete-obj" are defined in DCM.
>
>
> On Fri, 2006-08-11 at 17:17 -0400, Daniel Salama wrote:
>> Hi,
>>
>> This may be more of a suggestion since I think I already know the
>> answer.
>>
>> I come from the Ruby On Rails world and, as some of you may know,
>> their Active Record module provides hooks that allow the automatic
>> and customized evaluation of validation rules at different stages of
>> the committing process.
>>
>> At first, I was going to as how can I hook validation rules into the
>> persistent machinery. I guess I could (should) always wrap my commits
>> in transactions and then I could perform the validation process
>> within the transaction and, therefore, I could commit or abort
>> depending on the validation results.
>>
>> However, as my Lisp knowledge is still limited, I'd like to suggest
>> for some future version of Elephant, the inclusion of some type of
>> macro that works somehow like a with-transaction type operation. This
>> macro could support generic methods that could be "customized" per
>> class just like CLOS generic methods to support model validation.
>> Just for reference, Rails validation hooks support, among others:
>>
>> - before validation
>> - after validation
>> - before create
>> - after create
>> - before save
>> - after save
>> - before destroy
>> - after destroy
>>
>> Maybe some of this functionality could be implemented in the near
>> future. Maybe it's not that big of a deal, but, since my Lisp
>> knowledge is limited, I am just throwing this suggestion.
>>
>> Then again, maybe there is a better way to do it and I just have too
>> much of a Rails mentality for the time being :)
>>
>> Thanks,
>> Daniel
>> _______________________________________________
>> elephant-devel site list
>> elephant-devel at common-lisp.net <mailto:elephant-devel at common-lisp.net>
>> http://common-lisp.net/mailman/listinfo/elephant-devel
>>
> ------------------------------------------------------------------------
>
> _______________________________________________
> elephant-devel site list
> elephant-devel at common-lisp.net
> http://common-lisp.net/mailman/listinfo/elephant-devel
More information about the elephant-devel
mailing list