Hello,
I wanted to make a proposal towards refactoring how Controllers work within Vapor. I've been working on a project that has some similarities to Vapor, and I wanted to see how my vision fit within it.
Under my system, I have calls into the router like so:
router.registerRouteWithPath("/channels/:channel", usingAction: ChannelDashboard.index)
ChannelDashboard
conforms to Controller
:
protocol Controller: class {
var request: Request { get }
init(request: Request) throws
}
It also implements the method index
, which handles the actual request.
The key difference here and between what exists in Vapor today is that the request is passed into an initializer vs each request method. This would allow people to configure a variety of things at class initialization (which the router handles via currying, and I'll get into that soon). The way I'm using it is connecting to a DB, opening other sockets, and reading some extra data that all request methods would be interested in.
I feel like this would be useful because under the current Controller
implementation you wouldn't be able to do what Rails calls a before_filter
that would be able to look at request data; it'd have to wait until after the instantiation from the router closure.
In terms of implementation with currying, after the Request
object is instantiated we can attempt try to instantiate the controller. This is what I'm doing under my system, which takes a generic type + the protocol, and records the instantiation call into a closure attached with a path:
func registerRouteWithPath<ControllerType: Controller>(path: String, usingAction action: (Controller) -> () throws -> ResponseType) {
let actionCaller = { (request: Request) throws -> ResponseType in
try action(try ControllerType(request: request))()
}
let route = Route(path: path, actionCaller: actionCaller)
routes.append((path, route))
}
This is possible because Swift creates a class method for each instance method that takes an instance of the class, i.e:
ChannelDashboard.index(ChannelDashboard) -> () -> ResponseType
That's a bit confusing, but it makes for a very simple registration API, and its something that's worked well in my use case where I don't need to worry about processing request information after the fact.
In addition, Vapor or its users could extend Controller to look at request and provide convenience methods to the contents of the Request object. I've done this with parameter values, and also added some methods for working with environment variables.
What are your thoughts on this? I'd be happy to implement them myself if there's interest.
Thanks!
~James