Proposed API change for respond_to

Duane Johnson canadaduane at gmail.com
Fri Sep 21 16:22:48 EDT 2007


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)




More information about the Merb-devel mailing list