Last active
June 12, 2017 00:10
-
-
Save kellysutton/4df83e15940a7f428c9d3bf0be3f7867 to your computer and use it in GitHub Desktop.
Replace Side Effects with Return Values
This file contains hidden or 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 PayrollRunner | |
def run! | |
TaxEngineWrapper.new(tax_engine).apply(commands) | |
computed_taxes = tax_engine.calculate_taxes | |
payroll.assign_taxes(computed_taxes) | |
end | |
# This is now a pure function from the view of its callers. | |
# Sweet! | |
def commands | |
tax_engine_spy = TaxEngineSpy.new | |
# Commands are recorded against our tax_engine_spy, | |
# which pretends to be a perfectly normal `tax_engine`. | |
TaxEngineSetter::Factory.for(state).apply_state_info( | |
tax_engine_spy, | |
build_state_info() | |
) | |
[SetFederalInfoCommand.new(build_federal_info())] + | |
tax_engine_spy.generated_commands | |
end | |
end | |
class TaxEngineSpy | |
def initialize | |
@commands = [] | |
end | |
# We did not end up using `method_missing` in on our code, | |
# but rather listening for each specific method. | |
# `method_missing` is difficult to test and didn’t provide | |
# the level of safety we were looking for. | |
def method_missing(name, *args) | |
@commands << Command.new(name, *args) | |
end | |
def generated_commands | |
@commands | |
end | |
end |
This file contains hidden or 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 PayrollRunner | |
def run! | |
federal_info = build_federal_info() | |
state_info = build_state_info() | |
tax_engine.set_federal_info(federal_info) | |
tax_engine.set_state_info(state_info) | |
computed_taxes = tax_engine.calculate_taxes | |
payroll.assign_taxes(computed_taxes) | |
end | |
end |
This file contains hidden or 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 PayrollRunner | |
def run! | |
federal_info = build_federal_info() | |
state_info = build_state_info() | |
commands = [ | |
SetFederalInfoCommand.new(federal_info), | |
SetStateInfoCommand.new(state_info), | |
] | |
TaxEngineWrapper.new(tax_engine).apply(commands) | |
computed_taxes = tax_engine.calculate_taxes | |
payroll.assign_taxes(computed_taxes) | |
end | |
end | |
# This wrapper is designed to not change the API of something | |
# we don't control (the original tax engine), but allows | |
# us to apply our custom commands as normal method calls. | |
class TaxEngineWrapper | |
def initialize(tax_engine) | |
@tax_engine = tax_engine | |
end | |
def apply(commands) | |
commands.each do |command| | |
@tax_engine.public_send( | |
command.method_name, *command.arguments | |
) | |
end | |
end | |
end |
This file contains hidden or 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 PayrollRunner | |
def run! | |
federal_info = build_federal_info() | |
state_info = build_state_info() | |
tax_engine.set_federal_info(federal_info) | |
# With 50 states plus Washington, D.C., there are a lot of | |
# classes on the other end of this factory. | |
# | |
# All of them expect to be interacting with the `tax_engine` | |
# in an imperative fashion. | |
TaxEngineSetter::Factory.for(state).apply_state_info( | |
tax_engine, | |
state_info | |
) | |
computed_taxes = tax_engine.calculate_taxes | |
payroll.assign_taxes(computed_taxes) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment