polleverywhere/quebert

View on GitHub
lib/quebert/async_sender/instance.rb

Summary

Maintainability
A
0 mins
Test Coverage
module Quebert
  module AsyncSender
    # Perform jobs on instances of classes
    module Instance
      class InstanceJob < Job
        def perform(klass, initialize_args, meth, *args)
          Support.constantize(klass).new(*initialize_args).send(meth, *args)
        end
      end

      def self.included(base)
        # Its not as simple as including initialize in a class, we
        # have to do some tricks to make it work so we can put the include
        # before the initialize method as opposed to after. Ah, and thanks PivotalLabs for this.
        base.send(:include, AsyncSender::Promise::DSL)
        base.send(:include, InstanceMethods)
        base.send(:extend,  ClassMethods)
        base.overwrite_initialize
        base.instance_eval do
          def method_added(name)
            return if name != :initialize
            return if @__initialize_overwritten__
            @__initialize_overwritten__ = true
            overwrite_initialize
          end
        end
      end

      module InstanceMethods
        # Build a job that uses the @__initialize_args
        def build_job(meth, *args)
          InstanceJob.new(self.class.name, @__initialize_args, meth, *args)
        end

        # Remember the args used to initialize the class so that
        # we can serialize them into a Job.
        def initialize_with_async_sender(*args)
          initialize_without_async_sender(*(@__initialize_args = args))
        end
      end
      
      module ClassMethods
        # Hack into the class initialize method so that we can grab
        # the arguments used to create an instance of the class that
        # we can serialize into a job.
        def overwrite_initialize
          class_eval do
            unless method_defined?(:initialize_with_async_sender)
              define_method(:initialize_with_async_sender) do
                initialize_without_async_sender
              end
            end
            
            if instance_method(:initialize) != instance_method(:initialize_with_async_sender)
              alias_method :initialize_without_async_sender, :initialize
              alias_method :initialize, :initialize_with_async_sender
            end
          end
        end
      end
    end
  end
end