こんにちは!! tahara です。 Lisp on Rails 第2回です!
ActiveRecord::Base ではクラスメソッドとして find や all の検索メソッドが定義されています。
Common Lisp でそれらを実装するにあたり、メタクラスのメソッドとして実装してみます。 Common Lisp では自分でメタクラスを定義することができます。 次のようにメタクラスを定義します。 なんとなく、テーブル名とカラム情報を持たせています。
(defclass active-record-class (standard-class) ((%table-name :initarg :%table-name :accessor %table-name-of) (%columns :initarg :%columns :accessor %columns-of)))
各テーブル毎のクラスはこの active-record-class のインスタンスになり、 インスタンス変数としてテーブル名とカラム情報を持つイメージです。
メタクラスに全レコードを取得する all メソッドを実装します。
(defmethod all ((class active-record-class)) (multiple-value-bind (rows columns) (clsql-sys:query (format nil "select * from ~a" (%table-name-of class))) (loop for i in rows collect (make-instance-from-row class i columns))))
(def-record post)
は次のように展開されて
:METACLASS ACTIVE-RECORD::ACTIVE-RECORD-CLASS
を指定します。
(PROGN (DEFPARAMETER POST (DEFCLASS POST (BASE) ((ID :INITARG :ID :INITFORM NIL :ACCESSOR ID-OF) (NAME :INITARG :NAME :INITFORM NIL :ACCESSOR NAME-OF) (TITLE :INITARG :TITLE :INITFORM NIL :ACCESSOR TITLE-OF) (CONTENT :INITARG :CONTENT :INITFORM NIL :ACCESSOR CONTENT-OF) (CREATED-AT :INITARG :CREATED-AT :INITFORM NIL :ACCESSOR CREATED-AT-OF) (UPDATED-AT :INITARG :UPDATED-AT :INITFORM NIL :ACCESSOR UPDATED-AT-OF)) (:METACLASS ACTIVE-RECORD::ACTIVE-RECORD-CLASS))) ...
Ruby のクラス定義と違って Common Lisp のクラス定義ではクラス名に何も束縛しないため、
わざわざ defparameter
しています。
そんな感じでメタクラスによる
(all post)
ができました。
ソースはこちらから http://github.com/quek/lisp-on-rails
第3回につづきます