range_extd 1.0 → 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.
data/Rakefile CHANGED
@@ -1,9 +1,15 @@
1
1
  require 'rake/testtask'
2
2
 
3
- Rake::TestTask.new do |t|
4
- t.libs << 'test'
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
+