On Sep 20, 2007, at 3:18 PM, Ezra Zygmuntowicz wrote:

>
>       Merb controllers cannot be modules because modules are not
> instantiatable. The way merb's thread safety works is by
> instantiating a new controller object for each new request/thread.
>

But under _ry's proposed system, there is still an object to  
instantiate per request--it's just the action instead of the  
controller that gets its own class.

 From a software engineering perspective, I like the direction _ry is  
going.  The question he is raising seems to be, "What is the role of  
a controller?" and on a related note, "What is the role of an action?"

I've seen very large controllers before, and they don't always make a  
lot of sense as a single class.  A typical controller will have a  
bunch of public actions followed by a bunch of protected methods.   
Except in very segmented areas of the website (e.g. an admin area),  
each protected method is generally called once by one public action-- 
there is not always a lot of re-use going on; rather, factoring these  
methods out seems to be a way of making the code in public actions  
more readable.

In typical object-oriented design, we try to create classes that map  
easily to real-world things or events.  In this case, it seems that  
an Action is a reasonable object class to consider.  As _ry has  
mentioned, Actions have:

- one or more route mappings
- security measures (e.g. you can see an object, but you have to be  
logged in to edit it)
- object state (request parameters)
- multiple formats for the response
- multiple HTTP request method options (GET/POST etc.)
- before / after filters

_ry also mentioned that we currently instantiate controllers  
primarily to call an action.  It's possible that we could get a small  
performance benefit by not having to perform a check on all of the  
controller's before / after filters to see if they apply to the  
action.  Rather, an action would just be an Action object, and it  
would make calls to other things in the initialize method (or  
'before' method, see below).  With this architecture, we could take  
advantage of inheritance in the OO way (as well as map exceptions to  
actions the OO way):

module Merb
        class Action
                def before; end
                def after; end
        end
end

class AdminAction < Merb::Action
        def before
                if User.find(session[:user_id]).admin?
                        # ok
                else
                        # Raise the Unauthorized controller's NeedToLogin 
action (class):
                        raise Unauthorized::NeedToLogin, "Please log in to the 
admin area."
                end
                super
        rescue ActiveRecord::RecordNotFound
                raise InternalServerError::StaleSession
        end
end

module Admin
        module Products
                class Show < AdminAction
                        def initialize
                                # ...
                        end

                        def before
                                # Add additional before filter code, then
                                # call AdminAction's before filter
                                super
                        end

                        def html_response
                                render
                        end

                        def after
                                # Do something else afterward
                                super
                        end
                end
        end
end

As a side-effect of not using before filters, we would be able to  
simplify the code to re-load a class in development mode (the  
complicated part comes when we have to remove constants so that  
before filters don't get double- or triple-called).

I think _ry's ideas in this area are definitely worthy of discussion.

Duane Johnson
(canadaduane)


_______________________________________________
Merb-devel mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/merb-devel

Reply via email to