Migrating to Compojure 0.4.0 and Ring 0.2.0

117 views
Skip to first unread message

Brenton

unread,
Apr 1, 2010, 3:27:42 PM4/1/10
to Compojure
I just finished migrating a small application from 0.3.2 to 0.4.0 and
Ring 0.2.0.

In an effort to help others, I have written a blog post with my notes
on some of the main differences and some problems that I ran into. I
hope this is helpful. If you do read it and find that I am wrong on
some point, please let me know and I will fix my post.

http://formpluslogic.blogspot.com/2010/04/migrating-from-compojure-032-to-040-and_01.html

Thanks to James and Mark for all the great work.

Brenton

Nick Dimiduk

unread,
Apr 1, 2010, 3:44:46 PM4/1/10
to comp...@googlegroups.com
Thanks for the tips! Nice summary :)


--
You received this message because you are subscribed to the Google Groups "Compojure" group.
To post to this group, send email to comp...@googlegroups.com.
To unsubscribe from this group, send email to compojure+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/compojure?hl=en.


James Reeves

unread,
Apr 1, 2010, 4:41:14 PM4/1/10
to Compojure
Nice work! Just a few bleeding-edge updates:

1. Ring is now out of RC, so the Clojars version to use is "0.2.0"

2. As well as 'map' bindings, Compojure has some special syntax for
handling parameters when passed a vector. Each symbol in the vector is
mapped to the parameter of the same name. If an "& z" form is
provided, z is assigned to a map of every parameter that hasn't
already been mapped. In other words, this:

(GET "/foo" [x y & z]
(pr-str x y z))

Is equivalent to:

(GET "/foo" request
(let [x (get-in request [:params "x"]
y (get-in request [:params "y"]
z (dissoc (request :params) "x" "y")]
(pr-str x y z)))

3. I've also just recently added wrap! to take the place of decorate.
For an extra bit of conciseness, you can use a keyword :foo, to refer
to a function wrap-foo. The namespace is also accounted for, so :bar/
foo will map to bar/wrap-foo.

(wrap! your-routes
:multipart-params
(:session cookie-store))

This bank holiday weekend I'm going to be documenting all this in the
Compojure Wiki.

Also please note that 0.4.0 is still in development, so just because a
function is missing now, doesn't mean it'll be missing at release
(which shouldn't be sometime in April). I plan to add a "serve-file"
function back in under the compojure.response namespace.

- James

On Apr 1, 8:27 pm, Brenton <bashw...@gmail.com> wrote:
> I just finished migrating a small application from 0.3.2 to 0.4.0 and
> Ring 0.2.0.
>
> In an effort to help others, I have written a blog post with my notes
> on some of the main differences and some problems that I ran into. I
> hope this is helpful. If you do read it and find that I am wrong on
> some point, please let me know and I will fix my post.
>

> http://formpluslogic.blogspot.com/2010/04/migrating-from-compojure-03...

James Reeves

unread,
Apr 1, 2010, 4:42:48 PM4/1/10
to Compojure
On Apr 1, 9:41 pm, James Reeves <weavejes...@googlemail.com> wrote:
> (which shouldn't be sometime in April)

Uh, I mean it *should* be released sometime in April. :)

- James

Rick Mouritzen

unread,
Apr 1, 2010, 6:09:46 PM4/1/10
to comp...@googlegroups.com
Is there a story / approach for form params and form validation for 0.4.0? Or do we roll our own?

James Reeves

unread,
Apr 2, 2010, 10:31:26 AM4/2/10
to Compojure
On Apr 1, 11:09 pm, Rick Mouritzen <rickm...@gmail.com> wrote:
> Is there a story / approach for form params and form validation for 0.4.0?
> Or do we roll our own?

For validation, you'll have to roll your own. The old
compojure.validation library was in need of an overhaul, and was
geared around compojure.html. Because Compojure no longer has a
standard HTML-generating library, any future validation system would
need to be written to be more generic.

I'm not certain what you mean by 'form params'. Compojure uses Ring to
parse cookies and urlencoded parameters by default, so this should
work:

(defroutes example
(GET "/" []
(html
(form-to [:post "/greet"]
(text-field :name)
(submit-button "Send"))))
(POST "/greet" [name]
(html
[:h1 "Hello " name])))

- James

Vagif Verdi

unread,
Apr 2, 2010, 11:32:39 PM4/2/10
to Compojure
On Apr 1, 1:41 pm, James Reeves <weavejes...@googlemail.com> wrote:
> Also please note that 0.4.0 is still in development, so just because a
> function is missing now, doesn't mean it'll be missing at release
> (which should be sometime in April). I plan to add a "serve-file"

> function back in under the compojure.response namespace.
>
> - James

I attempted a dry run port of our web site to 0.4, just to see how
much work would it be.
I found a few things missing: session flash support, crypto and
encodings are gone.
Could you please clarify what are the plans about those things ?

Vagif Verdi

unread,
Apr 3, 2010, 12:28:44 AM4/3/10
to Compojure
On Apr 1, 1:41 pm, James Reeves <weavejes...@googlemail.com> wrote:
> Compojure has some special syntax for
> handling parameters when passed a vector. Each symbol in the vector is
> mapped to the parameter of the same name. If an "& z" form is
> provided, z is assigned to a map of every parameter that hasn't
> already been mapped. In other words, this:
>
>   (GET "/foo" [x y & z]
>     (pr-str x y z))
>

Correct me if i'm wrong, but it looks to me that routes synthax makes
a developer to chose between access to session or destructuring
params.

(GET "/foo" [x y & z]

...)

(GET "/bar" {params :params session :session}
...)

And since most of the web pages would need to look into session,
parameter destructuring will be of very limited use.

Am i missing something here ?

James Reeves

unread,
Apr 3, 2010, 10:40:15 AM4/3/10
to Compojure
On Apr 3, 4:32 am, Vagif Verdi <vagif.ve...@gmail.com> wrote:
> I attempted a dry run port of our web site to 0.4, just to see how
> much work would it be.
> I found a few things missing: session flash support, crypto and
> encodings are gone.
> Could you please clarify what are the plans about those things ?

Session flash support is probably something that should be added to
Ring. It may be that the 0.4.0 release doesn't have a session flash
middleware included, but I'll see it's included eventually, in either
Ring or Compojure. I'll keep you updated on this, if you want.

Regarding crypto, I wasn't aware anyone was using the library
directly. I mostly put it in for cookie sessions, which are now
covered by ring.middleware.session.cookie. May I ask what parts of the
crypto library you use?

ring.util.codec has encodings for base64 and urlencoded strings. Is
that sufficient for your needs?

- James

James Reeves

unread,
Apr 3, 2010, 10:53:07 AM4/3/10
to Compojure
On Apr 3, 5:28 am, Vagif Verdi <vagif.ve...@gmail.com> wrote:
> Correct me if i'm wrong, but it looks to me that routes synthax makes
> a developer to chose between access to session or destructuring
> params.

Well, you can nest destructuring:

(GET "/bar" {{x :x, :y y} :params, session :session}
...)

But yes, using the vector form for parameters means you can't
destructure the session as well.

> And since most of the web pages would need to look into session,
> parameter destructuring will be of very limited use.
>
> Am i missing something here ?

My feeling is that if most of the pages need to look at the session,
perhaps that indicates a binding would be useful. For example:

(declare *user*)

(defn find-post [id]
(db-find :posts {:id id, :user-id (*user* :id)}))

(defn wrap-user-binding [handler]
(ANY "*" {session :session, :as request}
(binding [*user* (session :user)]
(handler request))))

(defroutes my-routes
(GET "/post/:id" [id]
(find-post id)))

(wrap! my-routes :user-binding)

What's your opinion on this approach?

- James

Brenton

unread,
Apr 3, 2010, 12:47:16 PM4/3/10
to Compojure
James,

I like this approach. I think it is good that Ring and Compojure
support a functional style of session management as the default. For
simple cases this is the best way to go. But sometimes you need to do
a lot of work in the session and the functional style can seem like it
is getting in your way. What you have shown here is one good way to
get around this. I am currently working on a general purpose wrapper
that will take this one step further. It puts the session map in an
atom as well as providing functions for updating the session. When the
handler returns it will dump the data in the atom into the response.
There are many situations where this makes your life much easier. The
trick is to get this working so that it does not interfere with the
"functional" session data.

Here is the code.

http://github.com/brentonashworth/sandbar/blob/session/src/sandbar/stateful_session.clj

I am literally working on this now and so this with change and the
final version will end up in the master branch. This also contains my
poor man's flash implementation since that is currently missing.

http://github.com/brentonashworth/sandbar/blob/master/src/sandbar/stateful_session.clj

If anyone would like to help me think of a better way to deal with
this then I am open to suggestions. As of now I am putting the
"stateful" session data in a sub-map within the session so that
session keys will not collide with any that may be returned in the
request.

I wonder how many of you think this is a good idea. If so, maybe we
should have something like this included in Ring or Compojure so that
we have a standard (and better implemented) way of doing this. What do
think? It might be good to have a standard so that we can build
libraries on top if it.

One more thing. Coming from a Java background, I am used to the
session being managed by the application server. I may be wrong but I
think this is also what Compojure 0.3.2 was doing. Ring provides its
own session management and you can easily create your own backing
store implementation. This means that when the time comes to run
multiple front end app servers, you will be able to implement a
session store that will scale. Over time the community can build up
many different backing stores that will work in any situation. What
this means is that putting stuff in the session does not have to mean
accumulating evil server side state. I much prefer putting transient
"session" data in the session as opposed to mixing it in with the rest
of your data.

Brenton

Vagif Verdi

unread,
Apr 3, 2010, 2:48:59 PM4/3/10
to Compojure
On Apr 3, 7:53 am, James Reeves <weavejes...@googlemail.com> wrote:
> Well, you can nest destructuring:
>
>   (GET "/bar" {{x :x, :y y} :params, session :session}
>     ...)
>

Thanks, this is cool.

> My feeling is that if most of the pages need to look at the session,
> perhaps that indicates a binding would be useful. For example:
>
>   (declare *user*)
>
>   (defn find-post [id]
>     (db-find :posts {:id id, :user-id (*user* :id)}))
>
>   (defn wrap-user-binding [handler]
>     (ANY "*" {session :session, :as request}
>       (binding [*user* (session :user)]
>         (handler request))))
>
>   (defroutes my-routes
>     (GET "/post/:id" [id]
>       (find-post id)))
>
>   (wrap! my-routes :user-binding)
>
> What's your opinion on this approach?
>
> - James

Thank you James. This is exactly the approach that fits my
application.

In fact session is implemented in Ring as a optional module, which is
good.
I'm going to combine it with stateful-session module from Brenton's
sample application and completely get rid of session in request-
response map.

I do not like the idea that you can do one thing two different ways.
Leaves too much space for errors and bugs.

I think all the friction between functional style and session handling
happens because of the breaking separation of concerns.

Request-Response pair is a short lived object that is created at the
beginning of processing and is destroyed at the end.
Session on the other hand is a long lived data storage that spans a
lifetime of an application.

Trying to maintain both of them in one data structure is what makes it
awkward (for me).

> Regarding crypto, I wasn't aware anyone was using the library directly.

No problem, it is very easy to extract and use it as a separate
module, which i already did for my purposes.

Reply all
Reply to author
Forward
0 new messages