extensions 0.4.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/ChangeLog +190 -0
- data/HISTORY +59 -0
- data/README +341 -0
- data/README.1st +11 -0
- data/Rakefile +96 -0
- data/VERSION +1 -0
- data/bin/rbxtm +13 -0
- data/etc/checklist +17 -0
- data/etc/website/index.html +10 -0
- data/etc/website/upload.sh +25 -0
- data/install-doc.rb +89 -0
- data/install.rb +1098 -0
- data/install.sh +3 -0
- data/lib/extensions/_base.rb +153 -0
- data/lib/extensions/_template.rb +36 -0
- data/lib/extensions/all.rb +17 -0
- data/lib/extensions/array.rb +24 -0
- data/lib/extensions/class.rb +50 -0
- data/lib/extensions/enumerable.rb +183 -0
- data/lib/extensions/hash.rb +23 -0
- data/lib/extensions/io.rb +58 -0
- data/lib/extensions/numeric.rb +204 -0
- data/lib/extensions/object.rb +164 -0
- data/lib/extensions/ostruct.rb +41 -0
- data/lib/extensions/string.rb +316 -0
- data/lib/extensions/symbol.rb +28 -0
- data/test/TEST.rb +48 -0
- data/test/tc_array.rb +27 -0
- data/test/tc_class.rb +34 -0
- data/test/tc_enumerable.rb +87 -0
- data/test/tc_hash.rb +34 -0
- data/test/tc_io.rb +32 -0
- data/test/tc_numeric.rb +435 -0
- data/test/tc_object.rb +72 -0
- data/test/tc_ostruct.rb +60 -0
- data/test/tc_string.rb +438 -0
- data/test/tc_symbol.rb +20 -0
- metadata +99 -0
data/install.sh
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# This file is 'required' by all files that implement standard class
|
4
|
+
# extensions as part of the "Ruby/Extensions" project.
|
5
|
+
#
|
6
|
+
# The "Extensions" project requires 1.8.0 or greater to run, as it is too
|
7
|
+
# much hassle at the moment to consider supporting older versions. That may
|
8
|
+
# one day be implemented if demand is there. One option would be to require
|
9
|
+
# "shim", so that we can assume all 1.8 library methods are implemented.
|
10
|
+
#
|
11
|
+
# This file is only of interest to developers of the package, so no detailed
|
12
|
+
# documentation is included here. However, by way of introduction, this is what
|
13
|
+
# it's all about. Each method that is implemented as part of this package is
|
14
|
+
# done so through a framework implemented in this file. Take the following
|
15
|
+
# simple example:
|
16
|
+
#
|
17
|
+
# ExtensionsProject.implement(Integer, :even?, :instance) do
|
18
|
+
# class Integer
|
19
|
+
# #
|
20
|
+
# # RDoc comments.
|
21
|
+
# #
|
22
|
+
# def even?
|
23
|
+
# self % 2 == 0
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# This purposes of this are as follows:
|
29
|
+
# - if the intended method (in this case IO.write) is already defined,
|
30
|
+
# we don't want to overwrite it (we issue a warning and move on)
|
31
|
+
# - if the intended method is _not_ implemented as a result of the block,
|
32
|
+
# we have not done as we said, and an error is raised
|
33
|
+
# - the ExtensionsProject class gathers information on which methods have
|
34
|
+
# been implemented, making for a very handy command-line reference (+rbxtm+)
|
35
|
+
#
|
36
|
+
# The <tt>ExtensionsProject.implement</tt> method is responsible for ensuring
|
37
|
+
# these are so. It gives us documentation, and some assurance that the
|
38
|
+
# extensions are doing what we say they are doing.
|
39
|
+
#
|
40
|
+
|
41
|
+
# :enddoc:
|
42
|
+
|
43
|
+
#
|
44
|
+
# For what reason does Ruby define Module#methods, Module#instance_methods,
|
45
|
+
# and Module#method_defined?, but not Module#instance_method_defined? ?
|
46
|
+
#
|
47
|
+
# No matter, extending standard classes is the name of the game here.
|
48
|
+
#
|
49
|
+
class Module
|
50
|
+
if Module.method_defined?(:instance_method_defined?)
|
51
|
+
STDERR.puts "Warning: Module#instance_method_defined? already defined; not overwriting"
|
52
|
+
else
|
53
|
+
def instance_method_defined?(_method)
|
54
|
+
instance_methods(true).find { |m| m == _method.to_s }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if Module.method_defined?(:module_method_defined?)
|
59
|
+
STDERR.puts "Warning: Module#module_method_defined? already defined; not overwriting"
|
60
|
+
else
|
61
|
+
def module_method_defined?(_method)
|
62
|
+
singleton_methods(true).find { |m| m == _method.to_s }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
class ExtensionsProject
|
69
|
+
|
70
|
+
class << ExtensionsProject
|
71
|
+
@@extension_methods = []
|
72
|
+
|
73
|
+
#
|
74
|
+
# The list of methods implemented in this project.
|
75
|
+
#
|
76
|
+
def extension_methods
|
77
|
+
@@extension_methods
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Return the name of the project. To be used in error messages, etc., for
|
82
|
+
# consistency.
|
83
|
+
#
|
84
|
+
def project_name
|
85
|
+
"Ruby/Extensions"
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Wraps around the implementation of a method, emitting a warning if the
|
90
|
+
# method is already defined. Returns true to indicate - false to indicate
|
91
|
+
# failure (i.e. method is already defined). Raises an error if the
|
92
|
+
# specified method is not actually implemented by the block.
|
93
|
+
#
|
94
|
+
def implement(_module, _method, _type=:instance)
|
95
|
+
raise "Internal error: #{__FILE__}:#{__LINE__}" unless
|
96
|
+
_module.is_a? Module and
|
97
|
+
_method.is_a? Symbol and
|
98
|
+
_type == :instance or _type == :class or _type == :module
|
99
|
+
|
100
|
+
fullname = _module.to_s + string_rep(_type) + _method.to_s
|
101
|
+
|
102
|
+
if _defined?(_module, _method, _type)
|
103
|
+
STDERR.puts "#{project_name}: #{fullname} is already defined; not overwriting"
|
104
|
+
return false
|
105
|
+
else
|
106
|
+
yield # Perform the block; presumably a method implementation.
|
107
|
+
if _method == :initialize and _type == :instance
|
108
|
+
# Special case; we can't verify this.
|
109
|
+
@@extension_methods<< "#{_module}::new"
|
110
|
+
else
|
111
|
+
unless _defined?(_module, _method, _type)
|
112
|
+
raise "#{project_name}: internal error: was supposed to implement " +
|
113
|
+
"#{fullname}, but it didn't!"
|
114
|
+
end
|
115
|
+
@@extension_methods << fullname
|
116
|
+
end
|
117
|
+
return true
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
# See whether the given module implements the given method, taking account
|
123
|
+
# of the type (class/instance) required.
|
124
|
+
def _defined?(_module, _method, _type)
|
125
|
+
case _type
|
126
|
+
when :instance
|
127
|
+
_module.instance_method_defined?(_method) # See definition above.
|
128
|
+
when :class, :module
|
129
|
+
_module.module_method_defined?(_method) # See definition above.
|
130
|
+
end
|
131
|
+
end
|
132
|
+
private :_defined?
|
133
|
+
|
134
|
+
|
135
|
+
# Return the string representation of the given method type.
|
136
|
+
def string_rep(method_type)
|
137
|
+
case method_type
|
138
|
+
when :instance then "#"
|
139
|
+
when :class then "."
|
140
|
+
when :module then "."
|
141
|
+
else
|
142
|
+
nil
|
143
|
+
end
|
144
|
+
end
|
145
|
+
private :string_rep
|
146
|
+
end
|
147
|
+
end # class ExtensionsProject
|
148
|
+
|
149
|
+
|
150
|
+
if VERSION < "1.8.0"
|
151
|
+
raise "#{ExtensionsProject.project_name} requires Ruby 1.8.0 at least (for now)"
|
152
|
+
end
|
153
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
# A template for new files in the project; of no interest to end users. An
|
3
|
+
# error will be raised if you +require+ it.
|
4
|
+
#--
|
5
|
+
# :enddoc:
|
6
|
+
#
|
7
|
+
# == extensions/XXX.rb
|
8
|
+
#
|
9
|
+
# Adds methods to the builtin XXX class.
|
10
|
+
#
|
11
|
+
|
12
|
+
raise "Do not load this file!"
|
13
|
+
|
14
|
+
require "extensions/_base"
|
15
|
+
|
16
|
+
#
|
17
|
+
# * Enumerable#build_hash
|
18
|
+
#
|
19
|
+
ExtensionsProject.implement(Enumerable, :build_hash) do
|
20
|
+
module Enumerable
|
21
|
+
#
|
22
|
+
# Like #map/#collect, but it generates a Hash.
|
23
|
+
#
|
24
|
+
# [1,5,11].build_hash { |x| [x, x**2] }
|
25
|
+
# => { 1 => 2, 5 => 25, 11 => 121 }
|
26
|
+
#
|
27
|
+
def build_hash
|
28
|
+
result = {}
|
29
|
+
self.each do |elt|
|
30
|
+
key, value = yield elt
|
31
|
+
result[key] = value
|
32
|
+
end
|
33
|
+
result
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#
|
2
|
+
# == extensions/all.rb
|
3
|
+
#
|
4
|
+
# Require this file in order to access all of the standard class extensions
|
5
|
+
# available, or require individual extension files to narrow the selection.
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'extensions/array.rb'
|
9
|
+
require 'extensions/class.rb'
|
10
|
+
require 'extensions/enumerable.rb'
|
11
|
+
require 'extensions/hash.rb'
|
12
|
+
require 'extensions/io.rb'
|
13
|
+
require 'extensions/numeric.rb'
|
14
|
+
require 'extensions/object.rb'
|
15
|
+
require 'extensions/ostruct.rb'
|
16
|
+
require 'extensions/string.rb'
|
17
|
+
require 'extensions/symbol.rb'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
#
|
3
|
+
# == extensions/array.rb
|
4
|
+
#
|
5
|
+
# Adds methods to the builtin Array class.
|
6
|
+
#
|
7
|
+
|
8
|
+
require "extensions/_base"
|
9
|
+
|
10
|
+
#
|
11
|
+
# * Array#select!
|
12
|
+
#
|
13
|
+
ExtensionsProject.implement(Array, :select!) do
|
14
|
+
class Array
|
15
|
+
#
|
16
|
+
# In-place version of Array#select. (Counterpart to, and opposite of, the
|
17
|
+
# built-in #reject!)
|
18
|
+
#
|
19
|
+
def select!
|
20
|
+
reject! { |e| not yield(e) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
#
|
3
|
+
# == extensions/class.rb
|
4
|
+
#
|
5
|
+
# Adds methods to the builtin Class class.
|
6
|
+
#
|
7
|
+
|
8
|
+
require "extensions/_base"
|
9
|
+
|
10
|
+
ExtensionsProject.implement(Class, :autoinit) do
|
11
|
+
class Class
|
12
|
+
#
|
13
|
+
# A shorthand for the common chore of assigning initialize's parameters to
|
14
|
+
# instance variables. For example:
|
15
|
+
#
|
16
|
+
# class Circle
|
17
|
+
#
|
18
|
+
# attr_reader :radius, :location, :area
|
19
|
+
#
|
20
|
+
# autoinit(:radius, :location) do
|
21
|
+
# @area = Math::PI * @radius ** 2
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# A TypeError is raised unless all the arguments to +autoinit+ are strings
|
27
|
+
# or symbols.
|
28
|
+
#
|
29
|
+
#--
|
30
|
+
# Taken from ruby-talk:11668, by Avi Bryant.
|
31
|
+
def autoinit(*args, &block) # :yield:
|
32
|
+
unless args.all? { |a| Symbol === a or String === a }
|
33
|
+
raise TypeError, "All arguments must be symbols or strings"
|
34
|
+
end
|
35
|
+
block = proc {} if block.nil?
|
36
|
+
define_method(:__init_proc) { block }
|
37
|
+
params = args.join(", ")
|
38
|
+
vars = args.map { |a| "@#{a}" }.join(", ")
|
39
|
+
|
40
|
+
code = %{
|
41
|
+
def initialize(#{params})
|
42
|
+
#{vars} = #{params}
|
43
|
+
instance_eval(&__init_proc)
|
44
|
+
end
|
45
|
+
}
|
46
|
+
class_eval code
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,183 @@
|
|
1
|
+
#
|
2
|
+
# == extensions/enumerable.rb
|
3
|
+
#
|
4
|
+
# Adds methods to the builtin Enumerable module.
|
5
|
+
#
|
6
|
+
|
7
|
+
require "extensions/_base"
|
8
|
+
|
9
|
+
#
|
10
|
+
# * Enumerable#build_hash
|
11
|
+
#
|
12
|
+
ExtensionsProject.implement(Enumerable, :build_hash) do
|
13
|
+
module Enumerable
|
14
|
+
#
|
15
|
+
# Like <tt>#map</tt>/<tt>#collect</tt>, but it generates a Hash. The block
|
16
|
+
# is expected to return two values: the key and the value for the new hash.
|
17
|
+
# numbers = (1..3)
|
18
|
+
# squares = numbers.build_hash { |n| [n, n*n] } # 1=>1, 2=>4, 3=>9
|
19
|
+
# sq_roots = numbers.build_hash { |n| [n*n, n] } # 1=>1, 4=>2, 9=>3
|
20
|
+
#
|
21
|
+
def build_hash
|
22
|
+
result = {}
|
23
|
+
self.each do |elt|
|
24
|
+
key, value = yield elt
|
25
|
+
result[key] = value
|
26
|
+
end
|
27
|
+
result
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# There was a bug in Hash which causes the above code to issue a warning when
|
32
|
+
# used with a Hash. That was fixed on 2003-10-24.
|
33
|
+
if RUBY_RELEASE_DATE < "2003-10-25"
|
34
|
+
class Hash #:nodoc:
|
35
|
+
def build_hash
|
36
|
+
result = {}
|
37
|
+
self.each_pair do |k, v|
|
38
|
+
key, value = yield(k, v)
|
39
|
+
result[key] = value
|
40
|
+
end
|
41
|
+
result
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
#
|
49
|
+
# Enumerable#mapf
|
50
|
+
#
|
51
|
+
ExtensionsProject.implement(Enumerable, :mapf) do
|
52
|
+
module Enumerable
|
53
|
+
#
|
54
|
+
# "map function"
|
55
|
+
# enum.mapf(:x)
|
56
|
+
# is short for
|
57
|
+
# enum.map { |elt| elt.x }
|
58
|
+
#
|
59
|
+
def mapf(message)
|
60
|
+
self.map { |elt| elt.send(message) }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
#
|
67
|
+
# Enumerable#collectf
|
68
|
+
#
|
69
|
+
ExtensionsProject.implement(Enumerable, :collectf) do
|
70
|
+
module Enumerable
|
71
|
+
alias collectf mapf
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
#
|
77
|
+
# * Enumerable#includes?
|
78
|
+
#
|
79
|
+
ExtensionsProject.implement(Enumerable, :includes?) do
|
80
|
+
module Enumerable
|
81
|
+
alias includes? include?
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
#
|
87
|
+
# * Enumerable#contains?
|
88
|
+
#
|
89
|
+
ExtensionsProject.implement(Enumerable, :contains?) do
|
90
|
+
module Enumerable
|
91
|
+
alias contains? include?
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
#
|
97
|
+
# * Enumerable#has?
|
98
|
+
#
|
99
|
+
ExtensionsProject.implement(Enumerable, :has?) do
|
100
|
+
module Enumerable
|
101
|
+
alias has? include?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
#
|
107
|
+
# * Enumerable#map_with_index
|
108
|
+
#
|
109
|
+
ExtensionsProject.implement(Enumerable, :map_with_index) do
|
110
|
+
module Enumerable
|
111
|
+
#
|
112
|
+
# Same as Enumerable#map, but the index is yielded as well. See
|
113
|
+
# Enumerable#each_with_index.
|
114
|
+
# puts files.map_with_index { |fn, idx| "#{idx}. #{fn}" }
|
115
|
+
# print "Please select a file (0-#{files.size}): "
|
116
|
+
#
|
117
|
+
def map_with_index
|
118
|
+
result = []
|
119
|
+
self.each_with_index do |elt, idx|
|
120
|
+
result << yield(elt, idx)
|
121
|
+
end
|
122
|
+
result
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
#
|
129
|
+
# * Enumerable#collect_with_index
|
130
|
+
#
|
131
|
+
ExtensionsProject.implement(Enumerable, :collect_with_index) do
|
132
|
+
module Enumerable
|
133
|
+
alias collect_with_index map_with_index
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
#
|
139
|
+
# * Enumerable#partition_by
|
140
|
+
#
|
141
|
+
ExtensionsProject.implement(Enumerable, :partition_by) do
|
142
|
+
module Enumerable
|
143
|
+
#
|
144
|
+
# See Enumerable#partition for the background. #partition_by is best
|
145
|
+
# explained by example.
|
146
|
+
#
|
147
|
+
# (1..5).partition_by { |n| n % 3 }
|
148
|
+
# # -> { 0 => [3], 1 => [1, 4], 2 => [2,5] }
|
149
|
+
#
|
150
|
+
# ["I had", 1, "dollar and", 50, "cents"].partition_by { |e| e.class }
|
151
|
+
# # -> { String => ["I had","dollar and","cents"], Fixnum => [1,50] }
|
152
|
+
#
|
153
|
+
# #partition_by is used to group items in a collection by something they
|
154
|
+
# have in common. The common factor is the key in the resulting hash, the
|
155
|
+
# array of like elements is the value.
|
156
|
+
#
|
157
|
+
def partition_by
|
158
|
+
result = {}
|
159
|
+
self.each do |e|
|
160
|
+
value = yield e
|
161
|
+
(result[value] ||= []) << e
|
162
|
+
end
|
163
|
+
result
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
#
|
170
|
+
# * Object.in?
|
171
|
+
# This has special treatment: it's included here and in object.rb, so we don't
|
172
|
+
# want a warning if it's alredy defined.
|
173
|
+
#
|
174
|
+
unless Object.method_defined?(:in?)
|
175
|
+
ExtensionsProject.implement(Object, :in?) do
|
176
|
+
class Object
|
177
|
+
def in?(enumerable) # :nodoc: It's documented in object.rb.
|
178
|
+
enumerable.include?(self)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|