Created
October 9, 2012 04:56
-
-
Save xaviershay/3856696 to your computer and use it in GitHub Desktop.
What if we used dependency injection for Rails controller code?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ControllerSource | |
Response = Struct.new(:controller, :injector) do | |
def redirect_to(path, *args) | |
controller.redirect_to(controller.send("#{path}_path", *args)) | |
end | |
def render(*args) | |
ivars = {} | |
if args.last.is_a?(Hash) && args.last.has_key?(:ivars) | |
ivars = args.last.delete(:ivars) | |
end | |
ivars.each do |name, val| | |
controller.instance_variable_set("@#{name}", val) | |
end | |
controller.render *args | |
end | |
def respond_with(klass, *args) | |
obj = klass.new(*args) | |
format = controller.request.format.symbol | |
if obj.respond_to?(format) | |
injector.dispatch obj.method(format) | |
end | |
end | |
end | |
def initialize(controller) | |
@controller = controller | |
end | |
def params; @controller.params; end | |
def session; @controller.session; end | |
def flash; @controller.flash; end | |
def now_flash; @controller.flash.now; end | |
def response(injector) | |
Response.new(@controller, injector) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'ostruct' | |
class Injector | |
attr_reader :sources | |
def initialize(sources) | |
@sources = sources + [self] | |
end | |
def dispatch(method, overrides = {}) | |
args = method.parameters.map {|_, name| | |
source = ( | |
[OpenStruct.new(overrides)] + | |
sources | |
).detect {|source| source.respond_to?(name) } | |
if source | |
dispatch(source.method(name), overrides) | |
else | |
UnknownInjectable.new(name) | |
end | |
} | |
method.call(*args) | |
end | |
def injector | |
self | |
end | |
class UnknownInjectable < BasicObject | |
def initialize(name) | |
@name = name | |
end | |
def method_missing(*args) | |
::Kernel.raise "Tried to call method on an uninjected param: #{@name}" | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Controller | |
class Registration | |
def update(response, now_flash, update_form) | |
form = update_form | |
if form.save | |
response.respond_with SuccessfulUpdateResponse, form | |
else | |
now_flash[:message] = "Could not save registration." | |
response.render action: 'edit', ivars: {registration: form} | |
end | |
end | |
SuccessfulUpdateResponse = Struct.new(:form) do | |
def html(response, flash, current_event) | |
flash[:message] = "Updated details for %s" % form.name | |
response.redirect_to :registrations, current_event | |
end | |
def js(response) | |
response.render json: form | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Controller | |
class RegistrationSource | |
def current_event(params) | |
Event.find(params[:event_id]) | |
end | |
def current_registration(params, current_event) | |
current_event.registrations.find(params[:id]) | |
end | |
def current_organiser(session) | |
Organiser.find_by_id(session[:organiser_id]) | |
end | |
def authorized_organiser(current_event, current_organiser) | |
unless current_organiser && current_organiser.can_edit?(current_event) | |
raise UnauthorizedException | |
end | |
end | |
def update_form(params, current_registration) | |
Form::UpdateRegistration.build( | |
current_registration, | |
params[:registration] | |
) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'unit_helper' | |
require 'injector' | |
require 'controller/registration' | |
describe Controller::Registration do | |
success_response = Controller::Registration::SuccessfulUpdateResponse | |
let(:form) { fire_double("Form::UpdateRegistration") } | |
let(:response) { fire_double("ControllerSource::Response") } | |
let(:event) { fire_double("Event") } | |
let(:flash) { {} } | |
let(:now_flash) { {} } | |
let(:injector) { Injector.new([OpenStruct.new( | |
response: response.as_null_object, | |
current_event: event.as_null_object, | |
update_form: form.as_null_object, | |
flash: flash, | |
now_flash: now_flash | |
)]) } | |
describe '#update' do | |
it 'saves form and responds with successful update' do | |
form.should_receive(:save).and_return(true) | |
response | |
.should_receive(:respond_with) | |
.with(success_response, form) | |
injector.dispatch described_class.new.method(:update) | |
end | |
it 'render edit page when save fails' do | |
form.should_receive(:save).and_return(false) | |
response | |
.should_receive(:render) | |
.with(action: 'edit', ivars: {registration: form}) | |
injector.dispatch described_class.new.method(:update) | |
now_flash[:message].length.should > 0 | |
end | |
end | |
describe success_response do | |
describe '#html' do | |
it 'redirects to registration' do | |
response.should_receive(:redirect_to).with(:registrations, event) | |
injector.dispatch success_response.new(form).method(:html) | |
end | |
it 'includes name in flash message' do | |
form.stub(:name).and_return("Don") | |
injector.dispatch success_response.new(form).method(:html) | |
flash[:message].should include(form.name) | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class RegistrationsController < ApplicationController | |
def update | |
injector = Injector.new([ | |
ControllerSource.new(self), | |
Controller::RegistrationSource.new | |
]) | |
injector.dispatch Controller::Registration.new.method(:update) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment