# The dot operator calls methods on objects. Power Dots are dots with extra features
#
#   .? calls a method if the receiver is not nil, returns nil
#   otherwise. We have to write it ._?. in order to be valid Ruby
#
#   .try. calls a method only if the recipient responds to that method

require 'delegate'
require 'singleton'
require 'blankslate'

module HoboSupport
  def self.hobo_try(this, *args, &block)
    if args.length==0
      # Hobo style try
      CallIfAvailable.new(this)
    else
      # activesupport 2.3 style try
      this.send(:active_support_try, *args, &block)
    end
  end

  if defined? Rails
    class Railtie < Rails::Railtie
      initializer 'hobo_support.insert_into_active_record' do
        ActiveSupport.on_load(:active_record) do
          # CollectionProxy undef's most of it's methods, so put these
          # two back
          ActiveRecord::Associations::CollectionProxy.class_eval do
            def _?()
              self
            end
            def try(*args, &block)
              HoboSupport.hobo_try(self, *args, &block)
            end
          end
        end
      end
    end
  end
end

class Object

  def _?()
    self
  end

  alias_method :active_support_try, :try

  def try(*args, &block)
    HoboSupport.hobo_try(self, *args, &block)
  end

end


class NilClass
  def _?()
    SafeNil.instance
  end


  def try(*args)
    if args.length==0
      # Hobo style try
      CallIfAvailable.new(self)
    else
      # activesupport 2.3 style try
      nil
    end
  end

end


class SafeNil
  include Singleton

  begin
    old_verbose, $VERBOSE = $VERBOSE, nil   # just like Rails silence_warnings: suppress "warning: undefining `object_id' may cause serious problem"
    instance_methods.each { |m| undef_method m unless m[0,2] == '__' }
  ensure
    $VERBOSE = old_verbose
  end

  def method_missing(method, *args, &b)
    return nil
  end
end


alias DelegateClass_without_safe_nil DelegateClass
def DelegateClass(klass)
  c = DelegateClass_without_safe_nil(klass)
  c.class_eval do
    def _?
      self
    end
  end
  c
end


class CallIfAvailable < BlankSlate

  def initialize(target)
    @target = target
  end

  def method_missing(name, *args, &b)
    @target.send(name, *args, &b) if @target.respond_to?(name)
  end

end

module ActiveRecord
  module Associations
    class AssociationProxy

      # we need to make sure we don't trigger AssociationCollections' method_missing
      def try(*args, &block)
        HoboSupport.hobo_try(self, *args, &block)
      end
    end
  end

  module NamedScope
    class Scope
      # we need to make sure we don't trigger AssociationCollections' method_missing
      def try(*args, &block)
        HoboSupport.hobo_try(self, *args, &block)
      end
    end
  end

end

