

Why Noir doesn't have controllers - Mariel
http://groups.google.com/group/clj-noir/msg/502c3c7e24f7319c?pli=1

======
jshen
My controllers are thin, but they do serve a purpose. They check if a user is
logged in, authorized to view the thing they requested, check if I need to
redirect for SEO purposes, prevent request forgery, redirecs if a form
validation doesnt pass, and many other things.

Where does he do this stuff? I'm guessing much of it is done via ring
middleware, but some of it doesn't fit there, and it doesn't fit in the view
or model either.

~~~
masklinn
> some of it doesn't fit there, and it doesn't fit in the view or model
> either.

In Django, which does not have controllers either, you'd do this via a view
decorator in the URL dispatcher: the URL dispatcher is essentially the "plugin
point" for the framework-provided controller(s)

~~~
bretthoerner
Hm? Django has controllers, they just call them views. And what Django calls
templates are typically called views in other frameworks.

~~~
parfe
[https://docs.djangoproject.com/en/dev/faq/general/#django-
ap...](https://docs.djangoproject.com/en/dev/faq/general/#django-appears-to-
be-a-mvc-framework-but-you-call-the-controller-the-view-and-the-view-the-
template-how-come-you-don-t-use-the-standard-names)

Django styles itself as MTV: Model Template View. The concept of "Controller"
within Django I would say gets implemented across a few layers (Urls,
middleware, decorators and return HttpResponse objects).

I never cared much for the distinction though as long as everyone knows what
your nouns mean for the specific project you could call them RedFish,
BlueFish, OneFish frameworks and it would work just as well.

------
airlocksoftware
Slightly off topic, but I am really enjoying working with Noir and Clojure in
general. It just feels so clean, because I'm working with all of these elegant
abstractions. I can just write the code in a way that makes sense, and then
use a macro to fill in the details of whatever technologies I'm actually
working with.

------
trb
Sounds similar to the Pyramid web framework for Python, which skips
controllers too:

[http://docs.pylonsproject.org/projects/pyramid/en/1.2-branch...](http://docs.pylonsproject.org/projects/pyramid/en/1.2-branch/)

I prefer this approach over the usual web-mvc. It's important to note that a
view is not a template, but something that generates output. In Pyramid, it's
usually a function that passes data to the template (which generates html) or
produces JSON from native types.

~~~
po
Django also calls the functions/classes where you define the logic of
selecting the models to operate on, validate, pick output, etc… a 'view'. The
template where you map the data to html is just that: a template. People
coming to Django from Rails often get confused by that. In the end, it's very
similar but with a different name.

I'm reminded of the old Microsoft Foundation Classes with their Document-
Object model that lacked a controller. Their promise was that you could reuse
views and documents but in practice the lack of a controller to handle user
input meant you never could. At least, I never could.

------
lukeholder
I have been thinking allot about this lately.

ExpressionEngine is a good example. Completely template driven yet it is
hugely popular.

modern mvc is overrated.

~~~
damncabbage
Wordpress is template-driven. It is hugely popular.

It is also a giant pain in the posterior to develop for.

(I recommend not solely using a project's popularity to judge the
effectiveness of an architecture.)

------
mark_l_watson
I like to think of Noir as being a composable framework in the sense that in
cases in Sinatra, Rails, etc., I would remove duplicated code using
before/after controllers, with Noir I write helper functions for login
checking, wrapping a partial for similar web pages, etc. I nest these function
calls as appropriate.

So there is really not as much duplication of code as you might think.

Off topic, but: one of the biggest productivity boosts with Noir (and
Compojure) is simultaneously running a repl with the code to try stuff out,
run 'lein run' with auto-reload of changed files, and a real editor like Emacs
or IntelliJ with the Clojure plugin for permanent code changes. Auto-reloading
of CSS and Javascript assets also works fine. Sweet dev setup.

------
macca321
Sounds like a Naked Objects implementation to me. The fun really starts when
you start generating your views at runtime using reflection.

------
moomin
It's worth pointing out that even if noir wasn't built explicitly to look like
Sinatra, it's built on top of compojure, which was.

~~~
weavejester
Compojure was originally built to look a lot like Sinatra, but since then the
design has diverged a fair bit.

~~~
thmzlt
Can you elaborate on this? I'm mostly curious because I have used Sinatra, and
I am starting to look at both Noir and Compojure.

~~~
weavejester
Sure thing. So superficially, Compojure and Sinatra look similar:

    
    
        get "/greet/:name" do |name|
          "Hello #{name}"
        end
    
        (GET "/greet/:name" [name]
          (str "Hello " name))
    

But these snippets of code are actually pretty different in what they do.

The Sinatra code generates a new route and adds it to an instance variable on
the current object: it's a side-effectful method.

The Compojure code returns an anonymous function; it has no inherent side
effects. If we wanted to do anything with it we'd want to bind it to a symbol:

    
    
        (def greeting
          (GET "/greet/:name" [name]
            (str "Hello " name)))
    

The other main difference is that Compojure has no implicit variables like
"params" or "request". For instance, in Sinatra we could rewrite the example
as:

    
    
        get "/greet/:name" do
          "Hello #{params[:name]}"
        end
    

But in Compojure, you don't get access to any variable you haven't explicitly
bound:

    
    
        (GET "/greet/:name" {params :params}
          (str "Hello " (params :name)))
    

So Compojure is effectively very explicit where Sinatra is implicit.

The advantage of this approach is that Compojure is (IMO at least) better at
nesting and abstracting functionality. For example:

    
    
        (context "/user/:id" [id]
          (let [user (find-user id)]
            (routes
              (GET "/history"
                (get-history user))
              (GET "/profile"
                (get-profile user)))))

------
Alexandervn
But where does a route point to? To a view?

