module Backports

def self.alias_method(mod, new_name, old_name)

Safe alias_method that will only alias if the source exists and destination doesn't
def self.alias_method(mod, new_name, old_name)
  mod.instance_eval do
    alias_method new_name, old_name
  end if mod.method_defined?(old_name) && !mod.method_defined?(new_name)
end

def self.alias_method_chain(mod, target, feature)

(from Rails)
Modified to avoid polluting Module if so desired
def self.alias_method_chain(mod, target, feature)
  mod.class_eval do
    # Strip out punctuation on predicates or bang methods since
    # e.g. target?_without_feature is not a valid method name.
    aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
    yield(aliased_target, punctuation) if block_given?
    with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
    alias_method without_method, target
    alias_method target, with_method
    case
    when public_method_defined?(without_method)
      public target
    when protected_method_defined?(without_method)
      protected target
    when private_method_defined?(without_method)
      private target
    end
  end
end

def self.coerce_to(obj, cls, meth)

(from Rubinius)
is not of the right class.
Raises a TypeError if the coercion fails or the returned value
Helper method to coerce a value into a specific class.
def self.coerce_to(obj, cls, meth)
  return obj if obj.kind_of?(cls)
  begin
    ret = obj.__send__(meth)
  rescue Exception => e
    raise TypeError, "Coercion error: #{obj.inspect}.#{meth} => #{cls} failed:\n" \
                     "(#{e.message})"
  end
  raise TypeError, "Coercion error: obj.#{meth} did NOT return a #{cls} (was #{ret.class})" unless ret.kind_of? cls
  ret
end

def self.coerce_to_ary(obj)

def self.coerce_to_ary(obj)
  coerce_to(obj, Array, :to_ary)
end

def self.coerce_to_comparison(a, b, cmp = (a <=> b))

(from Rubinius)
Additionally, it maps any negative value to -1 and any positive value to +1
Checks for a failed comparison (in which case it throws an ArgumentError)
def self.coerce_to_comparison(a, b, cmp = (a <=> b))
  raise ArgumentError, "comparison of #{a} with #{b} failed" if cmp.nil?
  return 1 if cmp > 0
  return -1 if cmp < 0
  0
end

def self.coerce_to_hash(obj)

def self.coerce_to_hash(obj)
  coerce_to(obj, Hash, :to_hash)
end

def self.coerce_to_int(obj)

def self.coerce_to_int(obj)
  coerce_to(obj, Integer, :to_int)
end

def self.coerce_to_option(obj, option)

def self.coerce_to_option(obj, option)
  coerce_to_options(obj, option)[0]
end

def self.coerce_to_options(obj, *options)

def self.coerce_to_options(obj, *options)
  hash = coerce_to_hash(obj)
  hash.values_at(*options)
end

def self.coerce_to_str(obj)

def self.coerce_to_str(obj)
  coerce_to(obj, String, :to_str)
end

def self.combine_mode_and_option(mode = nil, options = Backports::Undefined)

Used internally to combine {IO|File} options hash into mode (String or Integer)
def self.combine_mode_and_option(mode = nil, options = Backports::Undefined)
  # Can't backport autoclose, {internal|external|}encoding
  mode, options = nil, mode if mode.respond_to?(:to_hash) && options == Backports::Undefined
  options = {} if options == nil || options == Backports::Undefined
  options = coerce_to_hash(options)
  if mode && options[:mode]
    raise ArgumentError, "mode specified twice"
  end
  mode ||= options[:mode] || "r"
  mode = try_convert(mode, String, :to_str) || try_convert(mode, Integer, :to_int) || mode
  if options[:textmode] || options[:binmode]
    text = options[:textmode] || (mode.is_a?(String) && mode =~ /t/)
    bin  = options[:binmode]  || (mode.is_a?(String) ? mode =~ /b/ : mode & File::Constants::BINARY != 0)
    if text && bin
      raise ArgumentError, "both textmode and binmode specified"
    end
    case
      when !options[:binmode]
      when mode.is_a?(String)
        mode.insert(1, "b")
      else
        mode |= File::Constants::BINARY
    end
  end
  mode
end

def self.combine_mode_perm_and_option(mode = nil, perm = Backports::Undefined, options = Backports::Undefined)

Used internally to combine {IO|File} options hash into mode (String or Integer) and perm
def self.combine_mode_perm_and_option(mode = nil, perm = Backports::Undefined, options = Backports::Undefined)
  mode, options = nil, mode if mode.respond_to?(:to_hash) && perm == Backports::Undefined
  perm, options = nil, perm if perm.respond_to?(:to_hash) && options == Backports::Undefined
  perm = nil if perm == Backports::Undefined
  options = {} if options == Backports::Undefined
  options = coerce_to_hash(options)
  if perm && options[:perm]
    raise ArgumentError, "perm specified twice"
  end
  [combine_mode_and_option(mode, options), perm || options[:perm]]
end

def self.convert_all_arguments_to_path(klass, selector, skip)

Metaprogramming utility to convert all file arguments to paths
def self.convert_all_arguments_to_path(klass, selector, skip)
  mod = class << klass; self; end
  unless mod.method_defined? selector
    warn "#{mod}##{selector} is not defined, so arguments can't converted to path"
    return
  end
  first_args = (1..skip).map{|i| "arg_#{i}"}.join(",") + (skip > 0 ? "," : "")
  alias_method_chain(mod, selector, :potential_path_arguments) do |aliased_target, punctuation|
    mod.module_eval <<-end_eval, __FILE__, __LINE__ + 1
      def #{aliased_target}_with_potential_path_arguments#{punctuation}(#{first_args}*files, &block)
        files = files.map{|f| Backports.convert_path(f) }
        #{aliased_target}_without_potential_path_arguments#{punctuation}(#{first_args}*files, &block)
      end
    end_eval
  end
end

def self.convert_first_argument_to_path(klass, selector)

Metaprogramming utility to convert the first file argument to path
def self.convert_first_argument_to_path(klass, selector)
  mod = class << klass; self; end
  unless mod.method_defined? selector
    warn "#{mod}##{selector} is not defined, so argument can't converted to path"
    return
  end
  arity = mod.instance_method(selector).arity
  last_arg = []
  if arity < 0
    last_arg = ["*rest"]
    arity = -1-arity
  end
  arg_sequence = (["file"] + (1...arity).map{|i| "arg_#{i}"} + last_arg + ["&block"]).join(", ")
  alias_method_chain(mod, selector, :potential_path_argument) do |aliased_target, punctuation|
    mod.module_eval <<-end_eval, __FILE__, __LINE__ + 1
      def #{aliased_target}_with_potential_path_argument#{punctuation}(#{arg_sequence})
        file = Backports.convert_path(file)
        #{aliased_target}_without_potential_path_argument#{punctuation}(#{arg_sequence})
      end
    end_eval
  end
end

def self.convert_path(path)

def self.convert_path(path)
  try_convert(path, IO, :to_io) ||
  begin
    path = path.to_path if path.respond_to?(:to_path)
    try_convert(path, String, :to_str) || path
  end
end

def self.is_array?(obj)

def self.is_array?(obj)
  coerce_to(obj, Array, :to_ary) if obj.respond_to? :to_ary
end

def self.make_block_optional(mod, *methods)

Tests first if block is already optional when given options
Metaprogramming utility to make block optional.
def self.make_block_optional(mod, *methods)
  mod = class << mod; self; end unless mod.is_a? Module
  options = methods.last.is_a?(Hash) ? methods.pop : {}
  methods.each do |selector|
    unless mod.method_defined? selector
      warn "#{mod}##{selector} is not defined, so block can't be made optional"
      next
    end
    unless options[:force]
      # Check if needed
      test_on = options.fetch(:test_on)
      result =  begin
                  test_on.send(selector, *options.fetch(:arg, []))
                rescue LocalJumpError
                  false
                end
      next if result.class.name =~ /Enumerator/
    end
    require 'enumerator'
    arity = mod.instance_method(selector).arity
    last_arg = []
    if arity < 0
      last_arg = ["*rest"]
      arity = -1-arity
    end
    arg_sequence = ((0...arity).map{|i| "arg_#{i}"} + last_arg + ["&block"]).join(", ")
    alias_method_chain(mod, selector, :optional_block) do |aliased_target, punctuation|
      mod.module_eval <<-end_eval, __FILE__, __LINE__ + 1
        def #{aliased_target}_with_optional_block#{punctuation}(#{arg_sequence})
          return to_enum(:#{aliased_target}_without_optional_block#{punctuation}, #{arg_sequence}) unless block_given?
          #{aliased_target}_without_optional_block#{punctuation}(#{arg_sequence})
        end
      end_eval
    end
  end
end

def self.require_relative_dir

def self.require_relative_dir
  dir = caller.first.split(/\.rb:\d/,2).first
  short_path = dir[/.*(backports\/.*)/, 1] << '/'
  Dir.entries(dir).
      map{|f| Regexp.last_match(1) if /^(.*)\.rb$/ =~ f}.
      compact.
      sort.
      each do |f|
        path = '../../' + short_path + f
        if Kernel.private_method_defined?(:require_relative)
          require_relative path
        else
          require File.expand_path(path)
        end
      end
end

def self.suppress_verbose_warnings

def self.suppress_verbose_warnings
  before = $VERBOSE
  $VERBOSE = false if $VERBOSE # Set to false (default warning) but not nil (no warnings)
  yield
ensure
  $VERBOSE = before
end

def self.try_convert(obj, cls, meth)

def self.try_convert(obj, cls, meth)
  return obj if obj.kind_of?(cls)
  return nil unless obj.respond_to?(meth)
  ret = obj.__send__(meth)
  raise TypeError, "Coercion error: obj.#{meth} did NOT return a #{cls} (was #{ret.class})" unless ret.nil? || ret.kind_of?(cls)
  ret
end

def self.write(binary, filename, string, offset, options)

def self.write(binary, filename, string, offset, options)
  offset, options = nil, offset if offset.respond_to?(:to_hash) && options == Backports::Undefined
  options = {} if options == Backports::Undefined
  options = coerce_to_hash(options)
  File.open(filename, 'a+'){} if offset # insure existence
  options = {:mode => offset.nil? ? "w" : "r+"}.merge(options)
  args = options[:open_args] || [options]
  File.open(filename, *Backports.combine_mode_perm_and_option(*args)) do |f|
    f.binmode if binary && f.respond_to?(:binmode)
    f.seek(offset) unless offset.nil?
    f.write(string)
  end
end

def <(whatever)

def <(whatever)
  true
end

def >(whatever)

def >(whatever)
  true
end

def float_to_integer(float)

def float_to_integer(float)
  map_via_packing(float, 'D', 'q')
end

def frown_upon kind, msg

def frown_upon kind, msg
  warn kind, msg if $VERBOSE
end

def integer_to_float(int)

def integer_to_float(int)
  map_via_packing(int, 'q', 'D')
end

def map_via_packing(nb, pack, unpack)

def map_via_packing(nb, pack, unpack)
  result, = [nb.abs].pack(pack).unpack(unpack)
  nb < 0 ? -result : result
end

def warn kind, msg

def warn kind, msg
  return if warned[kind]
  super msg
  warned[kind] = msg
end