Project

wry_inject

0.0
No commit activity in last 3 years
No release in over 3 years
Dependency Injection that allows you to generate pre-configured classes
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

Wry Inject allows dependency injection without taking over the initialize method.

Dependency injection allows us to avoid tight coupling in our projects. Instead of your class referencing a dependency directly (eg. a repository, payment gateway, etc.) these dependencies are passed in by a caller.

This allows tests to use a mock repository so it doesn't hit the db. Or a stubbed payment gateway so the class under test doesn't make calls to external systems. It also allows for much simpler testing (without 'allows_any_instance_of' calls).

This amount of configurability is great! But we want to avoid forcing callers to configure our class before they use it. Most callers will be happy with the default options.

This gem aims to provide dependency injection functionality with the least amount of "getting in the way".

Usage

include WryInject in your class to add a class_with class method. This can be used to inject dependencies into your class and returns a new child class.

require 'wry_inject'

class Order
  include WryInject

  def initialize(items)
    @items = items
  end

  def total
    @items.sum { |item| pricing_service.price_for(item) }
  end
end

klass = Order.class_with(pricing_service: PricingService.new)
klass.new([item1, item2]).total

You can also have default values, as seen below

class Order
  include WryInject

  wry_defaults pricing_service: PricingService.new, logger: Logger.new

  def initialize(items)
    @items = items
  end

  def total
    logger.log("retrieving total")
    @items.sum { |item| pricing_service.price_for(item) }
  end
end

Order.class_with_defaults.new([item1]).total
Order.class_with(pricing_service: MockPricingService.new).new([item1]).total

You can also add a namespace so the injected variables don't polute the namespace. As you can see in the above examples, dependencies can get lost in all your other methods. This allows more visibility about what the dependencies are.

class Order
  include WryInject
  wry_namespace :wry
  wry_defaults pricing_service: PricingService.new, logger: Logger.new

  def initialize(items)
    @items = items
  end

  def total
    wry.logger.log("retrieving total")
    @items.sum { |item| wry.pricing_service.price_for(item) }
  end
end

Order.class_with_defaults.new([item1]).total
Order.class_with(pricing_service: MockPricingService.new).new([item1]).total