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.
@@ -0,0 +1,3 @@
1
+ ruby install.rb config
2
+ ruby install.rb setup
3
+ ruby install.rb install
@@ -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
+