-
-
Save geelen/2290031 to your computer and use it in GitHub Desktop.
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
# Bad because the dependency on ModelClass and ApiClass are hard-coded | |
module HitTheApi | |
def self.go | |
ids_to_update = ModelClass.what_ids_should_I_update | |
ids_to_update.each do |id| | |
data = ApiClass.fetch_me_data(id) | |
ModelClass.persist_data(id, data) | |
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
# Cleaner 'go' method, but now the dependencies are hard-coded and pushed deeper. Worse. | |
module HitTheApi | |
def self.go | |
ids_to_update.each do |id| | |
fetch_and_persist(id) | |
end | |
end | |
private | |
def self.ids_to_update | |
ModelClass.what_ids_should_I_update | |
end | |
def self.fetch_and_persist(id) | |
data = ApiClass.fetch_me_data(id) | |
ModelClass.persist_data(id, data) | |
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
# Deps passed in makes things easy to test | |
module HitTheApi | |
def self.go(model, api) | |
ids_to_update = model.what_ids_should_I_update | |
ids_to_update.each do |id| | |
data = api.fetch_me_data(id) | |
model.persist_data(id, data) | |
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
# If the 'go' method needs to do something more complex, breaking out private methods starts to be painful | |
module HitTheApi | |
def self.go(model, api) | |
ids_to_update(model).each do |id| | |
fetch_and_persist(api, model, id) | |
end | |
end | |
private | |
def self.ids_to_update(model) | |
model.what_ids_should_I_update | |
end | |
def self.fetch_and_persist(api, model, id) | |
data = api.fetch_me_data(id) | |
model.persist_data(id, data) | |
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
# Similar to 2, but now you're instantiating something for no conceptual reason | |
# or any real benefit to the code | |
module HitTheApi | |
def initialize(model, api) | |
@model = model | |
@api = api | |
end | |
def go | |
ids_to_update = @model.what_ids_should_I_update | |
ids_to_update.each do |id| | |
data = @api.fetch_me_data(id) | |
@model.persist_data(id, data) | |
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
# Retains 2's testability but without 2a's ugly internal methods, but pushing the use of | |
# instance variables down into internal methods feels bad. | |
module HitTheApi | |
def initialize(model, api) | |
@model = model | |
@api = api | |
end | |
def go | |
ids_to_update.each do |id| | |
fetch_and_persist(id) | |
end | |
end | |
private | |
def ids_to_update | |
@model.what_ids_should_I_update | |
end | |
def fetch_and_persist(id) | |
data = @api.fetch_me_data(id) | |
@model.persist_data(id, data) | |
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
# Can stub out the dependencies, but only if you stub methods on the class under test, | |
# which I want to avoid, and doesn't let you pass other dependencies in production | |
module HitTheApi | |
def self.go | |
ids_to_update = model_class.what_ids_should_I_update | |
ids_to_update.each do |id| | |
data = api_class.fetch_me_data(id) | |
model_class.persist_data(id, data) | |
end | |
end | |
def self.model_class | |
ModelClass | |
end | |
def self.api_class | |
ApiClass | |
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
# A DSL to define the 'setter injection' pattern | |
class HitTheApi | |
dependencies model: -> { ModelClass.new }, | |
api: -> { ApiClass.new } | |
def go | |
ids_to_update.each do |id| | |
fetch_and_persist(id) | |
end | |
end | |
private | |
def ids_to_update | |
model.what_ids_should_I_update | |
end | |
def fetch_and_persist(id) | |
data = api.fetch_me_data(id) | |
model.persist_data(id, data) | |
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 Module | |
def dependencies(dep_names_to_factory_lambda) | |
dep_names_to_factory_lambda.each do |dep_name, factory_lambda| | |
# a way to inject the dependency | |
attr_writer dep_name | |
# a getter that will use the injected dependency or use the default lambda | |
# to create one and memoize it | |
define_method(dep_name) do |*args| | |
instance_variable_get("@#{dep_name}") || instance_variable_set("@#{dep_name}", factory_lambda.call(*args)) | |
end | |
end | |
end | |
end | |
# Dependencies could be injected as follows: | |
hitter = HitTheApi.new | |
hitter.model = TestModelClass.new | |
hitter.api = TestApiClass.new | |
hitter.go | |
# or, you could define something neater like | |
HitTheApi.with_dependencies(model: TestModelClass.new, api: TestApiClass.new).go | |
# with_dependencies would have to call new and set deps. Maybe that's kinda nuts. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment