Tom:
I agree mostly with you. There is nothing stopping you from injecting
outside dependencies on initialization and defaulting to the expected
dependencies; in fact, if we had stuck to that in our Rails app codebase
from the beginning, then our unit tests might have actually run quickly
from the get-go.
I think that a lot of Ruby/Rails devs are only first starting to run up
against large monolithic codebases with a massive coupling problem which is
creating negative side effects around such things as test run time and
maintainability, and are (re)discovering these principles that Java devs
may have already known about for some time.
So please, bring your (well-argued) opinion here! That said...
Another possible pattern I have been toying around with which could satisfy
your requirement (without requiring a possibly lengthy/ugly/disturbing
argument list to the initialization routine) is to define a few slightly
clever instance methods as proxies. Another advantage of this approach is
that you can specify all your class' exterior dependencies right up front
at the top of the class definition, so if you are getting a little too
coupled for your own good, it will be obvious:
class SchedulerJob
# BEGIN exterior dependencies
def request_schedule_class
defined?(super) ? super : RequestSchedule
end
def request_to_queue_pusher_class
defined?(super) ? super : RequestToQueuePusher
end
# END exterior dependencies
def run
expired_requests = request_schedule_class.new.fetch_all_expired
request_to_queue_pusher_class.new.enqueue(expired_requests)
end
end
Then in your unit test or what have you, you define a module like so
module CouplingIsForPornStubs
def request_schedule_class
my_tests_fake_schedule_class # or Class.new, or mock, or what have you
end
def request_to_queue_pusher_class
raise "come on, this is too much coupling, it's like a bukkake"
end
end
And then later on in the actual test case you just do
sj_under_test = SchedulerJob.new
sj_under_test.extend CouplingIsForPornStubs
assert_raise { sj_under_test.run }
And now your class instance under test will magically defer to your
module's version of the methods (or classes) first, then to its own.
I have some code that cleans this up and patterns it out, but I haven't put
it anywhere (where "anywhere" == "github") yet. 
-Peter
···
On Wed, Mar 6, 2013 at 4:45 PM, Tom Kos <lists@ruby-forum.com> wrote:
Thanks, this is more or less how I'd do that. I'd maybe just add default
arguments' values like:
class SchedulerJob
def initialize(schedule = RequestSchedule.new, pusher =
RequestToQueuePusher.new)
@schedule = schedule
@pusher = pusher
end
...
end
I'm far from stating that rubyists avoid SOLID principles, but it's hard
to spot codebases with dependencies injected on regular basis.
The only question remaining is that even using dependency injection you
still need to "new" your dependencies somewhere. In Java you got things
like Spring or Guice (frameworks for wiring up all stuff together). But
I guess in ruby it may be easily done with default arguments doing "new"
implicitly. Classes are still coupled but I think it may be bit better
than "new" in methods.
--
Posted via http://www.ruby-forum.com/\.
--