Skip to content

Instantly share code, notes, and snippets.

@davetron5000
Created June 11, 2024 12:57
Show Gist options
  • Save davetron5000/19aa850df641be6c334e9b64a944b6c8 to your computer and use it in GitHub Desktop.
Save davetron5000/19aa850df641be6c334e9b64a944b6c8 to your computer and use it in GitHub Desktop.
# Does something. Instances of this class can
# only be used once. The instance contains all
# the info about the execution. Repeated
# calls to do_it should in theory not be
# allowed and/or not be supported
class DoesSomething
def initialize(some,args)
@some = some
@args = args
end
def do_it
# ...
@competed = ...
@validation_errors = ...
end
def completed? = @completed
def validation_errors = @validation_errors
end
# Does something. Instances of this class are stateless and
# can be re-used any number of times, producing a fresh
# DoesSomethingResult each time
class DoesSomething
def do_it(some,args)
# some logic
DoesSomethingResult.new(...)
end
end
# Class describing all possible outcomes of DoesSomething's do_it method
class DoesSomethingResult
attr_reader :validation_errors
def initialize(completed,validation_errors=[])
@completed = completed
@validation_errors = validation_errors
end
def completed? = @completed
end
@bkuhlmann
Copy link

Hey. 👋

In both of these scenarios, why not use functional composition? The Pipeable gem is great at making this possible which means you can swap or mix-n-match procs, lambdas, methods, and/or classes (via the Command Pattern, that is). This would also free you up to construct classes with dependencies injected (i.e. object composition) for different behavior while essentially remaining stateless (although you could have some state within your classes as long as the state is only isolated to the class so that it remains encapsulated).

@davetron5000
Copy link
Author

I find that style confusing and somewhat convoluted. It puts a lot of layers of abstraction around calling methods. I had a really hard time figuring out what the Pipeable example is doing, since none of the methods that need to be called are called in the code. It just seems way overcomplicated for what is just a few lines:

class Demo
  def parse(data)
    if data ~= /Book.+Price/
      return
    end
    CSV.instance(data, headers: true).each.to_a.map { |row|
      "#{ row['Book'] }: #{ row['Price'] }"
    }
  end
end

It's also very non-idiomatic for Ruby, and I think there's value in not steering away from common language idioms.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment