backports 1.6.8 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.sw?
2
+ *.rbc
3
+ .DS_Store
4
+ coverage
5
+ rdoc
6
+ pkg
7
+ assets
data/CHANGELOG.rdoc CHANGED
@@ -1,5 +1,13 @@
1
1
  = Packable --- History
2
2
 
3
+ == Version 1.7 - May 27, 2009
4
+
5
+ Porting all 1.8.7 features to rubinius was quite enlightening (http://blog.marc-andre.ca/2009/05/schizo-ruby-puzzle.html ).
6
+ Many improvements were made to be consistent with MRI, e.g. converting arguments using to_int if needed, etc...
7
+ The changes are now reflected in 'backports'.
8
+
9
+ Some IO and ARGF methods were missing and are now complete.
10
+
3
11
  == Version 1.6.6 - April 30, 2009
4
12
 
5
13
  Important bug fix. String#gsub is left alone until I find a good way to modify it.
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 1
3
- :minor: 6
4
- :patch: 8
3
+ :minor: 7
4
+ :patch: 0
data/backports.gemspec ADDED
@@ -0,0 +1,98 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{backports}
5
+ s.version = "1.7.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Marc-Andr\303\251 Lafortune"]
9
+ s.date = %q{2009-05-31}
10
+ s.description = %q{Essential backports that enable some of the really nice features of ruby 1.8.7, ruby 1.9 and rails from ruby 1.8.6 and earlier.}
11
+ s.email = %q{github@marc-andre.ca}
12
+ s.extra_rdoc_files = [
13
+ "LICENSE",
14
+ "README.rdoc"
15
+ ]
16
+ s.files = [
17
+ ".document",
18
+ ".gitignore",
19
+ "CHANGELOG.rdoc",
20
+ "LICENSE",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION.yml",
24
+ "backports.gemspec",
25
+ "lib/backports.rb",
26
+ "lib/backports/argf.rb",
27
+ "lib/backports/array.rb",
28
+ "lib/backports/binding.rb",
29
+ "lib/backports/core_ext.rb",
30
+ "lib/backports/dir.rb",
31
+ "lib/backports/enumerable.rb",
32
+ "lib/backports/enumerator.rb",
33
+ "lib/backports/env.rb",
34
+ "lib/backports/fixnum.rb",
35
+ "lib/backports/float.rb",
36
+ "lib/backports/gc.rb",
37
+ "lib/backports/hash.rb",
38
+ "lib/backports/integer.rb",
39
+ "lib/backports/io.rb",
40
+ "lib/backports/kernel.rb",
41
+ "lib/backports/method.rb",
42
+ "lib/backports/module.rb",
43
+ "lib/backports/numeric.rb",
44
+ "lib/backports/object_space.rb",
45
+ "lib/backports/proc.rb",
46
+ "lib/backports/process.rb",
47
+ "lib/backports/range.rb",
48
+ "lib/backports/regexp.rb",
49
+ "lib/backports/string.rb",
50
+ "lib/backports/struct.rb",
51
+ "lib/backports/symbol.rb",
52
+ "test/array_test.rb",
53
+ "test/binding_test.rb",
54
+ "test/enumerable_test.rb",
55
+ "test/enumerator_test.rb",
56
+ "test/hash_test.rb",
57
+ "test/kernel_test.rb",
58
+ "test/method_test.rb",
59
+ "test/module_test.rb",
60
+ "test/object_test.rb",
61
+ "test/regexp_test.rb",
62
+ "test/string_test.rb",
63
+ "test/symbol_test.rb",
64
+ "test/test_helper.rb"
65
+ ]
66
+ s.has_rdoc = true
67
+ s.homepage = %q{http://github.com/marcandre/backports}
68
+ s.rdoc_options = ["--charset=UTF-8", "--title", "Backports library", "--main", "README.rdoc", "--line-numbers", "--inline-source"]
69
+ s.require_paths = ["lib"]
70
+ s.rubyforge_project = %q{backports}
71
+ s.rubygems_version = %q{1.3.1}
72
+ s.summary = %q{Backports or ruby 1.8.7+ & rails for older ruby.}
73
+ s.test_files = [
74
+ "test/array_test.rb",
75
+ "test/binding_test.rb",
76
+ "test/enumerable_test.rb",
77
+ "test/enumerator_test.rb",
78
+ "test/hash_test.rb",
79
+ "test/kernel_test.rb",
80
+ "test/method_test.rb",
81
+ "test/module_test.rb",
82
+ "test/object_test.rb",
83
+ "test/regexp_test.rb",
84
+ "test/string_test.rb",
85
+ "test/symbol_test.rb",
86
+ "test/test_helper.rb"
87
+ ]
88
+
89
+ if s.respond_to? :specification_version then
90
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
91
+ s.specification_version = 2
92
+
93
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
94
+ else
95
+ end
96
+ else
97
+ end
98
+ end
data/lib/backports.rb CHANGED
@@ -10,13 +10,6 @@ module Kernel
10
10
  end unless method_defined? :require_relative
11
11
  end
12
12
 
13
- class Array
14
- # Standard in rails, and we use it in module.
15
- def extract_options!
16
- last.is_a?(::Hash) ? pop : {}
17
- end unless method_defined? :extract_options!
18
- end
19
-
20
- %w(object module kernel object_space array enumerable enumerator string symbol integer fixnum hash proc binding dir io method regexp struct).each do |lib|
13
+ %w(core_ext module kernel array enumerable enumerator string symbol integer numeric fixnum hash proc binding dir io method regexp struct float object_space argf gc env process).each do |lib|
21
14
  require_relative "backports/#{lib}"
22
15
  end
@@ -0,0 +1,57 @@
1
+ if RUBY_VERSION < '1.8.7'
2
+ class << ARGF
3
+ # Standard in Ruby 1.8.7.
4
+ make_block_optional :each, :each_line, :each_byte
5
+ end
6
+ end
7
+
8
+ class << ARGF
9
+ # Standard in Ruby 1.8.7.
10
+ def bytes
11
+ to_enum :each_byte
12
+ end unless method_defined? :bytes
13
+
14
+ # Standard in Ruby 1.8.7.
15
+ def chars
16
+ to_enum :each_char
17
+ end unless method_defined? :chars
18
+
19
+ # Standard in Ruby 1.8.7.
20
+ def each_char
21
+ return to_enum(:each_char) unless block_given?
22
+ if $KCODE == "UTF-8"
23
+ lookup = 7.downto(4)
24
+ while c = read(1) do
25
+ n = c[0]
26
+ leftmost_zero_bit = lookup.find{|i| n[i].zero? }
27
+ case leftmost_zero_bit
28
+ when 7 # ASCII
29
+ yield c
30
+ when 6 # UTF 8 complementary characters
31
+ next # Encoding error, ignore
32
+ else
33
+ more = read(6-leftmost_zero_bit)
34
+ break unless more
35
+ yield c+more
36
+ end
37
+ end
38
+ else
39
+ while s = read(1)
40
+ yield s
41
+ end
42
+ end
43
+
44
+ self
45
+ end unless method_defined? :each_char
46
+
47
+ # Standard in Ruby 1.8.7.
48
+ alias_method :getbyte, :getc
49
+
50
+ # Standard in Ruby 1.8.7.
51
+ alias_method :readbyte, :readchar
52
+
53
+ # Standard in Ruby 1.8.7.
54
+ def lines(*args)
55
+ to_enum :each_line, *args
56
+ end unless method_defined? :lines
57
+ end
@@ -1,17 +1,19 @@
1
1
  class Array
2
+ # Standard in Ruby 1.9. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
2
3
  class << self
3
- # Standard in Ruby 1.9. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
4
- def try_convert(x)
5
- x.to_ary if x.respond_to? :to_ary
6
- end unless method_defined? :try_convert
4
+ # Try to convert obj into an array, using to_ary method.
5
+ # Returns converted array or nil if obj cannot be converted
6
+ # for any reason. This method is to check if an argument is an array.
7
+ def try_convert(obj)
8
+ return nil unless obj.respond_to?(:to_ary)
9
+ Type.coerce_to(obj, Array, :to_ary)
10
+ end
7
11
  end
8
12
 
9
- # Standard in Ruby 1.9. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
10
- make_block_optional :collect!, :map!, :each, :each_index, :reverse_each, :reject, :reject!, :delete_if, :test_on => [42]
11
-
12
- # Standard in Ruby 1.9. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
13
+
14
+ # Standard in Ruby 1.8.7+. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
13
15
  def combination(num)
14
- num = num.to_i
16
+ num = Type.coerce_to num, Fixnum, :to_int
15
17
  return to_enum(:combination, num) unless block_given?
16
18
  return self unless (0..size).include? num
17
19
  # Implementation note: slightly tricky.
@@ -27,32 +29,43 @@ class Array
27
29
  picks[move...num] = (new_index...(new_index+num-move)).to_a
28
30
  end
29
31
  end unless method_defined? :combination
30
-
31
- # Standard in Ruby 1.9. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
32
- def cycle(*arg, &block)
33
- return to_enum(:cycle, *arg) unless block_given?
34
- nb = arg.empty? ? (1/0.0) : arg.first
35
- nb.to_i.times{each(&block)}
32
+
33
+ # Standard in Ruby 1.8.7+. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
34
+ def cycle(n = nil, &block)
35
+ return to_enum(:cycle, n) unless block_given?
36
+ if n.nil?
37
+ loop(&block)
38
+ else
39
+ n = Type.coerce_to n, Fixnum, :to_int
40
+ n.times{each(&block)}
41
+ end
42
+ nil
36
43
  end unless method_defined? :cycle
37
-
38
- # extract_options! in backports.rb
39
-
40
- # flatten & flatten!, standard in ruby 1.9. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
44
+
45
+ # Standard in rails, and we use it here...
46
+ def extract_options!
47
+ last.is_a?(::Hash) ? pop : {}
48
+ end unless method_defined? :extract_options!
49
+
50
+ # Standard in Ruby 1.8.7+. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
51
+ make_block_optional :collect!, :map!, :each, :each_index, :reverse_each, :reject, :reject!, :delete_if, :test_on => [42]
52
+
53
+ # flatten & flatten!, standard in ruby 1.8.7+. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
41
54
  unless ([[]].flatten(1) rescue false)
42
55
 
43
56
  # Recursively flatten any contained Arrays into an one-dimensional result.
44
57
  # Adapted from rubinius'
45
- def flatten_with_optional_argument(level=nil)
46
- return flatten_without_optional_argument unless level || level == (1/0.0)
58
+ def flatten_with_optional_argument(level=-1)
47
59
  dup.flatten!(level) || self
48
60
  end
49
61
 
50
62
  # Flattens self in place as #flatten. If no changes are
51
63
  # made, returns nil, otherwise self.
52
64
  # Adapted from rubinius'
53
- def flatten_with_optional_argument!(level=nil)
54
- return flatten_without_optional_argument! unless level || level == (1/0.0)
55
-
65
+ def flatten_with_optional_argument!(level=-1)
66
+ level = Type.coerce_to(level, Integer, :to_int)
67
+ return flatten_without_optional_argument! unless level >= 0
68
+
56
69
  ret, out = nil, []
57
70
  ret = recursively_flatten_finite(self, out, level)
58
71
  replace(out) if ret
@@ -82,8 +95,8 @@ class Array
82
95
  end
83
96
  private :recursively_flatten_finite
84
97
  end # flatten & flatten!
85
-
86
- # index
98
+
99
+ # index. Standard in Ruby 1.8.7+. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
87
100
  unless ([1].index{true} rescue false)
88
101
  def index_with_block(*arg)
89
102
  return index_without_block(*arg) unless block_given? && arg.empty?
@@ -93,27 +106,37 @@ class Array
93
106
  alias_method_chain :index, :block
94
107
  alias_method :find_index, :index
95
108
  end
96
-
97
- # pop. Standard in Ruby 1.9. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
109
+
110
+ # pop. Standard in Ruby 1.8.7+. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
98
111
  unless ([1].pop(1) rescue false)
99
- def pop_with_optional_argument(*arg)
100
- return pop_without_optional_argument if arg.empty?
101
- n = arg.first.to_i
102
- slice!([0,size-n].max, size)
112
+ def pop_with_optional_argument(n = Undefined)
113
+ return pop_without_optional_argument if n == Undefined
114
+ n = Type.coerce_to(n, Fixnum, :to_int)
115
+ raise ArgumentError, "negative array size" if n < 0
116
+ first = size - n
117
+ first = 0 if first < 0
118
+ slice!(first..size).to_a
103
119
  end
104
120
  alias_method_chain :pop, :optional_argument
105
121
  end
106
-
122
+
123
+ # Standard in Ruby 1.9. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
107
124
  def product(*arg)
125
+ # Implementation notes: We build an enumerator for all the combinations
126
+ # by building it up successively using "inject" and starting from a trivial enumerator.
127
+ # It would be easy to have "product" yield to a block but the standard
128
+ # simply returns an array, so you'll find a simple call to "to_a" at the end.
129
+ #
108
130
  trivial_enum = Enumerator.new{|yielder| yielder.yield [] }
109
- [self, *arg].inject(trivial_enum) do |enum, array|
110
- Enumerator.new do |yielder|
111
- enum.each do |partial_product|
112
- array.each do |obj|
113
- yielder.yield partial_product + [obj]
131
+ [self, *arg].map{|x| Type.coerce_to(x, Array, :to_ary)}.
132
+ inject(trivial_enum) do |enum, array|
133
+ Enumerator.new do |yielder|
134
+ enum.each do |partial_product|
135
+ array.each do |obj|
136
+ yielder.yield partial_product + [obj]
137
+ end
114
138
  end
115
139
  end
116
- end
117
140
  end.to_a
118
141
  end unless method_defined? :product
119
142
 
@@ -126,35 +149,45 @@ class Array
126
149
  end
127
150
  alias_method_chain :rindex, :block
128
151
  end
129
-
130
- # shift. Standard in Ruby 1.9. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
152
+
153
+ # Standard in Ruby 1.8.7+. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
154
+ def sample(n = Undefined)
155
+ return self[rand(size)] if n == Undefined
156
+ n = Type.coerce_to(n, Fixnum, :to_int)
157
+ raise ArgumentError, "negative array size" if n < 0
158
+ n = size if n > size
159
+ result = Array.new(self)
160
+ n.times do |i|
161
+ r = i + rand(size - i)
162
+ result[i], result[r] = result[r], result[i]
163
+ end
164
+ result[n..size] = []
165
+ result
166
+ end unless method_defined? :sample
167
+
168
+ # shift. Standard in Ruby 1.8.7+. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
131
169
  unless ([1].shift(1) rescue false)
132
- def shift_with_optional_argument(*arg)
133
- return shift_without_optional_argument if arg.empty?
134
- n = arg.first.to_i
170
+ def shift_with_optional_argument(n = Undefined)
171
+ return shift_without_optional_argument if n == Undefined
172
+ n = Type.coerce_to(n, Fixnum, :to_int)
173
+ raise ArgumentError, "negative array size" if n < 0
135
174
  slice!(0, n)
136
175
  end
137
176
  alias_method_chain :shift, :optional_argument
138
177
  end
139
-
140
-
141
- def sample(*arg)
142
- return self[rand(size)] if arg.empty?
143
- n = [arg.first.to_i, size].min
144
- index = Array.new(size)
145
- n.times do |i|
146
- r = i + rand(size - i)
147
- index[i], index[r] = index[r] || r, index[i] || i
148
- end
149
- values_at(*index.first(n))
150
- end unless method_defined? :sample
151
-
178
+
179
+ # Standard in Ruby 1.8.7+. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
152
180
  def shuffle
153
- sample(size)
181
+ dup.shuffle!
154
182
  end unless method_defined? :shuffle
155
183
 
184
+ # Standard in Ruby 1.8.7+. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
156
185
  def shuffle!
157
- replace(sample(size))
186
+ size.times do |i|
187
+ r = i + rand(size - i)
188
+ self[i], self[r] = self[r], self[i]
189
+ end
190
+ self
158
191
  end unless method_defined? :shuffle!
159
192
 
160
193
  end
@@ -1,5 +1,5 @@
1
1
  class Binding
2
- # Standard in ruby 1.9. See official documentation[http://ruby-doc.org/core-1.9/classes/Symbol.html]
2
+ # Standard in ruby 1.8.7+. See official documentation[http://ruby-doc.org/core-1.9/classes/Symbol.html]
3
3
  def eval(expr, *arg)
4
4
  Kernel.eval(expr, self, *arg)
5
5
  end unless method_defined? :eval
@@ -0,0 +1,61 @@
1
+ # These are non-standard extensions
2
+
3
+ class Module
4
+ # Metaprogramming utility to make block optional.
5
+ # Tests first if block is already optional when given options
6
+ def make_block_optional(*methods)
7
+ options = methods.extract_options!
8
+ methods.each do |selector|
9
+ next unless method_defined? selector
10
+ unless options.empty?
11
+ test_on = options[:test_on] || self.new
12
+ next if (test_on.send(selector, *options.fetch(:arg, [])) rescue false)
13
+ end
14
+
15
+ arity = instance_method(selector).arity
16
+ last_arg = []
17
+ if arity < 0
18
+ last_arg = ["*rest"]
19
+ arity = -1-arity
20
+ end
21
+ arg_sequence = ((0...arity).map{|i| "arg_#{i}"} + last_arg + ["&block"]).join(", ")
22
+
23
+ alias_method_chain(selector, :optional_block) do |aliased_target, punctuation|
24
+ module_eval <<-end_eval
25
+ def #{aliased_target}_with_optional_block#{punctuation}(#{arg_sequence})
26
+ return to_enum(:#{aliased_target}_without_optional_block#{punctuation}, #{arg_sequence}) unless block_given?
27
+ #{aliased_target}_without_optional_block#{punctuation}(#{arg_sequence})
28
+ end
29
+ end_eval
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ module Type
36
+ # From Rubinius
37
+ class << self
38
+ def coerce_to(obj, cls, meth)
39
+ return obj if obj.kind_of?(cls)
40
+
41
+ begin
42
+ ret = obj.__send__(meth)
43
+ rescue Exception => e
44
+ raise TypeError, "Coercion error: #{obj.inspect}.#{meth} => #{cls} failed:\n" \
45
+ "(#{e.message})"
46
+ end
47
+ raise TypeError, "Coercion error: obj.#{meth} did NOT return a #{cls} (was #{ret.class})" unless ret.kind_of? cls
48
+ ret
49
+ end unless method_defined? :coerce_to
50
+ end
51
+
52
+ def self.coerce_to_comparison(a, b, cmp = (a <=> b))
53
+ raise ArgumentError, "comparison of #{a} with #{b} failed" if cmp.nil?
54
+ return 1 if cmp > 0
55
+ return -1 if cmp < 0
56
+ 0
57
+ end unless method_defined? :coerce_to_comparison
58
+ end
59
+
60
+ # From Rubinius
61
+ Undefined = Object.new unless Kernel.const_defined? :Undefined