extensions 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|