こんにちは!! tahara です。 Lisp on Rails 第8回です!
今回は ActiveRecord::Base の save, create, update, destroy 等々のメソッドには beforo_* や after_* というフックメソッドを定義することができます。 ActiveRecord::Callbacks でそのあたりの実装がされています。
これを Common Lisp でやろうとした場合、
(defmethod save :before ((self post)) ...)
で OK と思ったらそうはいきません。 before_* メソッドが false を返した場合はメソッドを呼び出しを中断する必要があります。 Common Lisp の before メソッドは返り値は無視してしまうので、そのまま使うことはできないのです。
仕方ないので自分で新しいメソッドコンビネーションを実装します。
(define-method-combination active-record () ((around (:around)) (before (:before)) (primary () :required t) (after (:after))) "before メソッドが nil を返した場合メソッドの実行を中断する。" (flet ((call-methods (methods) (mapcar #'(lambda (method) `(call-method ,method)) methods)) (call-methods-and (methods) `(and ,@(mapcar #'(lambda (method) `(call-method ,method)) methods)))) (let ((form (if (or before after (rest primary)) `(when ,(call-methods-and before) (multiple-value-prog1 (call-method ,(first primary) ,(rest primary)) ,@(call-methods (reverse after)))) `(call-method ,(first primary))))) (if around `(call-method ,(first around) (,@(rest around) (make-method ,form))) form))))
あとは defgeneric するときにこのメソッドコンビネーションを指定すれば OK です。
(defgeneric save (record) (:method-combination active-record) ...)
簡単にメソッドの呼び出し方法を定義できてしまうなんて Common Lisp はいい言語ですね。
ソースはこちらから http://github.com/quek/lisp-on-rails
第9回につづきます。