range_extd 1.1.1 → 2.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.
- checksums.yaml +4 -4
- data/ChangeLog +16 -0
- data/Makefile +7 -3
- data/News +4 -0
- data/README.en.rdoc +1146 -270
- data/README.ja.rdoc +1146 -270
- data/Rakefile +11 -5
- data/lib/range_extd/infinity.rb +426 -0
- data/lib/range_extd/load_all.rb +19 -0
- data/lib/range_extd/nil_class.rb +41 -0
- data/lib/range_extd/nowhere.rb +135 -0
- data/lib/range_extd/numeric.rb +160 -0
- data/lib/range_extd/object.rb +53 -0
- data/lib/range_extd/range.rb +401 -0
- data/lib/{range_extd/range_extd.rb → range_extd.rb} +387 -633
- data/range_extd.gemspec +50 -0
- data/test/all_required_test.rb +173 -0
- data/test/test_range_extd.rb +482 -148
- data/test/test_range_extd_nowhere.rb +84 -0
- metadata +29 -16
- data/lib/range_extd/infinity/infinity.rb +0 -600
data/Rakefile
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
require 'rake/testtask'
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
desc "Run tests (Usage: rake test all_required=true/false)"
|
4
|
+
task :test do
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
t.libs << 'test'
|
7
|
+
case ENV['all_required']
|
8
|
+
when 'true', 'yes', 'y', 't', 'T'
|
9
|
+
t.test_files = FileList['test/*_test.rb'] # where many additional require are executed.
|
10
|
+
else
|
11
|
+
# testing default "test/test*.rb"
|
12
|
+
end
|
13
|
+
end
|
5
14
|
end
|
6
15
|
|
7
|
-
desc "Run tests"
|
8
|
-
task :default => :test
|
9
|
-
|
@@ -0,0 +1,426 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
if ! defined?(Rational)
|
4
|
+
require 'rational' # For Ruby 1.8
|
5
|
+
end
|
6
|
+
|
7
|
+
## NOTE: Write nothing for the class description of RangeExtd, because it would have a higher priority for yard!
|
8
|
+
|
9
|
+
|
10
|
+
class RangeExtd < Range
|
11
|
+
# Constant to represent no {RangeExtd}. Note that +(3...3).valid?+, for example, is false under this scheme, and it should be represented with this constant.
|
12
|
+
NONE = :Abstract
|
13
|
+
|
14
|
+
# Constant to represent a general {RangeExtd} that include everything (negative to positive infinities). This is basically a generalized version of range of +(-Float::INFINITY..Float::INFINITY)+ to any (comparable) Class objects.
|
15
|
+
ALL = :Abstract
|
16
|
+
|
17
|
+
#
|
18
|
+
# =Class RangeExtd::Infinity
|
19
|
+
#
|
20
|
+
# Authors:: Masa Sakano
|
21
|
+
# License:: MIT
|
22
|
+
#
|
23
|
+
# ==Summary
|
24
|
+
#
|
25
|
+
# Class to hold just two main constants:
|
26
|
+
# * RangeExtd::Infinity::NEGATIVE
|
27
|
+
# * RangeExtd::Infinity::POSITIVE
|
28
|
+
#
|
29
|
+
# and two internal ones:
|
30
|
+
#
|
31
|
+
# * CLASSES_ACCEPTABLE (see below)
|
32
|
+
# * FLOAT_INFINITY (OBSOLETE; workaround for Ruby 1.8 to represent Float::INFINITY)
|
33
|
+
#
|
34
|
+
# There are no other objects in this class (you cannot create a new one).
|
35
|
+
#
|
36
|
+
# This class includes +Comparable+ module.
|
37
|
+
#
|
38
|
+
# ==Description
|
39
|
+
#
|
40
|
+
# Both the two constant are abstract values which are always smaller/larger,
|
41
|
+
# respectively, than any other Comparable objects (1 or -1 by i{#<=>}(obj))
|
42
|
+
# except for infinities with the same polarity, that is, positive or negative,
|
43
|
+
# in which case 0 is returned.
|
44
|
+
# See the document of the method {#==} for the definition of "infinity".
|
45
|
+
#
|
46
|
+
# +Infinity#succ+ used to be defined up to {RangeExtd} Ver.1 but is removed in Ver.2.
|
47
|
+
#
|
48
|
+
# There is a note of caution.
|
49
|
+
# The method {#<=>} is defined in this class as mentioned above.
|
50
|
+
# However, any operator is, by Ruby's definition, not commutative
|
51
|
+
# unless both the classes define so.
|
52
|
+
#
|
53
|
+
# There are only three built-in classes that are Comparable: String, Time and Numeric
|
54
|
+
# (except for Complex).
|
55
|
+
# Note Date and DateTime objects are so, too, however they need "require",
|
56
|
+
# hence are (and must be) treated, in the same was as with any other classes.
|
57
|
+
#
|
58
|
+
# But whether String, Time, or Numeric class objects, the [#<=>] operator
|
59
|
+
# does work in the commutative way with the instances of this class.
|
60
|
+
# ?z <=> RangeExtd::Infinity::POSITIVE # => nil
|
61
|
+
# RangeExtd::Infinity::POSITIVE <=> ?z # => 1.
|
62
|
+
# 50 <=> RangeExtd::Infinity::POSITIVE # => nil
|
63
|
+
# RangeExtd::Infinity::POSITIVE <=> 50 # => 1.
|
64
|
+
#
|
65
|
+
# For this reason, for example,
|
66
|
+
# (50 .. RangeExtd::Infinity::POSITIVE)
|
67
|
+
# raises an exception, because the Numeric instance 50 does not
|
68
|
+
# know how to compare itself with a {RangeExtd::Infinity} instance,
|
69
|
+
# and {Range} class does not allow such a case.
|
70
|
+
#
|
71
|
+
# To mitigate the inconvenience,
|
72
|
+
# this package provides helper libraries +range_extd/object+
|
73
|
+
# and +range_extd/numeric+ (or all-inclusive wrapper +range_extd/load_all+).
|
74
|
+
# If your code requires them, [#<=>] operators in String and Numeric
|
75
|
+
# will work commutatively with {RangeExtd::Infinity}.
|
76
|
+
# Note that external gem for Numeric like +BigFloat+, if you require it, may not work
|
77
|
+
# straightaway and so the following measure needs to be taken.
|
78
|
+
#
|
79
|
+
# Once the library +range_extd/object+ has been required (your code must
|
80
|
+
# explicitly include the statement +require "range_extd/object"+,
|
81
|
+
# unless your code requires +range_extd/load_all+),
|
82
|
+
# which redefines {Object#<=>} so that the operator in any descendant
|
83
|
+
# class works in a commutative way with {RangeExtd::Infinity} instances.
|
84
|
+
# YourComparable.new <=> RangeExtd::Infinity::POSITIVE # => -1
|
85
|
+
# RangeExtd::Infinity::POSITIVE <=> YourComparable.new # => 1
|
86
|
+
# The condition for it is, though, the method [#<=>] in the descendant class is
|
87
|
+
# written in a sensible manner, that is, it respects the method of
|
88
|
+
# the super-class when it does not know
|
89
|
+
# how to deal with a given object.
|
90
|
+
#
|
91
|
+
# However, some existing Comparable classes, perhaps written by some
|
92
|
+
# one else may not be so polite, and has disabled comparison
|
93
|
+
# with any object but those intended. Unlucky you!
|
94
|
+
# Indeed, the classes like Date and DateTime are one of them.
|
95
|
+
#
|
96
|
+
# For that sort of circumstances,
|
97
|
+
# the class method {RangeExtd::Infinity.overwrite_compare} provides
|
98
|
+
# a convenient way to overcome the problem to (dynamically) make
|
99
|
+
# the operator [#<=>] commutative for a given Comparable class.
|
100
|
+
#
|
101
|
+
# Note {RangeExtd::Infinity.overwrite_compare} does nothing for the classes
|
102
|
+
# registered in the Class constant Array {RangeExtd::Infinity::CLASSES_ACCEPTABLE}.
|
103
|
+
# So, if you want to avoid such modification of the method [#<=>], perhaps
|
104
|
+
# by some other end users, you can register the class in the array.
|
105
|
+
#
|
106
|
+
# Only the instance methods defined in this class are
|
107
|
+
# {#===}, {#==}, {#<=>}, {#to_s}, {#inspect},
|
108
|
+
# {#infinity?}, {#positive?} and {#negative?}. In addition, since Version 1.1,
|
109
|
+
# two unary operators {#@+} and {#@-} to unchange/swap the parity are defined,
|
110
|
+
# (the reason why {#<} and {#>} are modified is to deal with Integer and Float;
|
111
|
+
# I do not know whether the default behaviour of these classes have changed
|
112
|
+
# in the recent versions of Ruby, though).
|
113
|
+
#
|
114
|
+
# === Comparison operators
|
115
|
+
#
|
116
|
+
# {RangeExtd::Infinity::POSITIVE} and InfN {RangeExtd::Infinity::NEGATIVE}
|
117
|
+
# are always comparable with any comparable objects except for
|
118
|
+
# Float::INFINITY, in which case
|
119
|
+
#
|
120
|
+
# (RangeExtd::Infinity::POSITIVE <=> Float::INFINITY) # => nil
|
121
|
+
# (RangeExtd::Infinity::POSITIVE < Float::INFINITY) # => ArgumentError
|
122
|
+
# (RangeExtd::Infinity::POSITIVE > Float::INFINITY) # => ArgumentError
|
123
|
+
# (RangeExtd::Infinity::POSITIVE == Float::INFINITY) # => false
|
124
|
+
#
|
125
|
+
# which is what happens for the comparison operators for Float::INFINITY.
|
126
|
+
#
|
127
|
+
# Basically, the concept of {RangeExtd::Infinity::POSITIVE} is a generalised
|
128
|
+
# concept of Float::INFINITY. Therefore they are really not *equal*.
|
129
|
+
# On the other hand, {RangeExtd::Infinity::POSITIVE} is *greater* than
|
130
|
+
# any normal comparable objects (except those that are *infinite*).
|
131
|
+
# Therefore, all of the following are true ({Object#<=>} and some methods
|
132
|
+
# in some classes are modified)
|
133
|
+
#
|
134
|
+
# (RangeExtd::Infinity::POSITIVE > 5)
|
135
|
+
# (RangeExtd::Infinity::NEGATIVE < 5)
|
136
|
+
#
|
137
|
+
# (RangeExtd::Infinity::POSITIVE > "a")
|
138
|
+
# (RangeExtd::Infinity::NEGATIVE < "a")
|
139
|
+
#
|
140
|
+
# whereas
|
141
|
+
#
|
142
|
+
# (RangeExtd::Infinity::POSITIVE < Object.new) # => ArgumentError
|
143
|
+
#
|
144
|
+
# raises ArgumentError.
|
145
|
+
#
|
146
|
+
class Infinity
|
147
|
+
|
148
|
+
include Comparable
|
149
|
+
|
150
|
+
# Obsolete Constant FLOAT_INFINITY (for the sake of Ruby 1.8 or earlier).
|
151
|
+
# Please do not use it - it will be removed some time in the future.
|
152
|
+
# n.b., Module#private_constant is introduced in Ruby 1.9.3.
|
153
|
+
begin
|
154
|
+
FLOAT_INFINITY = Float::INFINITY
|
155
|
+
rescue # Ruby 1.8 or earlier.
|
156
|
+
FLOAT_INFINITY = 1/0.0
|
157
|
+
end
|
158
|
+
|
159
|
+
# Classes that accept to be compared with Infinity instances.
|
160
|
+
CLASSES_ACCEPTABLE = [self, Float, Integer, Rational, Numeric, String] # Fixnum, Bignum deprecated now.
|
161
|
+
# CLASSES_ACCEPTABLE = [self, Float, Fixnum, Bignum, Rational, Numeric, String] # , BigFloat
|
162
|
+
CLASSES_ACCEPTABLE.push BigFloat if defined? BigFloat
|
163
|
+
|
164
|
+
# Unary Operator: Plus
|
165
|
+
def +@
|
166
|
+
self
|
167
|
+
end
|
168
|
+
|
169
|
+
# Unary Operator: Minus
|
170
|
+
def -@
|
171
|
+
positive? ? NEGATIVE : POSITIVE
|
172
|
+
end
|
173
|
+
|
174
|
+
# returns always true.
|
175
|
+
#
|
176
|
+
# @see Infinity.infinity?
|
177
|
+
def infinity?
|
178
|
+
true
|
179
|
+
end
|
180
|
+
#alias_method :infinite?, :infinity? if !self.method_defined? :infinite? # Common with Float::INFINITY
|
181
|
+
## If the alias for :infinite? is defined as above, the following would raise
|
182
|
+
# NoMethodError: undefined method `>' for true:TrueClass
|
183
|
+
# in the operation
|
184
|
+
# Float::INFINITY <=> RangeExtd::Infinity::POSITIVE
|
185
|
+
#
|
186
|
+
|
187
|
+
# true if self is a positive infinity
|
188
|
+
def positive?
|
189
|
+
@positive
|
190
|
+
end
|
191
|
+
|
192
|
+
# true if self is a negative infinity
|
193
|
+
def negative?
|
194
|
+
!@positive
|
195
|
+
end
|
196
|
+
|
197
|
+
# Backup of the original method {RangeExtd::Infinity#==}
|
198
|
+
alias_method :cmp_before_rangeextd_infinity?, :== if ! self.method_defined?(:cmp_before_rangeextd_infinity?)
|
199
|
+
|
200
|
+
# Always -1 or 1 except for itself and the corresponding infinities (== 0). See {#==}.
|
201
|
+
# Or, nil (as defined by Object), if the argument is not Comparable, such as, nil and IO.
|
202
|
+
#
|
203
|
+
# @return [Integer, nil]
|
204
|
+
def <=>(c)
|
205
|
+
if c.nil? || !c.respond_to?(:<=) # Not Comparable?
|
206
|
+
nil
|
207
|
+
elsif c == Float::INFINITY
|
208
|
+
nil # Special case.
|
209
|
+
else
|
210
|
+
(self == c) ? 0 : (@positive ? 1 : -1)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Backup of the original method {RangeExtd::Infinity#>}
|
215
|
+
alias_method :greater_than_before_rangeextd_infinity?, :> if ! self.method_defined?(:greater_than_before_rangeextd_infinity?)
|
216
|
+
# Special case for Float::INFINITY
|
217
|
+
#
|
218
|
+
# (Float::INFINITY > RangeExtd::Infinity::POSITIVE)
|
219
|
+
# raises ArgumentError and so does this method.
|
220
|
+
def >(c)
|
221
|
+
((c.abs rescue c) == Float::INFINITY) ? raise(ArgumentError, "RangeExtd::Infinity object not comparable with '#{__method__}' with Float::INFINITY") : greater_than_before_rangeextd_infinity?(c)
|
222
|
+
end
|
223
|
+
|
224
|
+
# Backup of the original method {RangeExtd::Infinity#<}
|
225
|
+
alias_method :less_than_before_rangeextd_infinity?, :< if ! self.method_defined?(:less_than_before_rangeextd_infinity?) # No overwriting.
|
226
|
+
# Special case for Float::INFINITY
|
227
|
+
#
|
228
|
+
# (Float::INFINITY > RangeExtd::Infinity::POSITIVE)
|
229
|
+
# raises ArgumentError and so does this method.
|
230
|
+
def <(c)
|
231
|
+
((c.abs rescue c) == Float::INFINITY) ? raise(ArgumentError, "RangeExtd::Infinity object not comparable with '#{__method__}' with Float::INFINITY") : less_than_before_rangeextd_infinity?(c)
|
232
|
+
end
|
233
|
+
|
234
|
+
# Always false except for itself and the corresponding +Float::INFINITY+
|
235
|
+
# and those that have methods of {#infinity?} and {#positive?}
|
236
|
+
# with the corresponding true/false values, in which case this returns true.
|
237
|
+
def ==(c)
|
238
|
+
if (Infinity === c)
|
239
|
+
(@positive ^! c.positive?) # It should be OK to compare object_id?
|
240
|
+
#elsif c == FLOAT_INFINITY && @positive
|
241
|
+
# true
|
242
|
+
#elsif c == -FLOAT_INFINITY && !@positive
|
243
|
+
# true
|
244
|
+
elsif defined?(c.infinity?) && defined?(c.positive?)
|
245
|
+
(c.infinity? && (@positive ^! c.positive?))
|
246
|
+
else
|
247
|
+
false
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# Equivalent to {#==}
|
252
|
+
def ===(c)
|
253
|
+
self == c
|
254
|
+
end
|
255
|
+
|
256
|
+
# This used to be defined till RangeExtd Ver.1
|
257
|
+
## @return [Infinity] self
|
258
|
+
#def succ
|
259
|
+
# self
|
260
|
+
#end
|
261
|
+
|
262
|
+
# @return [String]
|
263
|
+
def inspect
|
264
|
+
if @positive
|
265
|
+
"INFINITY"
|
266
|
+
else
|
267
|
+
"-INFINITY"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
alias_method :to_s, :inspect
|
272
|
+
|
273
|
+
|
274
|
+
# Overwrite [#<=>] method of the given class, if necessary,
|
275
|
+
#
|
276
|
+
# to make its instances be comparable with RangeExtd::Infinity objects (constants).
|
277
|
+
# For example,
|
278
|
+
# RangeExtd::Infinity::NEGATIVE.<=>(any_comparable)
|
279
|
+
# always gives back -1 (except for same infinities). However the other way around,
|
280
|
+
# SomeClass.new.<=>(RangeExtd::Infinity::NEGATIVE)
|
281
|
+
# usually returns nil, which is not handy.
|
282
|
+
#
|
283
|
+
# Therefore, this function (Class method) provides a convenient
|
284
|
+
# way to overcome it, that is, if the given class
|
285
|
+
# (or the class of the given object) is Comparable
|
286
|
+
# and returns +nil+ when compared with {RangeExtd::Infinity}
|
287
|
+
# (note that such a check is only possible when an instance is given
|
288
|
+
# given to this method as the argument),
|
289
|
+
# its [#<=>] method is modified (and true is returned),
|
290
|
+
# unless it has been already done so, or it is one of the classes listed below,
|
291
|
+
# such as Numeric and String, in which case nil is returned.
|
292
|
+
# If it is not Comparable, false is returned. If +<=>+ returns
|
293
|
+
# something other than nil, nil is returned (for it likely
|
294
|
+
# means the class already recognises {RangeExtd::Infinity}).
|
295
|
+
# The judgement whether it is Comparable or not is based
|
296
|
+
# whether the class has an instance method +ThatClass#<=+
|
297
|
+
#
|
298
|
+
# In processing, this method first looks up at an Array
|
299
|
+
# {RangeExtd::Infinity::CLASSES_ACCEPTABLE},
|
300
|
+
# and if the given class is registered in it,
|
301
|
+
# it does nothing. If not, and if all the other conditions
|
302
|
+
# are met, it overwrites its <=> method and register
|
303
|
+
# the class in the array.
|
304
|
+
#
|
305
|
+
# @param obj [Object] Either Class or its instance. An instance is recommended, because an additional check is possible.
|
306
|
+
# @return [Boolean, nil] (see the description).
|
307
|
+
def self.overwrite_compare(obj)
|
308
|
+
if defined? obj.instance_methods
|
309
|
+
klass = obj
|
310
|
+
else
|
311
|
+
klass = obj.class
|
312
|
+
|
313
|
+
begin
|
314
|
+
_ = 1.0 + obj # Use "rescue ArgumentError" if using "1.0<obj"
|
315
|
+
return nil # No change for Numeric
|
316
|
+
rescue TypeError
|
317
|
+
end
|
318
|
+
|
319
|
+
begin
|
320
|
+
cmpval = (obj <=> self::POSITIVE)
|
321
|
+
return nil if !cmpval.nil? # the instance recognises RangeExtd::Infinity
|
322
|
+
rescue NoMethodError
|
323
|
+
return false # <=> is not defined (explicitly disabled, apparently).
|
324
|
+
rescue
|
325
|
+
# If the comparison with Infinity raises an Exception, the method will be modified here.
|
326
|
+
end
|
327
|
+
end # if defined? obj.instance_methods
|
328
|
+
|
329
|
+
# [Numeric, Fixnum, Bignum, Float, Rational, String, Complex].each do |i| # , BigFloat
|
330
|
+
(self::CLASSES_ACCEPTABLE+[self]).each do |i| # , BigFloat
|
331
|
+
# The class itself (RangeExtd::Infinity) must be rejected!
|
332
|
+
# Otherwise the rewrites itself, and may cause an infinite loop.
|
333
|
+
# In fact it is pre-defined in RangeExtd::Infinity, so the above addition is a duplication - just to make sure.
|
334
|
+
return nil if i == klass # No change for Numeric etc
|
335
|
+
# Built-in String, Numeric etc try to flip over "<=>" if it doesn't know the object!
|
336
|
+
end
|
337
|
+
self::CLASSES_ACCEPTABLE.push(klass) # The class is registered, so it would not come here again for the class.
|
338
|
+
|
339
|
+
a = klass.instance_methods
|
340
|
+
if !a.include?( :<= ) # NOT Comparable
|
341
|
+
return false
|
342
|
+
elsif a.include?(:compare_before_infinity)
|
343
|
+
return nil
|
344
|
+
else
|
345
|
+
# Overwrite the definition of "<=>" so that it is fliped over for Infinity.
|
346
|
+
|
347
|
+
code = <<__EOF__
|
348
|
+
alias_method :compare_before_infinity, :<=> if ! self.method_defined?(:compare_before_infinity)
|
349
|
+
def <=>(c)
|
350
|
+
return (-(c.send(__method__, self) || return)) if RangeExtd::Infinity.infinity? c
|
351
|
+
compare_before_infinity(c)
|
352
|
+
end
|
353
|
+
__EOF__
|
354
|
+
|
355
|
+
klass.class_eval(code)
|
356
|
+
|
357
|
+
true
|
358
|
+
end # if !a.include?( :<= ) # NOT Comparable
|
359
|
+
end # def self.overwrite_compare(obj)
|
360
|
+
|
361
|
+
# True if obj is a kind of Infinity like this class (excluding +Float::INFINITY+)
|
362
|
+
#
|
363
|
+
# This is similar to the following but is in a duck-typing way:
|
364
|
+
#
|
365
|
+
# RangeExtd::Infinity === obj
|
366
|
+
#
|
367
|
+
# Note that this returns false for Float::INFINITY.
|
368
|
+
# If you want true for Float::INFINITY, use {RangeExtd::Infinity.infinite?} instead.
|
369
|
+
#
|
370
|
+
# @param obj [Object]
|
371
|
+
def self.infinity?(obj)
|
372
|
+
obj.respond_to?(:infinity?) && obj.respond_to?(:positive?) && obj.respond_to?(:negative?)
|
373
|
+
end
|
374
|
+
|
375
|
+
# True if obj is either +Float::INFINITY+ or Infinity type.
|
376
|
+
#
|
377
|
+
# Note +Float#infinite?+ is defined (and actually it returns 1, not true);
|
378
|
+
# maybe that helps to memorise this method name (as opposed to +infinity?+)?
|
379
|
+
#
|
380
|
+
# @param obj [Object]
|
381
|
+
def self.infinite?(obj)
|
382
|
+
(obj.respond_to?(:infinite?) && obj.infinite?) || (obj.respond_to?(:infinity?) && obj.infinity?)
|
383
|
+
end
|
384
|
+
|
385
|
+
######################################
|
386
|
+
# Special tricky routine below. Do not even touch!
|
387
|
+
######################################
|
388
|
+
|
389
|
+
private
|
390
|
+
|
391
|
+
def initialize(t)
|
392
|
+
@positive = (t && true)
|
393
|
+
end
|
394
|
+
|
395
|
+
#self.remove_const :NEGATIVE if defined? self::NEGATIVE # tricky manoeuvre for documentation purposes... (see infinity.rb for the explanatory document)
|
396
|
+
#self.remove_const :POSITIVE if defined? self::POSITIVE # However, in this case, I have failed to include their descriptions in yard after many attempts, possibly because of "private"... (so these lines are commented out.)
|
397
|
+
NEGATIVE = new(false)
|
398
|
+
POSITIVE = new(true)
|
399
|
+
|
400
|
+
#NEGATIVE.freeze
|
401
|
+
#POSITIVE.freeze
|
402
|
+
|
403
|
+
end # class Infinity
|
404
|
+
|
405
|
+
|
406
|
+
class Infinity
|
407
|
+
# Disable new() so no other object will be created.
|
408
|
+
private_class_method :new
|
409
|
+
|
410
|
+
######################################
|
411
|
+
# Special tricky routine below. Do not rouch!
|
412
|
+
######################################
|
413
|
+
|
414
|
+
warn_level = $VERBOSE
|
415
|
+
begin
|
416
|
+
$VERBOSE = nil # Suppress the warning in the following line.
|
417
|
+
remove_method :initialize
|
418
|
+
ensure
|
419
|
+
$VERBOSE = warn_level
|
420
|
+
end
|
421
|
+
#undef_method :initialize
|
422
|
+
|
423
|
+
end # class Infinity
|
424
|
+
|
425
|
+
end # class RangeExtd < Range
|
426
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
req_files = %w(../range_extd infinity nowhere nil_class numeric object)
|
4
|
+
req_files.each do |req_file|
|
5
|
+
begin
|
6
|
+
require_relative req_file
|
7
|
+
rescue LoadError
|
8
|
+
require req_file
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
if $DEBUG
|
13
|
+
puts "NOTE: Library full paths:"
|
14
|
+
req_files.each do |elibbase|
|
15
|
+
ar = $LOADED_FEATURES.grep(/(^|\/)#{Regexp.quote(File.basename(elibbase))}(\.rb)?$/).uniq
|
16
|
+
print elibbase+": " if ar.empty?; p ar
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
# Adds a couple of methods in NilClass and modifies the behaviour +==+ slightly
|
4
|
+
# so that it returns true for nil-equivalent objects if they return true for +nil?+
|
5
|
+
#
|
6
|
+
# In default, it seems the judgement is based on +other.__id__+.
|
7
|
+
# Note that the method +eql?+ (and of course +equal?+) unchange.
|
8
|
+
#
|
9
|
+
# Here is the summary of the changes:
|
10
|
+
#
|
11
|
+
# * {NilClass#nowhere?} is added, which returns +false+.
|
12
|
+
# * {NilClass#class_raw} is added, which returns {NilClass}
|
13
|
+
# * +(nil == RangeExtd::Nowhere::NOWHERE)+ returns +true+
|
14
|
+
#
|
15
|
+
# To activate these features, explicitly do either of the following
|
16
|
+
# require "range_extd/nil_class"
|
17
|
+
# require "range_extd/load_all"
|
18
|
+
#
|
19
|
+
class NilClass
|
20
|
+
# returns true
|
21
|
+
def nowhere?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
# Identical to +nil.class+
|
26
|
+
#
|
27
|
+
# @return [Class]
|
28
|
+
def class_raw
|
29
|
+
self.class
|
30
|
+
end
|
31
|
+
|
32
|
+
alias_method :double_equals?, :== if ! self.method_defined?(:double_equals?) # No overwriting.
|
33
|
+
|
34
|
+
# returns true if other returns true with +nil?+.
|
35
|
+
#
|
36
|
+
# @return [Boolean]
|
37
|
+
def ==(other)
|
38
|
+
other.nil?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "singleton"
|
4
|
+
|
5
|
+
if ! defined?(Rational)
|
6
|
+
require 'rational' # For Ruby 1.8
|
7
|
+
end
|
8
|
+
|
9
|
+
## NOTE: Write nothing for the class description of RangeExtd, because it would have a higher priority for yard!
|
10
|
+
|
11
|
+
|
12
|
+
class RangeExtd < Range
|
13
|
+
|
14
|
+
#
|
15
|
+
# =Class RangeExtd::Nowhere
|
16
|
+
#
|
17
|
+
# Authors:: Masa Sakano
|
18
|
+
# License:: MIT
|
19
|
+
#
|
20
|
+
# ==Summary
|
21
|
+
#
|
22
|
+
# Singleton Class to host a unique value that behaves like +nil+ except
|
23
|
+
# for a couple of methods to distinguish itself from +nil+ and except
|
24
|
+
# that it is not regarded as the false value in conditional statements.
|
25
|
+
#
|
26
|
+
# ==Description
|
27
|
+
#
|
28
|
+
# The unique value is obtained with {RangeExtd::Nowhere.instance} and is
|
29
|
+
# also available as the class constant:
|
30
|
+
# {RangeExtd::Nowhere::NOWHERE}
|
31
|
+
#
|
32
|
+
# The instance behaves exactly like +nil+ except for the behaviour of the false value
|
33
|
+
# in the conditional statement (because unfortunately there is no way in Ruby
|
34
|
+
# to make the object behave like false/nil in the conditional statement;
|
35
|
+
# see {https://stackoverflow.com/a/14449380/3577922}) and except for some methods
|
36
|
+
# that are defined in +BasicObject+, such as +__id__+ (n.b., by contract, +object_id+
|
37
|
+
# of this class's instance would return the identical id to nil's),
|
38
|
+
# and two custom methods:
|
39
|
+
#
|
40
|
+
# * {#nowhere?} returns +true+
|
41
|
+
# * {#class_raw} returns this class (n.b., the standard +class+ method returns +NilClass+).
|
42
|
+
#
|
43
|
+
# and the equality behaviour with +eql?+, that is,
|
44
|
+
#
|
45
|
+
# RangeExtd::Nowhere::NOWHERE.eql?(nil) # => false
|
46
|
+
#
|
47
|
+
# whereas
|
48
|
+
#
|
49
|
+
# RangeExtd::Nowhere::NOWHERE == nil # => true
|
50
|
+
#
|
51
|
+
# This file in itself does not alter NilClass at all. It is highly recommended to do
|
52
|
+
#
|
53
|
+
# require "range_extd/nil_class"
|
54
|
+
#
|
55
|
+
# to implement these additional features in {NilClass} so that the behaviours
|
56
|
+
# would be comutative. In particular, without requiring it,
|
57
|
+
#
|
58
|
+
# nil == RangeExtd::Nowhere::NOWHERE # => false
|
59
|
+
#
|
60
|
+
# returns false, which is most likely not convenient.
|
61
|
+
# In practice, if you require "+range_extd+", the file is also automatically required
|
62
|
+
# and so you do not have to worry about it, unless you decide to use this class
|
63
|
+
# independently of the main {RangeExtd}.
|
64
|
+
#
|
65
|
+
# ==Note about the behaviour in conditional statements
|
66
|
+
#
|
67
|
+
# The (sole) instance of this class behaves like +true+ like in the conditional statement unlike +nil+
|
68
|
+
# which behaves like the false value. Unfortunately, there is no way in Ruby
|
69
|
+
# to make an object behave like false/nil in the conditional statement;
|
70
|
+
# see {https://stackoverflow.com/a/14449380/3577922}. In the conceptual sense,
|
71
|
+
# however, the difference should not be a problem. If one checks whether
|
72
|
+
# a {Range} (say, +range+) is beginless/endless on the basis of its begin/end value,
|
73
|
+
# the judgement should be based on the method +nil?+ or its equivalent like
|
74
|
+
#
|
75
|
+
# if range.begin.nil?
|
76
|
+
#
|
77
|
+
# and *not*
|
78
|
+
#
|
79
|
+
# if !range.begin
|
80
|
+
#
|
81
|
+
# because the latter does not distinguish +(..a)+ and +(false..false)+.
|
82
|
+
#
|
83
|
+
class Nowhere < BasicObject
|
84
|
+
include ::Singleton
|
85
|
+
|
86
|
+
# returns true
|
87
|
+
def nowhere?
|
88
|
+
true
|
89
|
+
end
|
90
|
+
|
91
|
+
# returns this class {RangeExtd::Nowhere}
|
92
|
+
#
|
93
|
+
# Note that the standard +class+ method returns +NilClass+
|
94
|
+
#
|
95
|
+
# @return [Class]
|
96
|
+
def class_raw
|
97
|
+
::RangeExtd::Nowhere
|
98
|
+
end
|
99
|
+
|
100
|
+
# returns true if other is nil.
|
101
|
+
#
|
102
|
+
# @return [Boolean]
|
103
|
+
def ==(other)
|
104
|
+
other.nil?
|
105
|
+
end
|
106
|
+
|
107
|
+
# returns false if other is the standard nil of NilClass.
|
108
|
+
def eql?(other)
|
109
|
+
self.equal? other
|
110
|
+
end
|
111
|
+
|
112
|
+
## '==' is reflected, hence no need to define.
|
113
|
+
#def <=>(other)
|
114
|
+
# other.nil?
|
115
|
+
#end
|
116
|
+
|
117
|
+
# The hash value is adjusted, which is not strictly guaranteed to be unique, though in pracrtice it is most likely to be so.
|
118
|
+
#
|
119
|
+
# Even without this, `nil.eql?` would return false. However,
|
120
|
+
# it is a special case. Without this, the hash value of {RangeExtd::NONE}
|
121
|
+
# would be the same as that of +RangeExtd(..nil, true)+.
|
122
|
+
#
|
123
|
+
# @return [Integer]
|
124
|
+
def hash(*args)
|
125
|
+
nil.send(:hash, *args) - 1
|
126
|
+
end
|
127
|
+
|
128
|
+
def method_missing(method, *args, &block)
|
129
|
+
return nil.send method, *args, &block
|
130
|
+
end
|
131
|
+
end # class Nowhere < BasicObject
|
132
|
+
|
133
|
+
Nowhere::NOWHERE = self.const_get(:Nowhere).instance
|
134
|
+
end # class RangeExtd < Range
|
135
|
+
|