-
-
Save lukeredpath/1219164 to your computer and use it in GitHub Desktop.
| class WidgetsController | |
| def index | |
| IndexedResourceCommand.new(Widget).execute(params, default_responder) | |
| end | |
| def create | |
| CreateResourceCommand.new(Widget).execute(params, default_responder) | |
| end | |
| private | |
| def default_responder | |
| # could have a different responder for different content-types (e.g. respond_to) | |
| RenderOrRedirectResponder.new(self) # needs self to get access to controller rendering behaviour | |
| end | |
| end | |
| class ResourceCommand | |
| def initialize(scope) | |
| @scope = scope | |
| end | |
| end | |
| class IndexResourceCommand | |
| def execute(params, responder) | |
| responder.respond_with_collection(@scope.all) | |
| end | |
| end | |
| class CreateResourceCommand | |
| def execute(params, responder) | |
| instance = @scope.new(params) | |
| if instance | |
| responder.respond_with_saved_object(instance) | |
| else | |
| responder.respond_with_unsaved_object(instance) | |
| end | |
| end | |
| end | |
| class RenderOrRedirectResponder | |
| def respond_with_collection(collection) | |
| # set up assigns and render the appropriate template | |
| end | |
| def respond_with_saved_object(object) | |
| @controller.redirect_to url_for(object) | |
| end | |
| def respond_with_unsaved_object(object) | |
| # set up assigns and render template with errors | |
| end | |
| end |
class ResourceCommand < Struct.new(:scope, :responder); end
class IndexResourceCommand < ResourceCommand
def execute(params)
responder.respond_with scope.all
end
end
class WidgetsController
def index
IndexResourceCommand.new(Widget, self).execute(params)
end
endLooks good, I do think this idea has some legs.
One thing I don't really like about the approach is that your creating the dependencies inline within the method. Seems like the roles of the dependencies should be figured out, and then passed into the constructor for the controller object if you were going to go with the command sort of approach. Overall though I agree that a more classic OO implementation would lead to a cleaner controller layer.
Sure, this is really just a dump of the initial ideas in my head. Although, with this approach I'd see little need to unit test the controllers and treat them as boundary objects and cover them integration tests. What would you inject (probably through accessor injection to avoid working around issues with Rails and controller initialisation).
So if I understand you right, your basically using the controller as a port where data comes in? If you were to cover them with integration tests, what would you assert on?
Well, I guess that would depend on the user stories.
I have no idea if this would actually be a good design having not explored it further, more than anything I feel that Rails controllers are probably the weakest part of an app from an OOP point of view and I'm interested in ways of investigating a better approach, whether or not this is it. :)
Actually, I'm thinking that most of the Responder stuff can be replaced with respond_with.