Module: Backports

Defined in:
lib/backports/tools.rb

Overview

Methods used internally by the backports.

Defined Under Namespace

Classes: Yielder

Constant Summary collapse

Undefined =

Used internally to make it easy to deal with optional arguments (from Rubinius)

Object.new

Class Method Summary collapse

Class Method Details

.alias_method_chain(mod, target, feature) ⇒ Object

Modified to avoid polluting Module if so desired (from Rails)



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/backports/tools.rb', line 48

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

.coerce_to(obj, cls, meth) ⇒ Object

Helper method to coerce a value into a specific class. Raises a TypeError if the coercion fails or the returned value is not of the right class. (from Rubinius)

Raises:

  • (TypeError)


75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/backports/tools.rb', line 75

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

.coerce_to_comparison(a, b, cmp = (a <=> b)) ⇒ Object

Checks for a failed comparison (in which case it throws an ArgumentError) Additionally, it maps any negative value to -1 and any positive value to +1 (from Rubinius)

Raises:

  • (ArgumentError)


91
92
93
94
95
96
# File 'lib/backports/tools.rb', line 91

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

.make_block_optional(mod, *methods) ⇒ Object

Metaprogramming utility to make block optional. Tests first if block is already optional when given options



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/backports/tools.rb', line 15

def self.make_block_optional mod,*methods
  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.empty?
      test_on = options[:test_on] || self.new
      next if (test_on.send(selector, *options.fetch(:arg, [])) rescue false)
    end
    
    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
        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

.require_relative(relative_feature) ⇒ Object

Adapted from Pragmatic’s “Programming Ruby” (since their version was buggy…)



5
6
7
8
9
10
11
# File 'lib/backports/tools.rb', line 5

def self.require_relative(relative_feature)
  file = caller.first.split(/:\d/,2).first
  if /\A\((.*)\)/ =~ file # eval, etc. 
    raise LoadError, "require_relative is called in #{$1}" 
  end 
  require File.expand_path(relative_feature, File.dirname(file))
end