extensions 0.4.0 → 0.5.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 +12 -0
- data/HISTORY +59 -59
- data/InstalledFiles +50 -0
- data/README +19 -8
- data/README.1st +11 -11
- data/Rakefile +6 -0
- data/VERSION +1 -1
- data/bin/rbxtm +13 -13
- data/etc/website/index.html +10 -10
- data/install.rb +1098 -1098
- data/install.sh +3 -3
- data/lib/extensions/_base.rb +153 -153
- data/lib/extensions/_template.rb +36 -36
- data/lib/extensions/all.rb +19 -17
- data/lib/extensions/array.rb +24 -24
- data/lib/extensions/binding.rb +224 -0
- data/lib/extensions/class.rb +50 -50
- data/lib/extensions/continuation.rb +71 -0
- data/lib/extensions/hash.rb +23 -23
- data/lib/extensions/io.rb +58 -58
- data/lib/extensions/numeric.rb +204 -204
- data/lib/extensions/object.rb +164 -164
- data/lib/extensions/string.rb +316 -316
- data/lib/extensions/symbol.rb +28 -28
- data/test/tc_binding.rb +87 -0
- data/test/tc_continuation.rb +38 -0
- metadata +11 -11
@@ -0,0 +1,224 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
#
|
3
|
+
# == extensions/binding.rb
|
4
|
+
#
|
5
|
+
# Adds methods to the builtin Binding class.
|
6
|
+
#
|
7
|
+
|
8
|
+
require "extensions/_base"
|
9
|
+
require "extensions/continuation"
|
10
|
+
|
11
|
+
#
|
12
|
+
# Ruby's built-in Binding class doesn't contain any methods. It is merely a "context" object
|
13
|
+
# that can be used in calls to <tt>Kernel.eval</tt>, like this:
|
14
|
+
#
|
15
|
+
# def example(_binding)
|
16
|
+
# return eval("x", _binding)
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# x = 55
|
20
|
+
# current_binding = Kernel.binding
|
21
|
+
# example(current_binding) # -> 55
|
22
|
+
#
|
23
|
+
# The most useful method introduced to Binding by the _extensions_ package is
|
24
|
+
# Binding.of_caller. It allows you to access the binding of the calling method, thus
|
25
|
+
# enabling you to access local variables in that scope. The other methods are a convenient
|
26
|
+
# object-oriented facade for operations that you can already do with #eval as demonstrated
|
27
|
+
# above. Here is an example that showcases all of the Binding methods included in
|
28
|
+
# _extensions_.
|
29
|
+
#
|
30
|
+
# def example
|
31
|
+
# Binding.of_caller do |b|
|
32
|
+
# puts "x + y = #{b.eval('x + y')}"
|
33
|
+
# puts "x = #{b[:x]}"
|
34
|
+
# puts "Local variables: " + b.local_variables.join(', ')
|
35
|
+
# b[:y] += 1
|
36
|
+
# puts "Changed value of y in calling context to #{b[:y]}"
|
37
|
+
# puts "Is 'z' defined in calling context? " + (b.defined?(:z) ? 'Yes' : 'No')
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# x = 5
|
42
|
+
# y = 17
|
43
|
+
# example
|
44
|
+
# y # -> 18
|
45
|
+
#
|
46
|
+
# Binding.of_caller was written by Florian Gross. The other methods were written by Tom
|
47
|
+
# Sawyer.
|
48
|
+
#
|
49
|
+
class Binding
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# * Binding.of_caller
|
54
|
+
#
|
55
|
+
ExtensionsProject.implement(Binding, :of_caller, :class) do
|
56
|
+
class Binding
|
57
|
+
#
|
58
|
+
# This method returns the binding of the method that called your method, enabling you to
|
59
|
+
# access its local variables. If you call it without being in a method, it will raise an
|
60
|
+
# Exception.
|
61
|
+
#
|
62
|
+
# === Example
|
63
|
+
#
|
64
|
+
# def inc_counter
|
65
|
+
# Binding.of_caller do |b|
|
66
|
+
# eval("counter += 1", b)
|
67
|
+
# end
|
68
|
+
# # <--- line (A)
|
69
|
+
# end
|
70
|
+
# counter = 0
|
71
|
+
# inc_counter
|
72
|
+
# inc_counter
|
73
|
+
# counter # -> 2
|
74
|
+
#
|
75
|
+
# === Warning
|
76
|
+
#
|
77
|
+
# <tt>Binding.of_caller</tt> must be the _last_ method call in the method. For example,
|
78
|
+
# if you insert some code at line *A* in the example above, an Exception will be raised.
|
79
|
+
# You'll get away with a simple assignment, but anything involving a method call is
|
80
|
+
# trouble.
|
81
|
+
#
|
82
|
+
# === Explanation
|
83
|
+
#
|
84
|
+
# It works by installing a temporary trace_func (see Kernel.set_trace_func). This makes
|
85
|
+
# available -- to the trace function -- the binding of a method after it has returned.
|
86
|
+
# Using a continuation, <tt>Binding.of_caller</tt> will let _your_ method return,
|
87
|
+
# retrieve the binding, and return to the <tt>of_caller</tt> call with that binding in
|
88
|
+
# hand. This time it executes the block.
|
89
|
+
#
|
90
|
+
# Because it is actually running <tt>Binding.of_caller</tt> twice, and returning from
|
91
|
+
# your method twice, any code between the <tt>of_caller</tt> call and the end of your
|
92
|
+
# method will be run twice. This is obviously not desirable, so an Exception is raised
|
93
|
+
# if any code is found.
|
94
|
+
#
|
95
|
+
# See the thread around ruby-talk:109607 for more discussion.
|
96
|
+
#
|
97
|
+
# === Extra Warning
|
98
|
+
#
|
99
|
+
# If you have a trace function in place, <tt>Binding.of_caller</tt> will destroy that.
|
100
|
+
# Ruby does not allow you to access the current trace function, so it can't be restored
|
101
|
+
# afterwards. XXX: will this clash with the profiler and/or debugger?
|
102
|
+
#
|
103
|
+
# === Credits
|
104
|
+
#
|
105
|
+
# <tt>Binding.of_caller</tt> was written by Florian Frank.
|
106
|
+
#
|
107
|
+
def Binding.of_caller(&block)
|
108
|
+
old_critical = Thread.critical
|
109
|
+
Thread.critical = true
|
110
|
+
count = 0
|
111
|
+
cc, result, error = Continuation.create(nil, nil)
|
112
|
+
error.call if error
|
113
|
+
|
114
|
+
tracer = lambda do |*args|
|
115
|
+
type, context = args[0], args[4]
|
116
|
+
if type == "return"
|
117
|
+
count += 1
|
118
|
+
# First this method and then calling one will return --
|
119
|
+
# the trace event of the second event gets the context
|
120
|
+
# of the method which called the method that called this
|
121
|
+
# method.
|
122
|
+
if count == 2
|
123
|
+
# It would be nice if we could restore the trace_func
|
124
|
+
# that was set before we swapped in our own one, but
|
125
|
+
# this is impossible without overloading set_trace_func
|
126
|
+
# in current Ruby.
|
127
|
+
set_trace_func(nil)
|
128
|
+
cc.call(eval("binding", context), nil)
|
129
|
+
end
|
130
|
+
elsif type != "line"
|
131
|
+
set_trace_func(nil)
|
132
|
+
error_msg = "Binding.of_caller used in non-method context or " +
|
133
|
+
"trailing statements of method using it aren't in the block."
|
134
|
+
cc.call(nil, lambda { raise(Exception, error_msg ) })
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
unless result
|
139
|
+
set_trace_func(tracer)
|
140
|
+
return nil
|
141
|
+
else
|
142
|
+
Thread.critical = old_critical
|
143
|
+
yield result
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end # class Binding
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
#
|
151
|
+
# * Binding#eval
|
152
|
+
#
|
153
|
+
ExtensionsProject.implement(Binding, :eval, :instance) do
|
154
|
+
class Binding
|
155
|
+
#
|
156
|
+
# Evaluates the given string in the context of this binding.
|
157
|
+
#
|
158
|
+
def eval(str)
|
159
|
+
Kernel.eval(str, self)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
#
|
166
|
+
# * Binding#local_variables
|
167
|
+
#
|
168
|
+
ExtensionsProject.implement(Binding, :local_variables, :instance) do
|
169
|
+
class Binding
|
170
|
+
#
|
171
|
+
# Returns the variables that are local to this binding.
|
172
|
+
#
|
173
|
+
def local_variables
|
174
|
+
self.eval('local_variables')
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
#
|
181
|
+
# * Binding#[]
|
182
|
+
#
|
183
|
+
ExtensionsProject.implement(Binding, :[], :instance) do
|
184
|
+
class Binding
|
185
|
+
#
|
186
|
+
# Returns the value of the given variable in this binding.
|
187
|
+
#
|
188
|
+
def [](variable)
|
189
|
+
self.eval(variable.to_s)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
#
|
196
|
+
# * Binding#[]=
|
197
|
+
#
|
198
|
+
ExtensionsProject.implement(Binding, :[]=, :instance) do
|
199
|
+
class Binding
|
200
|
+
#
|
201
|
+
# Sets the given variable (in this binding) to the given value.
|
202
|
+
#
|
203
|
+
def []=(variable, value)
|
204
|
+
self.eval("lambda { |v| #{variable} = v }").call(value)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
#
|
211
|
+
# * Binding#defined?
|
212
|
+
#
|
213
|
+
ExtensionsProject.implement(Binding, :defined?, :instance) do
|
214
|
+
class Binding
|
215
|
+
#
|
216
|
+
# Evaluates <tt>defined?</tt> in this binding.
|
217
|
+
#
|
218
|
+
def defined?(variable)
|
219
|
+
self.eval("defined?(#{variable})")
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
|
data/lib/extensions/class.rb
CHANGED
@@ -1,50 +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
|
-
|
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,71 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
#
|
3
|
+
# == extensions/continuation.rb
|
4
|
+
#
|
5
|
+
# Adds methods to the builtin Continuation class.
|
6
|
+
#
|
7
|
+
|
8
|
+
require "extensions/_base"
|
9
|
+
|
10
|
+
#
|
11
|
+
# * Continuation.create
|
12
|
+
#
|
13
|
+
ExtensionsProject.implement(Continuation, :create, :class) do
|
14
|
+
class Continuation
|
15
|
+
#
|
16
|
+
# <tt>Continuation.create</tt> offers a nicer interface for creating continuations than
|
17
|
+
# <tt>Kernel.callcc</tt>.
|
18
|
+
#
|
19
|
+
# === Example
|
20
|
+
#
|
21
|
+
# Count down from 10 to 0 using a continuation.
|
22
|
+
#
|
23
|
+
# continuation, counter = Continuation.create(10)
|
24
|
+
# puts counter
|
25
|
+
# continuation.call(counter - 1) if counter > 0
|
26
|
+
#
|
27
|
+
# Implement a workalike of <tt>Array#inject</tt> using continuations. For simplicity's
|
28
|
+
# sake, this is not fully compatible with the real <tt>#inject</tt>.
|
29
|
+
#
|
30
|
+
# class Array
|
31
|
+
# def cc_inject( value=nil )
|
32
|
+
# copy = self.clone
|
33
|
+
# cc, result, item = Continuation.create( value, nil )
|
34
|
+
# next_item = copy.shift
|
35
|
+
# if result and item
|
36
|
+
# cc.call( yield(result, item), next_item )
|
37
|
+
# elsif next_item
|
38
|
+
# cc.call( next_item, result )
|
39
|
+
# end
|
40
|
+
# result
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# [1,2,3,4,5].cc_inject { |acc, n| acc + n } # -> 15
|
45
|
+
#
|
46
|
+
# === Explanation
|
47
|
+
#
|
48
|
+
# I've got no idea how it works. TODO: work it out. In particular, what do the arguments
|
49
|
+
# do? And what the hell is going on in #cc_inject???!?
|
50
|
+
#
|
51
|
+
# === See Also
|
52
|
+
#
|
53
|
+
# This method is included in the 'extensions' package primarily to support
|
54
|
+
# Binding.of_caller.
|
55
|
+
#
|
56
|
+
# === Credits
|
57
|
+
#
|
58
|
+
# <tt>Continuation.create</tt> was written and demonstrated by Florian Gross. See
|
59
|
+
# ruby-talk:94681.
|
60
|
+
#
|
61
|
+
def Continuation.create(*args, &block)
|
62
|
+
cc = nil
|
63
|
+
result = callcc { |c|
|
64
|
+
cc = c
|
65
|
+
block.call(cc) if block and args.empty?
|
66
|
+
}
|
67
|
+
result ||= args
|
68
|
+
return *[cc, *result]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/extensions/hash.rb
CHANGED
@@ -1,23 +1,23 @@
|
|
1
|
-
#!/usr/local/bin/ruby -w
|
2
|
-
#
|
3
|
-
# == extensions/hash.rb
|
4
|
-
#
|
5
|
-
# Adds methods to the builtin Hash class.
|
6
|
-
#
|
7
|
-
|
8
|
-
require "extensions/_base"
|
9
|
-
|
10
|
-
#
|
11
|
-
# * Hash#select!
|
12
|
-
#
|
13
|
-
ExtensionsProject.implement(Hash, :select!) do
|
14
|
-
class Hash
|
15
|
-
#
|
16
|
-
# In-place version of Hash#select. (Counterpart to, and opposite of, the
|
17
|
-
# built-in #reject!)
|
18
|
-
#
|
19
|
-
def select!
|
20
|
-
reject! { |k,v| not yield(k,v) }
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
#
|
3
|
+
# == extensions/hash.rb
|
4
|
+
#
|
5
|
+
# Adds methods to the builtin Hash class.
|
6
|
+
#
|
7
|
+
|
8
|
+
require "extensions/_base"
|
9
|
+
|
10
|
+
#
|
11
|
+
# * Hash#select!
|
12
|
+
#
|
13
|
+
ExtensionsProject.implement(Hash, :select!) do
|
14
|
+
class Hash
|
15
|
+
#
|
16
|
+
# In-place version of Hash#select. (Counterpart to, and opposite of, the
|
17
|
+
# built-in #reject!)
|
18
|
+
#
|
19
|
+
def select!
|
20
|
+
reject! { |k,v| not yield(k,v) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/extensions/io.rb
CHANGED
@@ -1,58 +1,58 @@
|
|
1
|
-
#!/usr/local/bin/ruby -w
|
2
|
-
|
3
|
-
#
|
4
|
-
# == extensions/io.rb
|
5
|
-
#
|
6
|
-
# Adds methods to the builtin IO class.
|
7
|
-
#
|
8
|
-
|
9
|
-
require "extensions/_base"
|
10
|
-
|
11
|
-
# This is Ruby's built-in IO class.
|
12
|
-
class IO
|
13
|
-
end
|
14
|
-
|
15
|
-
#
|
16
|
-
# * IO.write
|
17
|
-
#
|
18
|
-
ExtensionsProject.implement(IO, :write, :class) do
|
19
|
-
class << IO
|
20
|
-
#
|
21
|
-
# Writes the given data to the given path and closes the file. This is
|
22
|
-
# done in binary mode, complementing <tt>IO.read</tt> in standard Ruby.
|
23
|
-
#
|
24
|
-
# Returns the number of bytes written.
|
25
|
-
#
|
26
|
-
def write(path, data)
|
27
|
-
File.open(path, "wb") do |file|
|
28
|
-
return file.write(data)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
#
|
35
|
-
# * IO.writelines
|
36
|
-
#
|
37
|
-
ExtensionsProject.implement(IO, :writelines, :class) do
|
38
|
-
class << IO
|
39
|
-
#
|
40
|
-
# Writes the given array of data to the given path and closes the file.
|
41
|
-
# This is done in binary mode, complementing <tt>IO.readlines</tt> in
|
42
|
-
# standard Ruby.
|
43
|
-
#
|
44
|
-
# Note that +readlines+ (the standard Ruby method) returns an array of lines
|
45
|
-
# <em>with newlines intact</em>, whereas +writelines+ uses +puts+, and so
|
46
|
-
# appends newlines if necessary. In this small way, +readlines+ and
|
47
|
-
# +writelines+ are not exact opposites.
|
48
|
-
#
|
49
|
-
# Returns +nil+.
|
50
|
-
#
|
51
|
-
def writelines(path, data)
|
52
|
-
File.open(path, "wb") do |file|
|
53
|
-
file.puts(data)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
#
|
4
|
+
# == extensions/io.rb
|
5
|
+
#
|
6
|
+
# Adds methods to the builtin IO class.
|
7
|
+
#
|
8
|
+
|
9
|
+
require "extensions/_base"
|
10
|
+
|
11
|
+
# This is Ruby's built-in IO class.
|
12
|
+
class IO
|
13
|
+
end
|
14
|
+
|
15
|
+
#
|
16
|
+
# * IO.write
|
17
|
+
#
|
18
|
+
ExtensionsProject.implement(IO, :write, :class) do
|
19
|
+
class << IO
|
20
|
+
#
|
21
|
+
# Writes the given data to the given path and closes the file. This is
|
22
|
+
# done in binary mode, complementing <tt>IO.read</tt> in standard Ruby.
|
23
|
+
#
|
24
|
+
# Returns the number of bytes written.
|
25
|
+
#
|
26
|
+
def write(path, data)
|
27
|
+
File.open(path, "wb") do |file|
|
28
|
+
return file.write(data)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# * IO.writelines
|
36
|
+
#
|
37
|
+
ExtensionsProject.implement(IO, :writelines, :class) do
|
38
|
+
class << IO
|
39
|
+
#
|
40
|
+
# Writes the given array of data to the given path and closes the file.
|
41
|
+
# This is done in binary mode, complementing <tt>IO.readlines</tt> in
|
42
|
+
# standard Ruby.
|
43
|
+
#
|
44
|
+
# Note that +readlines+ (the standard Ruby method) returns an array of lines
|
45
|
+
# <em>with newlines intact</em>, whereas +writelines+ uses +puts+, and so
|
46
|
+
# appends newlines if necessary. In this small way, +readlines+ and
|
47
|
+
# +writelines+ are not exact opposites.
|
48
|
+
#
|
49
|
+
# Returns +nil+.
|
50
|
+
#
|
51
|
+
def writelines(path, data)
|
52
|
+
File.open(path, "wb") do |file|
|
53
|
+
file.puts(data)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|