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/.
>
>


-- 
http://www.politifact.com/
http://www.factcheck.org/
http://insureblog.blogspot.com/2012/08/the-myth-of-unbiased-media.html
http://www.cracked.com/article_16656_6-brainwashing-techniques-theyre-using-you-right-now.html