def hook(opts = {})
      raise AlreadyHookedError, "#{base_object} method '#{method_name}' has already been hooked" if self.class.get(base_object, method_name, singleton_method)

      @hook_opts = opts
      @original_method_visibility = method_visibility_of(method_name)
      hook_opts[:visibility] ||= original_method_visibility

      if original_method_visibility || !hook_opts[:force]
        @original_method = current_method
      end

      if original_method && original_method.owner == base_object
        original_method.owner.send(:remove_method, method_name)
      end

      if singleton_method
        base_object.define_singleton_method(method_name, override_method)
      else
        base_object.define_method(method_name, override_method)
      end

      if [:public, :protected, :private].include? hook_opts[:visibility]
        method_owner.send(hook_opts[:visibility], method_name)
      end

      Agency.instance.recruit(self)
      @was_hooked = true
      self
    end
def hook(opts = {})
      raise AlreadyHookedError, "#{base_object} method '#{method_name}' has already been hooked" if self.class.get(base_object, method_name, singleton_method)

      @hook_opts = opts
      @original_method_visibility = method_visibility_of(method_name)
      hook_opts[:visibility] ||= original_method_visibility

      if original_method_visibility || !hook_opts[:force]
        @original_method = current_method
      end

      if original_method && original_method.owner == base_object
        original_method.owner.send(:remove_method, method_name)
      end

      if singleton_method
        base_object.define_singleton_method(method_name, override_method)
      else
        base_object.define_method(method_name, override_method)
      end

      if [:public, :protected, :private].include? hook_opts[:visibility]
        method_owner.send(hook_opts[:visibility], method_name)
      end

      Agency.instance.recruit(self)
      @was_hooked = true
      self
    end
set what object and method the spy should watch
 @param object
 @param method_name <Symbol>
 @param singleton_method <Boolean> spy on the singleton method or the normal method
 hooks the method into the object and stashes original method if it exists
 @param [Hash] opts what do do when hooking into a method
 @option opts [Boolean] force (false) if set to true will hook the method even if it doesn't exist
 @option opts [Symbol<:public, :protected, :private>] visibility overrides visibility with whatever method is given
 @return [self]