Module Backports
In: lib/backports/tools.rb

Methods used internally by the backports.

Methods

Classes and Modules

Module Backports::StdLib

Constants

Undefined = Object.new   Used internally to make it easy to deal with optional arguments (from Rubinius)

Public Class methods

Used internally. Safe alias_method that will only alias if the source exists and destination doesn‘t

[Source]

     # File lib/backports/tools.rb, line 249
249:   def self.alias_method(mod, new_name, old_name)
250:     mod.class_eval do
251:       alias_method new_name, old_name if method_defined?(old_name) and not method_defined?(new_name)
252:     end
253:   end

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

[Source]

     # File lib/backports/tools.rb, line 173
173:   def self.alias_method_chain(mod, target, feature)
174:     mod.class_eval do
175:       # Strip out punctuation on predicates or bang methods since
176:       # e.g. target?_without_feature is not a valid method name.
177:       aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
178:       yield(aliased_target, punctuation) if block_given?
179: 
180:       with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
181: 
182:       alias_method without_method, target
183:       alias_method target, with_method
184: 
185:       case
186:         when public_method_defined?(without_method)
187:           public target
188:         when protected_method_defined?(without_method)
189:           protected target
190:         when private_method_defined?(without_method)
191:           private target
192:       end
193:     end
194:   end

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)

[Source]

     # File lib/backports/tools.rb, line 200
200:   def self.coerce_to(obj, cls, meth)
201:     return obj if obj.kind_of?(cls)
202: 
203:     begin
204:       ret = obj.__send__(meth)
205:     rescue Exception => e
206:       raise TypeError, "Coercion error: #{obj.inspect}.#{meth} => #{cls} failed:\n" \
207:                        "(#{e.message})"
208:     end
209:     raise TypeError, "Coercion error: obj.#{meth} did NOT return a #{cls} (was #{ret.class})" unless ret.kind_of? cls
210:     ret
211:   end

[Source]

     # File lib/backports/tools.rb, line 217
217:   def self.coerce_to_ary(obj)
218:     coerce_to(obj, Array, :to_ary)
219:   end

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)

[Source]

     # File lib/backports/tools.rb, line 236
236:   def self.coerce_to_comparison(a, b, cmp = (a <=> b))
237:     raise ArgumentError, "comparison of #{a} with #{b} failed" if cmp.nil?
238:     return 1 if cmp > 0
239:     return -1 if cmp < 0
240:     0
241:   end

[Source]

     # File lib/backports/tools.rb, line 213
213:   def self.coerce_to_int(obj)
214:     coerce_to(obj, Integer, :to_int)
215:   end

[Source]

     # File lib/backports/tools.rb, line 221
221:   def self.coerce_to_str(obj)
222:     coerce_to(obj, String, :to_str)
223:   end

Used internally to combine {IO|File} options hash into mode (String or Integer)

[Source]

     # File lib/backports/tools.rb, line 264
264:   def self.combine_mode_and_option(mode = nil, options = Backports::Undefined)
265:     # Can't backport autoclose, {internal|external|}encoding
266:     mode, options = nil, mode if mode.is_a?(Hash) and options == Backports::Undefined
267:     options = {} if options == Backports::Undefined
268:     if mode && options[:mode]
269:       raise ArgumentError, "mode specified twice"
270:     end
271:     mode ||= options[:mode] || "r"
272:     if options[:textmode] || options[:binmode]
273:       text = options[:textmode] || (mode.is_a?(String) && mode =~ /t/)
274:       bin  = options[:binmode]  || (mode.is_a?(String) ? mode =~ /b/ : mode & File::Constants::BINARY != 0)
275:       if text && bin
276:         raise ArgumentError, "both textmode and binmode specified"
277:       end
278:       case
279:         when !options[:binmode]
280:         when mode.is_a?(String)
281:           mode.insert(1, "b")
282:         else
283:           mode |= File::Constants::BINARY
284:       end
285:     end
286:     mode
287:   end

Used internally to combine {IO|File} options hash into mode (String or Integer) and perm

[Source]

     # File lib/backports/tools.rb, line 290
290:   def self.combine_mode_perm_and_option(mode = nil, perm = Backports::Undefined, options = Backports::Undefined)
291:     mode, options = nil, mode if mode.is_a?(Hash) and perm == Backports::Undefined
292:     perm, options = nil, perm if perm.is_a?(Hash) and options == Backports::Undefined
293:     perm = nil if perm == Backports::Undefined
294:     options = {} if options == Backports::Undefined
295:     if perm && options[:perm]
296:       raise ArgumentError, "perm specified twice"
297:     end
298:     [combine_mode_and_option(mode, options), perm || options[:perm]]
299:   end

Metaprogramming utility to convert all file arguments to paths

[Source]

     # File lib/backports/tools.rb, line 148
148:   def self.convert_all_arguments_to_path(mod, skip, *methods)
149:     methods.each do |selector|
150:       unless mod.method_defined? selector
151:         warn "#{mod}##{selector} is not defined, so arguments can't converted to path"
152:         next
153:       end
154:       first_args = (1..skip).map{|i| "arg_#{i}"}.join(",") + (skip > 0 ? "," : "")
155:       alias_method_chain(mod, selector, :potential_path_arguments) do |aliased_target, punctuation|
156:         mod.module_eval "def \#{aliased_target}_with_potential_path_arguments\#{punctuation}(\#{first_args}*files, &block)\nfiles = files.map{|f| Backports.convert_to_path f}\n\#{aliased_target}_without_potential_path_arguments\#{punctuation}(\#{first_args}*files, &block)\nend\n"
157:       end
158:     end
159:   end

Metaprogramming utility to convert the first file argument to path

[Source]

     # File lib/backports/tools.rb, line 121
121:   def self.convert_first_argument_to_path(mod, *methods)
122:     methods.each do |selector|
123:       unless mod.method_defined? selector
124:         warn "#{mod}##{selector} is not defined, so argument can't converted to path"
125:         next
126:       end
127:       arity = mod.instance_method(selector).arity
128:       last_arg = []
129:       if arity < 0
130:         last_arg = ["*rest"]
131:         arity = -1-arity
132:       end
133:       arg_sequence = (["file"] + (1...arity).map{|i| "arg_#{i}"} + last_arg + ["&block"]).join(", ")
134: 
135:       alias_method_chain(mod, selector, :potential_path_argument) do |aliased_target, punctuation|
136:         mod.module_eval "def \#{aliased_target}_with_potential_path_argument\#{punctuation}(\#{arg_sequence})\nfile = Backports.convert_to_path(file)\n\#{aliased_target}_without_potential_path_argument\#{punctuation}(\#{arg_sequence})\nend\n"
137:       end
138:     end
139:   end

[Source]

     # File lib/backports/tools.rb, line 167
167:   def self.convert_to_path(file_or_path)
168:     file_or_path.respond_to?(:to_path) ? file_or_path.to_path  : file_or_path
169:   end

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

[Source]

     # File lib/backports/tools.rb, line 83
 83:   def self.make_block_optional(mod, *methods)
 84:     options = methods.last.is_a?(Hash) ? methods.pop : {}
 85:     methods.each do |selector|
 86:       unless mod.method_defined? selector
 87:         warn "#{mod}##{selector} is not defined, so block can't be made optional"
 88:         next
 89:       end
 90:       unless options[:force]
 91:         # Check if needed
 92:         test_on = options.fetch(:test_on)
 93:         result =  begin
 94:                     test_on.send(selector, *options.fetch(:arg, []))
 95:                   rescue LocalJumpError
 96:                     false
 97:                   end
 98:         next if result.class.name =~ /Enumerator$/
 99:       end
100:       arity = mod.instance_method(selector).arity
101:       last_arg = []
102:       if arity < 0
103:         last_arg = ["*rest"]
104:         arity = -1-arity
105:       end
106:       arg_sequence = ((0...arity).map{|i| "arg_#{i}"} + last_arg + ["&block"]).join(", ")
107: 
108:       alias_method_chain(mod, selector, :optional_block) do |aliased_target, punctuation|
109:         mod.module_eval "def \#{aliased_target}_with_optional_block\#{punctuation}(\#{arg_sequence})\nreturn to_enum(:\#{aliased_target}_without_optional_block\#{punctuation}, \#{arg_sequence}) unless block_given?\n\#{aliased_target}_without_optional_block\#{punctuation}(\#{arg_sequence})\nend\n"
110:       end
111:     end
112:   end

Adapted from Pragmatic‘s "Programming Ruby" (since their version was buggy…)

[Source]

    # File lib/backports/tools.rb, line 4
 4:   def self.require_relative(relative_feature)
 5:     file = caller.first.split(/:\d/,2).first
 6:     if /\A\((.*)\)/ =~ file # eval, etc.
 7:       raise LoadError, "require_relative is called in #{$1}"
 8:     end
 9:     require File.expand_path(relative_feature, File.dirname(file))
10:   end

[Source]

    # File lib/backports/tools.rb, line 12
12:   def self.require_relative_dir(relative_dir)
13:     dir = File.expand_path(relative_dir, File.dirname(caller.first.split(/:\d/,2).first))
14:     Dir.entries(dir).
15:         map{|f| Regexp.last_match(1) if /^(.*)\.rb$/ =~ f}.
16:         compact.
17:         sort.
18:         each do |f|
19:           require File.expand_path(f, dir)
20:         end
21:   end

Used internally to propagate lambda?

[Source]

     # File lib/backports/tools.rb, line 256
256:   def self.track_lambda(from, to, default = false)
257:     is_lambda = from.instance_variable_get :@is_lambda
258:     is_lambda = default if is_lambda.nil?
259:     to.instance_variable_set :@is_lambda, is_lambda
260:     to
261:   end

[Source]

     # File lib/backports/tools.rb, line 225
225:   def self.try_convert(obj, cls, meth)
226:     return obj if obj.kind_of?(cls)
227:     return nil unless obj.respond_to?(meth)
228:     ret = obj.__send__(meth)
229:     raise TypeError, "Coercion error: obj.#{meth} did NOT return a #{cls} (was #{ret.class})" unless ret.nil? || ret.kind_of?(cls)
230:     ret
231:   end

[Source]

     # File lib/backports/tools.rb, line 301
301:   def self.write(binary, filename, string, offset, options)
302:     offset, options = nil, offset if Hash === offset and options == Backports::Undefined
303:     options = {} if options == Backports::Undefined
304:     options = {:mode => offset.nil? ? "w" : "r+"}.merge(options)
305:     args = options[:open_args] || [options]
306:     File.open(filename, *args) do |f|
307:       f.binmode if binary && f.respond_to?(:binmode)
308:       f.seek(offset) unless offset.nil?
309:       f.write(string)
310:     end
311:   end

[Validate]