phallguy/scorpion

View on GitHub
lib/scorpion/dependency.rb

Summary

Maintainability
A
25 mins
Test Coverage
A
96%
module Scorpion
  # Dependency that can be injected into a {Scorpion::Object} by a {Scorpion}.
  class Dependency

    require "scorpion/dependency/captured_dependency"
    require "scorpion/dependency/class_dependency"
    require "scorpion/dependency/module_dependency"
    require "scorpion/dependency/builder_dependency"
    require "scorpion/dependency/argument_dependency"

    # ============================================================================
    # @!group Attributes
    #

    # @!attribute
    # @return [Class,Module,Symbol] contract describing the desired behavior of the dependency.
    attr_reader :contract

    #
    # @!endgroup Attributes

    def initialize( contract )
      @contract = contract
    end

    # @return [Boolean] if the dependency satisfies the required contract.
    def satisfies?( contract )
      satisfies_contract?( contract )
    end

    # Fetch an instance of the dependency.
    # @param [Hunt] the hunting context.
    # @return [Object] the hunted dependency.
    def fetch( hunt )
      fail "Not Implemented"
    end

    # Release the dependency, freeing up any long held resources.
    def release
    end

    # Replicate the Dependency.
    # @return [Dependency] a replication of the dependency.
    def replicate
      dup
    end

    def ==( other )
      return unless other
      self.class == other.class && contract == other.contract
    end
    alias_method :eql?, :==

    def hash
      self.class.hash ^ contract.hash
    end

    def inspect
      result = "<#{ contract.inspect }"
      result << ">"
      result
    end

    private

      # @return [Boolean] true if the pray satisfies the given contract.
      def satisfies_contract?( contract )
        if self.contract.is_a?(Symbol) || contract.is_a?(Symbol)
          self.contract == contract
        else
          self.contract <= contract
        end
      end

    class << self

      # Define dependency based on the desired contract.
      # @return [Dependency] the defined dependency.
      def define( contract, options = {}, &builder )
        if options.key?( :return )
          Scorpion::Dependency::BuilderDependency.new( contract ) do
            options[:return]
          end
        elsif with = options[ :with ]
          Scorpion::Dependency::BuilderDependency.new( contract, with )
        elsif block_given?
          Scorpion::Dependency::BuilderDependency.new( contract, builder )

        # Allow a Class/Module to define a #create method that will resolve
        # and return an instance of itself. Do not automatically inherit the
        # #create method so only consider it if the owner of the method is the
        # contract itself.
        elsif contract.respond_to?( :create ) && contract.singleton_methods( false ).include?( :create )
          Scorpion::Dependency::BuilderDependency.new( contract ) do |hunt, *args, &block|
            contract.create hunt, *args, &block
          end
        else
          dependency_class( contract ).new( contract, &builder )
        end
      end

      private

        def dependency_class( contract, &builder )
          return Scorpion::Dependency::ClassDependency   if contract.is_a? Class
          return Scorpion::Dependency::ModuleDependency  if contract.is_a? Module

          raise Scorpion::BuilderRequiredError
        end
    end

  end
end