Account Options

  1. Sign in
The old Google Groups will be going away soon.
Switch to the new Google Groups.
Google Groups Home
« Groups Home
Making route/session/flash "globally" available
There are currently too many topics in this group that display first. To make this topic appear first, remove this option from another topic.
There was an error processing your request. Please try again.
flag
  5 messages - Collapse all  -  Translate all to Translated (View all originals)
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
Brian Carper  
View profile  
 More options Jul 8 2009, 3:40 pm
From: Brian Carper <briancar...@gmail.com>
Date: Wed, 8 Jul 2009 12:40:34 -0700 (PDT)
Local: Wed, Jul 8 2009 3:40 pm
Subject: Making route/session/flash "globally" available
I have routes that dispatch to functions which call many other
functions (often 5 or more levels deep) to dynamically generate HTML
for the layout of my site.  Some of those functions need to see the
session, so that certain bits of layout are drawn or left out
depending whether the user is logged in.

So pretty much every function needs to have the session as a
parameter, and most of them just pass it along to other functions
without doing anything with it.  I have similar needs for the params
and headers and flash. Passing all of this around to every function is
painful.

How do you deal with this?  I come from a Rails background where
params and session are always easily accessible.  I like this for
convenience even though it isn't as "functional".  I'm currently doing
something like this:

(def *REQUEST* nil)
(def *SESSION* nil)
(def *PARAMS* nil)
(def *HEADERS* nil)
(def *FLASH* nil)

(defn with-bindings [handler]
  (fn [request]
    (binding [*REQUEST* request
              *SESSION* (:session request)
              *PARAMS*  (:params request)
              *HEADERS* (:headers request)
              *FLASH*   (:flash request)]
      (handler request))))

(defn binding-routes [& handlers]
  (-> (apply routes* handlers)
      with-bindings
      with-params
      with-cookies
      with-session))

(def blog-routes
 (binding-routes
  (GET "/" (index-page))))  ; now index-page and all the functions it
calls can access thread-local copies of the session/params/etc. vars
from above

Good idea or bad idea?  Is there a better way?

Thanks
--Brian


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
James Reeves  
View profile  
 More options Jul 8 2009, 5:25 pm
From: James Reeves <weavejes...@googlemail.com>
Date: Wed, 8 Jul 2009 14:25:47 -0700 (PDT)
Local: Wed, Jul 8 2009 5:25 pm
Subject: Re: Making route/session/flash "globally" available
On Jul 8, 8:40 pm, Brian Carper <briancar...@gmail.com> wrote:

> So pretty much every function needs to have the session as a
> parameter, and most of them just pass it along to other functions
> without doing anything with it.  I have similar needs for the params
> and headers and flash. Passing all of this around to every function is
> painful.

> How do you deal with this?  I come from a Rails background where
> params and session are always easily accessible.  I like this for
> convenience even though it isn't as "functional".

This answer might be a little lengthy, so please bear with me.

In computer software, bugs are caused when a program enters an
unforeseen state. So to prevent bugs is equivalent to reducing
unpredictability.

Functional programming is a way of reducing unpredictability by
eliminating implicit state. In a pure functional language like
Haskell, functions are free of side-effects and this makes them more
predictable. For instance, a function in Haskell will always return
the same result, given the same arguments.

In a more general sense, what functional programming languages like
Haskell do is limit the flow of information: only data explicitly
passed through a functions arguments affect its results. We can
additionally constrain this data by adding strict type checking, and
Haskell provides developers with a very sophisticated type system.

Since Haskell is a very good language for reducing bugs, why do I use
Clojure?

Basically because robustness is not the only measure of a programming
language. Clojure sacrifices static typing and allows some side
effects in return for greater flexibility and conciseness. In my view,
Clojure strikes a better balance between flexibility and robustness.

Global bindings require a similar trade-off. You're trading robustness
for conciseness, but the trick is to ensure that you're getting the
right balance.

Let's now consider your application. Your views depend strongly on
whether the user is logged in or not. You probably also need
information like their username and some for of user ID. Assuming
that's all you need:

  (declare current-user)

  (defn with-user-binding [handler]
    (fn [request]
      (binding [current-user (-> request :session :user)]
        (handler request))))

  (defroutes user-routes
    ...)

  (decorate user-routes with-user-binding)

In the above case, I'm using exactly one binding, and I'm limiting it
to user-routes, which is a collection of all the routes that I know
will need the current-user binding. In the above example, I'm passing
no more information than is required to my functions.

In Compojure, you should think of routes as a gateway; something that
not only defines how browsers can access your application, but also
something that determines what information is allowed to pass through
to your internal logic.

In my opinion, a philosophy of information restriction like this
results in programs that have less bugs, are more secure, and are
easier to refactor.

- James


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Brian Carper  
View profile  
 More options Jul 8 2009, 9:12 pm
From: Brian Carper <briancar...@gmail.com>
Date: Wed, 8 Jul 2009 18:12:02 -0700 (PDT)
Local: Wed, Jul 8 2009 9:12 pm
Subject: Re: Making route/session/flash "globally" available
On Jul 8, 2:25 pm, James Reeves <weavejes...@googlemail.com> wrote:

> In Compojure, you should think of routes as a gateway; something that
> not only defines how browsers can access your application, but also
> something that determines what information is allowed to pass through
> to your internal logic.

> In my opinion, a philosophy of information restriction like this
> results in programs that have less bugs, are more secure, and are
> easier to refactor.

Thanks for your reply.  I've always viewed routes as a simple dispatch
table from request URIs to handlers.  Handlers know which bits of the
session/params/flash they need.  Making that decision at the route
level puts a lot of distance between the decision and the place the
data is actually used.  I think this makes it more difficult to
understand and refactor, in addition to tons of bookkeeping in the
routes, which is why I went with what I did.

The session is an immutable object, so I see no danger in letting
everyone have a peek.  Functions are only reading the session, not
altering it; there are no side-effects introduced.  There is the
danger of lots of functions looking into and depending on the guts of
the session, but I only access the session through accessor functions
I wrote, so I'd only need to change the accessor in that case.

I realize safety vs. convenience is an eternal debate though.  I agree
in general that restricting information is good, but I just don't see
how it's useful in this case.  If you could give a specific example of
how your version is better than mine, that'd be helpful.


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
James Reeves  
View profile  
 More options Jul 10 2009, 2:45 pm
From: James Reeves <weavejes...@googlemail.com>
Date: Fri, 10 Jul 2009 11:45:12 -0700 (PDT)
Local: Fri, Jul 10 2009 2:45 pm
Subject: Re: Making route/session/flash "globally" available
On Jul 9, 2:12 am, Brian Carper <briancar...@gmail.com> wrote:

> I realize safety vs. convenience is an eternal debate though.  I agree
> in general that restricting information is good, but I just don't see
> how it's useful in this case.  If you could give a specific example of
> how your version is better than mine, that'd be helpful.

It's unfortunately quite tricky to demonstrate safety via an example.
After a few days of thought, I was unable to come up with a concise
example that could effectively demonstrate the advantages of my
version. The best I can do is to point out that my version involves
less information being exposed to functions, which in turn means there
are less factors that could cause bugs.

Additionally, a useful side-effect of this approach is that the user
is forced to think carefully about how data is passed between
functions. If a lot of data is being shared between functions, that
might indicate your functions are too tightly coupled. By artificially
restricting the flow of data, we force a design that is very loosely
coupled. This is useful for testing, as less data needs to be mocked,
and for refactoring, as it's easier to replace a portion of
architecture if it has only a few connections to the rest of your
application.

- James


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Brian Carper  
View profile  
 More options Jul 15 2009, 1:41 pm
From: Brian Carper <briancar...@gmail.com>
Date: Wed, 15 Jul 2009 10:41:39 -0700 (PDT)
Local: Wed, Jul 15 2009 1:41 pm
Subject: Re: Making route/session/flash "globally" available
On Jul 10, 11:45 am, James Reeves <weavejes...@googlemail.com> wrote:

> It's unfortunately quite tricky to demonstrate safety via an example.
> After a few days of thought, I was unable to come up with a concise
> example that could effectively demonstrate the advantages of my
> version. The best I can do is to point out that my version involves
> less information being exposed to functions, which in turn means there
> are less factors that could cause bugs.

I've been stewing over this for a few days and I've almost convinced
myself that you're right.  Thanks for something to think about in any
case!

One definite weakness of my approach is testing or even playing around
at the REPL, because it's easy to forget to bind those vars when you
call the functions, but it's impossible to forget to pass them if
they're required, explicit function parameters.


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
End of messages
« Back to Discussions « Newer topic     Older topic »