| A Proposal To Magically Remove 'params' |
[Sep. 2nd, 2007|01:19 pm] |
I have a magical proposal for the anti-magic web framework.
Controller methods do not use arguments and yet expect arguments. This is handled through this params hash because we don't know in advance what parameters a client could pass to Merb. But in almost every instance, it is too much to know all the query parameters - one doesn't care if the user threw in an extraneous variable - one only cares about the variables we need to process the action.
Instead of using params we could make actions more functional by taking advantage of this cool programming concept called arguments. An example class ProductController
def show(id) # GET /products/show?id=12
@product = Product.find(id)
render
end
def index(page = 1) # GET /products?page=2
@products = Product.paginate_all(page)
render
end
endGetting a parameter list for a method is rather difficult, but using zenspider's ParseTree it is possible. Proof of concept.
ParseTree is slow, but it only needs to be run once at load - we can cache the argument lists for each method.
It is extremely magical, but the magic is limited in scope - we are not going to be using this all around the the source tree. Only for controller actions.
It does not increase the frame stack and should have zero effect on performance.
Advantages:- Less to type. No more params[:username], just username
- Cleaner API. At a glance it is visible what parameters actions take.
-
params in its current state is slightly dangerous. Clients can pass to it whatever (key, value) pairs they want and Merb will blindly symbolizes the keys. These symbols are never garbage collected.
- We can automatically raise a
BadRequest if the user does not supply all of the arguments needed.
x-posted |
|
|
| Controller Exceptions in Merb |
[Jul. 31st, 2007|07:59 pm] |
Since Merb has the lovely property of rendering the output of an action, using ruby-level exceptions to render error pages in Merb could be a cute way to approach error handling.
Suppose one has an action for editing a product which you would like to restrict to administrators: def edit
raise AdminAccessReqired unless session[:user] and session[:user].admin?
@product = Product.find(params[:id])
raise UnknownProduct if @product.nil?
render
end Where AdminAccessReqired and UnknownProduct are ControllerExceptions.
The controller exception hierarchy would be rooted with the base class, Merb::ControllerException. The Merb dispatcher would rescue any exception which was a kind_of ControllerException. Derived from ControllerException would be an exception for each of the HTTP error codes. For example: module ControllerExceptions
class Unauthorized < ControllerException
STATUS_CODE = 401
# ...
class Forbidden < ControllerException
STATUS_CODE = 403
# ...
class NotFound < ControllerException
STATUS_CODE = 404
# ... Application authors could place addition derivations into dist/app/exceptions. These must be children of an already defined Merb controller exception class. The user defined exceptions would have a method called action which acts like typical controller action. It is called to render a page when the exception is raised. For example # dist/app/exceptions/admin_access_required.rb
class AdminAccessRequired < Merb::ControllerExceptions::Unauthorized
def action
if session[:user].nil?
redirect '/login'
else
render # views/exceptions/admin_access_required.rhtml
end
end
end If the user is logged in but does not have administrative access, this will render a page describing the problem with the proper HTTP status code 401.
The UnknownProduct exception might look like this: # dist/app/exceptions/unknown_product.rb
class UnknownProduct < Merb::ControllerExceptions::NotFound
def action
@id = params[:id]
render # views/exceptions/unknown_product.rhtml
end
end If passed a bad id, the server will respond with 404 and a page that is specific to missing a product.
The advantages of this scheme are- Simplifies controller action definitions by placing exceptional logic elsewhere
- Eases the conformity to HTTP by returning proper error codes
- Could allow for before/after filters around a restricted type of exceptions (eg, for logging purposes)
- Further modularizes testing code: one needs only to check that an action raise a particular exception
The idea is from Robert Hahn
x-posted here |
|
|