[Cl-perec-devel] Lots of questions :-)
Mihai Bazon
mihai at bazon.net
Wed Sep 16 06:01:17 UTC 2009
Hey Pixel!
Thanks for your clarifications and also for your blog series. ;-)
The defmethod stuff works, and you guys convinced me -- it's better
than using an SQL writer.
I have one more question: is there a way to use objects without
"reviving instance"? The reason I'd do that is that there are objects
that change very infrequently, such as Users. If I get an User object
from the DB when a request comes in and I need to pass it around to
various functions, it would be nice if they don't need to do
(with-transaction (revive-instance user)) because that involves a
(mostly useless) SELECT since the data is already there.
I noticed (setf (cl-perec:persistent-p user) nil) but that feels like a
dirty hack; will this save the object back in the DB if I need to
change it?
Cheers,
-Mihai
Pixel // pinterface <pinterface at gmail.com> wrote:
> Disclaimer: It's been nearly two months since I've done anything with
> cl-perec (too many other projects, bleh). I've done some minimal testing
> of what I say below, so hopefully I'm not committing any major errors,
> but there's no guarantee. :)
>
> "Mihai Bazon" <mihai at bazon.net> wrote in
> message news:f33c75c11638fc18c81f7cb445e6924c at bazon.net...
> > Hi Attila,
> >
> > Thanks for your fast reply!
> >
> > Indeed, I knew about pinterface series, it helped a lot to get started.
>
> I'm glad to hear that.[0] Feel free to let me know if anything was unclear,
> poorly explained, or seemed to be missing. I generally know what I mean,
> but that doesn't mean anybody else does! :)
>
> > I tried defining my own type today and after some embarrassing amount of
> > trial and error I came up with this:
> >
> > (cl-perec:defptype password ()
> > '(text 128))
>
> (Aside: I somehow missed 'cl-perec:text entirely. Good to know about that.)
>
> > (cl-perec::defmapping password (cl-rdbms::sql-character-varying-type ...)
> > 'cl-perec:identity-reader ; unexported
> > 'password-writer)
> >
> > (defun password-writer (val rdbms-values index)
> > (setf (elt rdbms-values index)
> > (ironclad:byte-array-to-hex-string
> > (ironclad:digest-sequence :sha1 (babel:string-to-octets val)))))
> >
> > (pushnew 'password cl-perec::*canonical-types*)
> > (pushnew 'password cl-perec::*mapped-type-precedence-list*)
> >
> > It works, it creates SHA1 automatically for password fields, but there's
> > one weird side effect: now *all* columns of a TEXT type are
> > VARCHAR(128), and they are all mangled to SHA1. :-) Could you point out
> > what did I do wrong?
>
> The way Common Lisp types work, (subtypep 'password 'text) and (subtypep
> 'text 'password) are both true. Not coincidentally, one of the tests
> cl-perec uses to determine which reader/writer mapping to use is
> #'subtypep[1]. Since--according to the CL type hierarchy--your 'password
> type and perec's 'text types are equivalent, whichever appears in
> *mapped-type-precedence-list* first will determine behavior for both (and
> any other equivalent types).
>
> You can generally trip up #'subtypep using a satisfies test. For instance:
> (cl-perec:defptype password ()
> '(and (text 128) (satisfies constantly)))
> Will result in[2]
> (subtypep 'password 'text) => t, t
> (subtypep 'text 'password) => nil, nil
> which means so long as 'password appears in *mapped-type-precedence-list*
> /before/ 'text, the 'password reader/writer mapping will be used for
> passwords, but not text. If 'text appears before 'password, 'password will
> be run through 'text's reader/writer mapping.
>
> HOWEVER! As Attila has already mentioned, the SQL writer is the wrong place
> to encrypt passwords anyway. I /think/[3] you could do something like:
> (defmethod (setf password-of) (new-value object)
> (setf (slot-value object 'password) (encrypt-password new-value)))
> or even
> (defmethod (setf password-of) (new-value object)
> (call-next-method (encrypt-password new-value) object))
> with the caveat that you would always have to update the password using the
> accessor function, and never using slot-value or with-slots (unless you
> wanted to bypass the encryption for some reason).
>
> Attila already covered this somewhat, but it bears repeating: the SQL-reader
> and SQL-writer for a type must be the inverse of each other. In essence,
> (equivalent-p thing (sql-reader (sql-writer thing)))
> should be true (for whatever value of #'equivalent-p is relevant to your
> type). Any transformation you make in the sql-writer must be undone by the
> sql-reader.
>
> In general, the only time you should be defining your own SQL readers and
> writers is when you want to serialize an object in a funky way. E.g., if
> you want to convert objects of an 'e-mail class into strings of the form
> <local-part>@<domain.tld> for the SQL database.
>
>
> Footnotes:
> 0. Hi! I'm the guy who is writing that particular series on cl-perec. :)
> 1. see #'cl-perec::mapped-type-for in persistence/type.lisp
> 2. If you redefine the password type within the same lisp image, be sure to
> remove 'password from the cl-perec::*mapped-types* hash, otherwise
> cl-perec won't register the change in type hierarchy.[4]
> 3. "think" here meaning "Warning! I haven't bothered to check!"
> 4. If it still doesn't register the change, restart your lisp image. perec
> does a lot of aggressive internal caching and gets tripped up by changes
> a little too easily.
>
>
> Hopefully that wasn't too confusing.
>
> -pix (aka pinterface)
>
>
>
>
>
> _______________________________________________
> Cl-perec-devel mailing list
> Cl-perec-devel at common-lisp.net
> http://common-lisp.net/cgi-bin/mailman/listinfo/cl-perec-devel
More information about the cl-perec-devel
mailing list