Class: Unbounded::Range

Inherits:
Range
  • Object
show all
Includes:
Formats
Defined in:
lib/unbounded/range.rb

Overview

An unbounded (infinite) range extension for the standard Ruby Range class.

Constant Summary

Constants included from Formats

Formats::POSTGRES_FORMAT, Formats::SIMPLE_FORMAT

Enumerable Overrides collapse

Instance Method Summary collapse

Methods included from Formats

#alter_by_postgres_style!, #alter_by_simple_style!, #parse_for_range, #parse_standard_range_options, #parse_string_for_range_options

Methods included from Utility

#match_to_hash, #numerify

Constructor Details

#initialize(range_start, range_minimum, exclude_end = false) ⇒ Range #initialize(range_string) ⇒ Range

Returns a new instance of Range.

Overloads:

  • #initialize(range_start, range_minimum, exclude_end = false) ⇒ Range

    Create a range the traditional way, à la Range.new.

    Parameters:

    • range_start (#succ)
    • range_end (#succ)
    • exclude_end (Boolean) (defaults to: false)
  • #initialize(range_string) ⇒ Range

    Create a range in the same manner as you would in PostgreSQL

    Parameters:

    • range_string (String)


19
20
21
22
23
24
25
# File 'lib/unbounded/range.rb', line 19

def initialize(*args)
  parsed = parse_for_range(args)

  @format = parsed.format

  super(parsed.minimum, parsed.maximum, parsed.exclude_end)
end

Class Method Details

.humanized(first, last) ⇒ String

Parameters:

  • first (Numeric)
  • last (Numeric)

Returns:

  • (String)


143
144
145
# File 'lib/unbounded/range.rb', line 143

def humanized(first, last)
  return new(first, last).humanized
end

.unbounded_endpoint(type, int_to_return) ⇒ void (private)

This method returns an undefined value.

Parameters:

  • type (Symbol)

    should be :min or :max

  • int_to_return (Integer)

    integer to return for bitwise comparison in #unbounded?



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/unbounded/range.rb', line 164

def unbounded_endpoint(type, int_to_return)
  attr_name = type == :min ? "begin" : "end"

  class_eval <<-RUBY, __FILE__, __LINE__ + 1
    def unbounded_#{type}imum
      self.#{attr_name}.respond_to?(:infinite?) && self.#{attr_name}.infinite? ? #{int_to_return} : 0
    end

    def unbounded_#{type}imum?
      unbounded_#{type}imum.nonzero?
    end

    def finite_#{type}imum?
      !unbounded_#{type}imum?
    end

    private :unbounded_#{type}imum
  RUBY
end

Instance Method Details

#count(*args, &block) ⇒ Integer, INFINITY

Note:

This would not be true in some cases, e.g. checking for negative numbers on a range of 0 to infinity.

Same as Enumerable#count, except in cases where the collection is #unbounded?.

Returns:

  • (Integer, INFINITY)

    Infinity when unbounded, super otherwise



78
79
80
# File 'lib/unbounded/range.rb', line 78

def count(*args, &block)
  unbounded? ? INFINITY : super
end

#finite_maximum?Boolean

Inverse complement of #unbounded_maximum?

Returns:

  • (Boolean)


186
# File 'lib/unbounded/range.rb', line 186

unbounded_endpoint :max, 2

#finite_minimum?Boolean

Inverse complement of #unbounded_minimum?

Returns:

  • (Boolean)


185
# File 'lib/unbounded/range.rb', line 185

unbounded_endpoint :min, 1

#firstNumeric #first(n) ⇒ Array<#succ>

Overloads:

  • #firstNumeric

    Returns equivalent to #min.

    Returns:

    • (Numeric)

      equivalent to #min

  • #first(n) ⇒ Array<#succ>

    Returns first n-elements of the range.

    Parameters:

    • n (Integer)

    Returns:

    • (Array<#succ>)

      first n-elements of the range



87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/unbounded/range.rb', line 87

def first(*args)
  n = args.shift

  if unbounded_minimum? && (n.nil? || n.kind_of?(Integer))
    if n.nil?
      min
    else
      Array.new(n, min)
    end
  else
    return n.nil? ? super() : super(n)
  end
end

#humanizedString

TODO:

i18n support

Returns a more human-readable format.

Returns:

  • (String)

    a more human-readable format



29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/unbounded/range.rb', line 29

def humanized
  case unbounded?
  when :infinite
    "infinite"
  when :maximum
    "#{self.begin}+"
  when :minimum
    exclude_end? ? "fewer than #{self.end}" : "#{self.end} or fewer"
  else
    super
  end
end

#infinite?Boolean

Returns:

  • (Boolean)


43
44
45
# File 'lib/unbounded/range.rb', line 43

def infinite?
  numeric? && ( ( unbounded_minimum | unbounded_maximum ) == 3 )
end

#lastNumeric #last(n) ⇒ Array<#succ>

Overloads:

  • #lastNumeric

    Returns equivalent to #max.

    Returns:

    • (Numeric)

      equivalent to #max

  • #last(n) ⇒ Array<#succ>

    Returns last n-elements of the range.

    Parameters:

    • n (Integer)

    Returns:

    • (Array<#succ>)

      last n-elements of the range



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/unbounded/range.rb', line 106

def last(*args)
  n = args.shift

  if unbounded? && (n.nil? || n.kind_of?(Integer))
    if n.nil?
      max
    elsif unbounded? == :minimum # To prevent `cannot iterate from Float`
      new_minimum = max - (n - 1)

      (new_minimum..max).to_a
    else
      Array.new(n, max) # Array of Infinity
    end
  else
    return n.nil? ? super() : super(n)
  end
end

#maxObject

Overload for Range#max to account for "unbounded_maximum? && exclude_end?" edge case



125
126
127
128
129
# File 'lib/unbounded/range.rb', line 125

def max
  super
rescue TypeError
  self.end
end

#minmaxArray(Numeric, Numeric)

The default implementation of Range#minmax for needlessly uses #each, which can cause infinite enumeration.

Returns:

  • (Array(Numeric, Numeric))


134
135
136
# File 'lib/unbounded/range.rb', line 134

def minmax
  [min, unbounded? ? INFINITY : max]
end

#numeric?Boolean

Returns whether this range is numeric.

Returns:

  • (Boolean)

    whether this range is numeric



48
49
50
# File 'lib/unbounded/range.rb', line 48

def numeric?
  self.begin.kind_of?(Numeric) && self.end.kind_of?(Numeric)
end

#unboundedself

For compatibility with Unbounded::RangeExtension#unbounded unded?

Returns:

  • (self)


55
56
57
# File 'lib/unbounded/range.rb', line 55

def unbounded
  self
end

#unbounded?(unbounded_type = nil) ⇒ Symbol?

Check whether this range is unbounded in some way.

Returns:

  • (Symbol, nil)

    returns a symbol for the type of unboundedness, nil otherwise



61
62
63
64
65
66
67
68
69
70
71
# File 'lib/unbounded/range.rb', line 61

def unbounded?(unbounded_type = nil)
  return false unless numeric?

  unboundedness = case unbounded_minimum | unbounded_maximum
  when 3 then :infinite
  when 2 then :maximum
  when 1 then :minimum
  end

  return unbounded_type && unboundedness ? ( unboundedness == :infinite ) || ( unboundedness == unbounded_type ) : unboundedness
end

#unbounded_maximum2, 0 (private)

Determine whether the max is unbounded.

Returns:

  • (2, 0)

    2 if unbounded, 0 if otherwise



186
# File 'lib/unbounded/range.rb', line 186

unbounded_endpoint :max, 2

#unbounded_maximum?Integer?

Predicate method for #unbounded_maximum that checks against zero

Returns:

  • (Integer, nil)


186
# File 'lib/unbounded/range.rb', line 186

unbounded_endpoint :max, 2

#unbounded_minimum1, 0 (private)

Determine whether the min is unbounded.

Returns:

  • (1, 0)

    1 if unbounded, 0 if otherwise



185
# File 'lib/unbounded/range.rb', line 185

unbounded_endpoint :min, 1

#unbounded_minimum?Integer?

Predicate method for #unbounded_minimum that checks against zero

Returns:

  • (Integer, nil)


185
# File 'lib/unbounded/range.rb', line 185

unbounded_endpoint :min, 1