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)



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/backports/tools.rb', line 97

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)


124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/backports/tools.rb', line 124

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)


140
141
142
143
144
145
# File 'lib/backports/tools.rb', line 140

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

.convert_all_arguments_to_path(mod, skip, *methods) ⇒ Object

Metaprogramming utility to convert all file arguments to paths



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

def self.convert_all_arguments_to_path mod, skip, *methods
  methods.each do |selector|
    unless mod.method_defined? selector
      warn "#{mod}##{selector} is not defined, so arguments can't converted to path"
      next
    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
        def #{aliased_target}_with_potential_path_arguments#{punctuation}(#{first_args}*files, &block)
          files = files.map{|f| Backports.convert_to_path f}
          #{aliased_target}_without_potential_path_arguments#{punctuation}(#{first_args}*files, &block)
        end
      end_eval
    end
  end
end

.convert_first_argument_to_path(mod, *methods) ⇒ Object

Metaprogramming utility to convert the first file argument to path



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

def self.convert_first_argument_to_path mod,*methods
  methods.each do |selector|
    unless mod.method_defined? selector
      warn "#{mod}##{selector} is not defined, so argument can't converted to path"
      next
    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
        def #{aliased_target}_with_potential_path_argument#{punctuation}(#{arg_sequence})
          file = Backports.convert_to_path(file)
          #{aliased_target}_without_potential_path_argument#{punctuation}(#{arg_sequence})
        end
      end_eval
    end
  end
end

.convert_to_path(file_or_path) ⇒ Object



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

def self.convert_to_path(file_or_path)
  coerce_to(file_or_path, String, :to_str) rescue coerce_to(file_or_path, String, :to_path) rescue file_or_path
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