brettweavnet/outliers

View on GitHub
lib/outliers/collection.rb

Summary

Maintainability
A
3 hrs
Test Coverage
require 'enumerator'

module Outliers
  class Collection

    include Enumerable
    include Outliers::Verifications::Shared::Collection

    attr_reader :provider
    attr_accessor :targets

    def self.to_human
      (self.to_s.split('::') - ['Outliers', 'Resources']).map { |p| p.underscore }.join('_').downcase.gsub(/_collection$/, '')
    end

    def self.verifications
       Outliers::Verifications::Shared::Collection.verifications + self.resource_class.verifications 
    end

    def self.filters
      []
    end

    def self.resource_class
      array = self.to_s.gsub(/Collection$/, '').split('::')
      array.inject(Object) {|o,c| o.const_get c}
    end

    def to_s
      self.class.to_human
    end

    def initialize(provider)
      @targets   = []
      @provider = provider
      @logger   = Outliers.logger
    end

    def each &block  
      list.each do |resource|
        block.call resource
      end  
    end

    def exclude_by_key(exclusions)
      save = list.reject {|u| exclusions.include? u.public_send key}
      @list = save
    end

    def filter(action, args)
      name  = args.keys.first
      value = args.fetch name

      unless self.public_methods.include? "filter_#{name}".to_sym
        raise Exceptions::UnknownFilter.new "Unknown filter '#{name}'."
      end

      filtered_list = self.public_send "filter_#{name}", value

      case action
      when 'include'
        logger.info "Including resources filtered by '#{name}' with value '#{value}'."
        logger.warn "No resources match filter." unless (filtered_list & @list).any?
        @list = filtered_list & @list
      when 'exclude'
        logger.info "Excluding resources filtered by '#{name}' with value '#{value}'."
        @list -= filtered_list
      else
        raise Exceptions::UnknownFilterAction.new "Filters must be either 'include' or 'exclude'."
      end
    end

    def verify(name, arguments=nil)
      logger.debug "Verifying '#{name}'."

      name += "?" unless name =~ /^.*\?$/

      set_target_resources name if targets.any?

      logger.debug "Target resources '#{list_by_key.join(', ')}'."

      unless verification_exists? name
        raise Exceptions::UnknownVerification.new "Unkown verification '#{name}'."
      end

      if collection_verification? name
        send_collection_verification name, arguments
      else
        send_resources_verification name, arguments
      end
    end

    def list
      @list ||= load_all
    end

    def key
      resource_class.key
    end

    def resource_class
      self.class.resource_class
    end

    private

    def verification_exists?(name)
      m = resource_class.instance_methods - resource_class.class.instance_methods
      m += Outliers::Verifications::Shared::Collection.instance_methods
      m -= [:source, :id, :method_missing]
      m.include? name.to_sym
    end

    def list_by_key
      list.map {|r| r.public_send key}
    end

    def connect
      @provider.connect
    end

    def logger
      @logger
    end

    def collection_verification?(name)
      self.public_methods.include? name.to_sym
    end

    def set_target_resources(verification)
      logger.debug "Setting target resource(s) to '#{targets.join(', ')}'."

      @list = list.select {|r| targets.include? r.id }

      unless list.any?
        raise Outliers::Exceptions::TargetNotFound.new "No resources found matching one or more of '#{targets}'."
      end

      @list
    end

    def send_resources_verification(verification, arguments)
      failing_resources = reject do |resource|
        r = send_verification resource, verification, arguments
        logger.debug "Verification of resource '#{resource.id}' #{r ? 'passed' : 'failed'}."
        r
      end
      passing_resources = list - failing_resources
      resources = []
      resources += passing_resources.map { |r| { id: r.id, status: 0 } }
      resources += failing_resources.map { |r| { id: r.id, status: 1 } }
      { resources: resources, passing: failing_resources.none? }
    end

    def send_collection_verification(verification, arguments)
      send_verification self, verification, arguments
    end

    def send_verification(object, verification, arguments) 
      if object.method(verification).arity.zero?
        unless arguments.nil?
          raise Outliers::Exceptions::NoArgumentRequired.new "Verification '#{verification}' does not require an arguments."
        end
        object.public_send verification
      else
        if arguments.nil?
          raise Outliers::Exceptions::ArgumentRequired.new "Verification '#{verification}' requires arguments."
        end
        object.public_send verification, arguments
      end
    end

  end
end