unbounded 0.0.1
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 +7 -0
- data/.gitignore +17 -0
- data/.pryrc +4 -0
- data/.rspec +2 -0
- data/.yardopts +5 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +57 -0
- data/Rakefile +28 -0
- data/lib/unbounded/formats.rb +110 -0
- data/lib/unbounded/infinite_enumerator.rb +10 -0
- data/lib/unbounded/range.rb +188 -0
- data/lib/unbounded/range_extension.rb +21 -0
- data/lib/unbounded/utility.rb +37 -0
- data/lib/unbounded/version.rb +7 -0
- data/lib/unbounded.rb +35 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/shared_context_for_postgres_style_ranges.rb +15 -0
- data/spec/support/shared_examples_for_a_finite_maximum.rb +15 -0
- data/spec/support/shared_examples_for_a_finite_minimum.rb +15 -0
- data/spec/support/shared_examples_for_an_excluded_end.rb +9 -0
- data/spec/support/shared_examples_for_an_infinite_range.rb +15 -0
- data/spec/support/shared_examples_for_an_unbounded_maximum.rb +27 -0
- data/spec/support/shared_examples_for_an_unbounded_minimum.rb +25 -0
- data/spec/support/shared_examples_for_any_unbounded_range.rb +13 -0
- data/spec/support/shared_examples_for_humanized_string.rb +36 -0
- data/spec/support/shared_examples_for_numerification.rb +46 -0
- data/spec/support/shared_examples_for_postgres_range_exclusivity.rb +46 -0
- data/spec/unbounded/range_extension_spec.rb +16 -0
- data/spec/unbounded/range_spec.rb +127 -0
- data/spec/unbounded/utility_spec.rb +54 -0
- data/spec/unbounded_spec.rb +11 -0
- data/unbounded.gemspec +29 -0
- metadata +191 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c98cc5d255568b4124a3ae5e87269348e2971136
|
4
|
+
data.tar.gz: ae0242ba93586dc599b2bf606b216dddcd19054b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0fcc4b3258a7fd16b7c193c342f99e6c271bdb6a2be04e5e5b822d7471f46ef8c030ed4c6d8b259c7e2f8c6600a19e05d4dad1edf25b416f8556322698027fa8
|
7
|
+
data.tar.gz: 4e72d60a82c534ba8b1d84cc833900c473de8f62b19293b5a2f89dbf06f5e7bcc8ffa9c05f165c1ad2971e8aa8fb89b025ae66ec5e42a0ae18c74f7db514d55c
|
data/.gitignore
ADDED
data/.pryrc
ADDED
data/.rspec
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Alexa Grey
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# Unbounded
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'unbounded'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
```shell
|
16
|
+
bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
```shell
|
22
|
+
gem install unbounded
|
23
|
+
```
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
Create an unbounded range by passing nil as one of the end points:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
normal_style = Unbounded::Range.new(0, nil) # => 0..INFINITY
|
31
|
+
```
|
32
|
+
|
33
|
+
... Or using postgres-style ranges
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
postgres_style = Unbounded::Range.new('[0,100)') # => 0...100
|
37
|
+
```
|
38
|
+
|
39
|
+
Alternatively, convert a vanilla range:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
converted = (1..3).unbounded
|
43
|
+
converted.kind_of? Unbounded::Range # => true
|
44
|
+
```
|
45
|
+
|
46
|
+
## Contributing
|
47
|
+
|
48
|
+
1. Fork it
|
49
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
50
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
51
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
52
|
+
5. Create new Pull Request
|
53
|
+
|
54
|
+
## Todo
|
55
|
+
|
56
|
+
* invertable ranges (at least for numeric)
|
57
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
|
5
|
+
require "rspec/core/rake_task"
|
6
|
+
require 'yard'
|
7
|
+
|
8
|
+
RSpec::Core::RakeTask.new('spec')
|
9
|
+
|
10
|
+
desc 'Generate docs'
|
11
|
+
YARD::Rake::YardocTask.new(:doc) do |t|
|
12
|
+
t.files = ['lib/**/*.rb']
|
13
|
+
t.options = ['--protected', '--private', '-r', 'README.md']
|
14
|
+
end
|
15
|
+
|
16
|
+
namespace :doc do
|
17
|
+
desc 'Generate and publish YARD docs to github pages.'
|
18
|
+
task :publish => ['doc'] do
|
19
|
+
Dir.chdir(File.dirname(__FILE__) + '/../doc') do
|
20
|
+
system %Q{git add .}
|
21
|
+
system %Q{git add -u}
|
22
|
+
system %Q{git commit -m 'Generating docs for version #{version}.'}
|
23
|
+
system %Q{git push origin gh-pages}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
task :default => :spec
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Unbounded
|
4
|
+
# Methods for parsing alternative range formats
|
5
|
+
# Presently there are two:
|
6
|
+
#
|
7
|
+
# * {POSTGRES_FORMAT}
|
8
|
+
# * {SIMPLE_FORMAT}
|
9
|
+
module Formats
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
# @!parse include Unbounded::Utility
|
13
|
+
include Unbounded::Utility
|
14
|
+
|
15
|
+
# A regular expression that loosely describes the format used within Postgres
|
16
|
+
# for their range types. They look something like:
|
17
|
+
#
|
18
|
+
# * `"[4,6]"` == `4..6`
|
19
|
+
# * `"[1,9)"` == `1...9`
|
20
|
+
#
|
21
|
+
# An endpoint can be omitted to create an unbounded type:
|
22
|
+
#
|
23
|
+
# * `"[1,]"` == `1..INFINITY`
|
24
|
+
# * `"[,0)"` == `-INFINITY...0`
|
25
|
+
#
|
26
|
+
# @see http://www.postgresql.org/docs/9.2/static/rangetypes.html for more information
|
27
|
+
# @return [Regexp]
|
28
|
+
POSTGRES_FORMAT=/^
|
29
|
+
(?<lower_inc>[\[\(])
|
30
|
+
(?<minimum>\S*)
|
31
|
+
,\s*
|
32
|
+
(?<maximum>\S*)
|
33
|
+
(?<upper_inc>[\)\]])
|
34
|
+
$/x
|
35
|
+
|
36
|
+
# A regular expression for parsing the simple format used in
|
37
|
+
# Range.new shorthand, e.g. `'1..3'`, `'4...10'`
|
38
|
+
# @return [Regexp]
|
39
|
+
SIMPLE_FORMAT=/^(?<minimum>[^\.]+)(?<inclusiveness>\.\.\.?)(?<maximum>[^\.]+)$/
|
40
|
+
|
41
|
+
# @param [Array] args arguments from {Unbounded::Range#initialize}
|
42
|
+
# @return [OpenStruct]
|
43
|
+
def parse_for_range(args)
|
44
|
+
hashed = case args.length
|
45
|
+
when 0 then parse_standard_range_options(nil, nil)
|
46
|
+
when 1 then parse_string_for_range_options(args.shift)
|
47
|
+
when 2..3 then parse_standard_range_options(*args)
|
48
|
+
end.presence or raise ArgumentError, "Don't know how to parse arguments: #{args.inspect}"
|
49
|
+
|
50
|
+
|
51
|
+
OpenStruct.new(hashed).tap do |o|
|
52
|
+
if o.format == :postgres
|
53
|
+
alter_by_postgres_style! o
|
54
|
+
elsif o.format == :simple
|
55
|
+
alter_by_simple_style! o
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
# Parse the standard range initialization options
|
62
|
+
# @see Range#initialize
|
63
|
+
# @param [Object] min
|
64
|
+
# @param [Object] max
|
65
|
+
# @param [Boolean] exclude_end
|
66
|
+
# @return [{format: :standard, minimum: Numeric, maximum: Numeric, exclude_end: Boolean}]
|
67
|
+
def parse_standard_range_options(min, max, exclude_end = false)
|
68
|
+
{
|
69
|
+
:format => :standard,
|
70
|
+
:minimum => min.presence || NINFINITY,
|
71
|
+
:maximum => max.presence || INFINITY,
|
72
|
+
:exclude_end => !!exclude_end
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
# Parse string using {POSTGRES_FORMAT} or {SIMPLE_FORMAT}.
|
77
|
+
# @param [#to_s] s
|
78
|
+
# @return [Hash]
|
79
|
+
def parse_string_for_range_options(s)
|
80
|
+
case s.to_s
|
81
|
+
when POSTGRES_FORMAT
|
82
|
+
match_to_hash(Regexp.last_match).merge(:format => :postgres)
|
83
|
+
when SIMPLE_FORMAT
|
84
|
+
match_to_hash(Regexp.last_match).merge(:format => :simple)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Modify the options slightly for postgres formats.
|
89
|
+
# @param [OpenStruct] options
|
90
|
+
# @return [void]
|
91
|
+
def alter_by_postgres_style!(options)
|
92
|
+
options.minimum = numerify(options.minimum, NINFINITY)
|
93
|
+
options.maximum = numerify(options.maximum, INFINITY)
|
94
|
+
options.exclude_end = options.upper_inc == ')'
|
95
|
+
|
96
|
+
if options.lower_inc == '('
|
97
|
+
options.minimum += 1
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Modify the options slightly to account for simple (e.g. 1..3) formatting
|
102
|
+
# @param [OpenStruct] options
|
103
|
+
# @return [void]
|
104
|
+
def alter_by_simple_style!(options)
|
105
|
+
options.minimum = numerify(options.minimum, NINFINITY)
|
106
|
+
options.maximum = numerify(options.maximum, INFINITY)
|
107
|
+
options.exclude_end = options.inclusiveness.length == 3
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Unbounded
|
4
|
+
# An unbounded (infinite) range extension for the standard Ruby Range class.
|
5
|
+
class Range < ::Range
|
6
|
+
include Formats
|
7
|
+
|
8
|
+
RANGE_FORMATS[:humanized] = proc { |first, last| ::Unbounded::Range.humanized(first, last) }
|
9
|
+
RANGE_FORMATS[:postgres] = proc { |first, last| ::Unbounded::Range.postgres(first, last) }
|
10
|
+
|
11
|
+
# @overload initialize(range_start, range_minimum, exclude_end = false)
|
12
|
+
# Create a range the traditional way, à la `Range.new`.
|
13
|
+
# @param [#succ] range_start
|
14
|
+
# @param [#succ] range_end
|
15
|
+
# @param [Boolean] exclude_end
|
16
|
+
# @overload initialize(range_string)
|
17
|
+
# Create a range in the same manner as you would in PostgreSQL
|
18
|
+
# @param [String] range_string
|
19
|
+
def initialize(*args)
|
20
|
+
parsed = parse_for_range(args)
|
21
|
+
|
22
|
+
@format = parsed.format
|
23
|
+
|
24
|
+
super(parsed.minimum, parsed.maximum, parsed.exclude_end)
|
25
|
+
end
|
26
|
+
|
27
|
+
# @todo i18n support
|
28
|
+
# @return [String] a more human-readable format
|
29
|
+
def humanized
|
30
|
+
case unbounded?
|
31
|
+
when :infinite
|
32
|
+
"infinite"
|
33
|
+
when :maximum
|
34
|
+
"#{self.begin}+"
|
35
|
+
when :minimum
|
36
|
+
exclude_end? ? "fewer than #{self.end}" : "#{self.end} or fewer"
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Boolean]
|
43
|
+
def infinite?
|
44
|
+
numeric? && ( ( unbounded_minimum | unbounded_maximum ) == 3 )
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Boolean] whether this range is numeric
|
48
|
+
def numeric?
|
49
|
+
self.begin.kind_of?(Numeric) && self.end.kind_of?(Numeric)
|
50
|
+
end
|
51
|
+
|
52
|
+
# For compatibility with {Unbounded::RangeExtension#unbounded}
|
53
|
+
# unded?
|
54
|
+
# @return [self]
|
55
|
+
def unbounded
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
# Check whether this range is unbounded in some way.
|
60
|
+
# @return [Symbol,nil] returns a symbol for the type of unboundedness, nil otherwise
|
61
|
+
def unbounded?(unbounded_type = nil)
|
62
|
+
return false unless numeric?
|
63
|
+
|
64
|
+
unboundedness = case unbounded_minimum | unbounded_maximum
|
65
|
+
when 3 then :infinite
|
66
|
+
when 2 then :maximum
|
67
|
+
when 1 then :minimum
|
68
|
+
end
|
69
|
+
|
70
|
+
return unbounded_type && unboundedness ? ( unboundedness == :infinite ) || ( unboundedness == unbounded_type ) : unboundedness
|
71
|
+
end
|
72
|
+
|
73
|
+
# @!group Enumerable Overrides
|
74
|
+
# Same as `Enumerable#count`, except in cases where the collection is {Range#unbounded?}.
|
75
|
+
# @note This would not be true in some cases, e.g. checking for negative
|
76
|
+
# numbers on a range of 0 to infinity.
|
77
|
+
# @return [Integer, INFINITY] Infinity when unbounded, super otherwise
|
78
|
+
def count(*args, &block)
|
79
|
+
unbounded? ? INFINITY : super
|
80
|
+
end
|
81
|
+
|
82
|
+
# @overload first
|
83
|
+
# @return [Numeric] equivalent to `#min`
|
84
|
+
# @overload first(n)
|
85
|
+
# @param [Integer] n
|
86
|
+
# @return [Array<#succ>] first n-elements of the range
|
87
|
+
def first(*args)
|
88
|
+
n = args.shift
|
89
|
+
|
90
|
+
if unbounded_minimum? && (n.nil? || n.kind_of?(Integer))
|
91
|
+
if n.nil?
|
92
|
+
min
|
93
|
+
else
|
94
|
+
Array.new(n, min)
|
95
|
+
end
|
96
|
+
else
|
97
|
+
return n.nil? ? super() : super(n)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# @overload last
|
102
|
+
# @return [Numeric] equivalent to {#max}
|
103
|
+
# @overload last(n)
|
104
|
+
# @param [Integer] n
|
105
|
+
# @return [Array<#succ>] last n-elements of the range
|
106
|
+
def last(*args)
|
107
|
+
n = args.shift
|
108
|
+
|
109
|
+
if unbounded? && (n.nil? || n.kind_of?(Integer))
|
110
|
+
if n.nil?
|
111
|
+
max
|
112
|
+
elsif unbounded? == :minimum # To prevent `cannot iterate from Float`
|
113
|
+
new_minimum = max - (n - 1)
|
114
|
+
|
115
|
+
(new_minimum..max).to_a
|
116
|
+
else
|
117
|
+
Array.new(n, max) # Array of Infinity
|
118
|
+
end
|
119
|
+
else
|
120
|
+
return n.nil? ? super() : super(n)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Overload for `Range#max` to account for "unbounded_maximum? && exclude_end?" edge case
|
125
|
+
def max
|
126
|
+
super
|
127
|
+
rescue TypeError
|
128
|
+
self.end
|
129
|
+
end
|
130
|
+
|
131
|
+
# The default implementation of `Range#minmax` for needlessly uses `#each`,
|
132
|
+
# which can cause infinite enumeration.
|
133
|
+
# @return [Array(Numeric, Numeric)]
|
134
|
+
def minmax
|
135
|
+
[min, unbounded? ? INFINITY : max]
|
136
|
+
end
|
137
|
+
# @!endgroup
|
138
|
+
|
139
|
+
class << self
|
140
|
+
# @param [Numeric] first
|
141
|
+
# @param [Numeric] last
|
142
|
+
# @return [String]
|
143
|
+
def humanized(first, last)
|
144
|
+
return new(first, last).humanized
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
# @param [Symbol] type should be :min or :max
|
149
|
+
# @param [Integer] int_to_return integer to return for bitwise comparison in {#unbounded?}
|
150
|
+
# @return [void]
|
151
|
+
# @!macro [attach] unbounded_endpoint
|
152
|
+
# @!method unbounded_$1imum
|
153
|
+
# Determine whether the $1 is unbounded.
|
154
|
+
# @!visibility private
|
155
|
+
# @return [$2,0] $2 if unbounded, 0 if otherwise
|
156
|
+
#
|
157
|
+
# @!method unbounded_$1imum?
|
158
|
+
# Predicate method for {#unbounded_$1imum} that checks against zero
|
159
|
+
# @return [Integer,nil]
|
160
|
+
#
|
161
|
+
# @!method finite_$1imum?
|
162
|
+
# Inverse complement of {#unbounded_$1imum?}
|
163
|
+
# @return [Boolean]
|
164
|
+
def unbounded_endpoint(type, int_to_return)
|
165
|
+
attr_name = type == :min ? "begin" : "end"
|
166
|
+
|
167
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
168
|
+
def unbounded_#{type}imum
|
169
|
+
self.#{attr_name}.respond_to?(:infinite?) && self.#{attr_name}.infinite? ? #{int_to_return} : 0
|
170
|
+
end
|
171
|
+
|
172
|
+
def unbounded_#{type}imum?
|
173
|
+
unbounded_#{type}imum.nonzero?
|
174
|
+
end
|
175
|
+
|
176
|
+
def finite_#{type}imum?
|
177
|
+
!unbounded_#{type}imum?
|
178
|
+
end
|
179
|
+
|
180
|
+
private :unbounded_#{type}imum
|
181
|
+
RUBY
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
unbounded_endpoint :min, 1
|
186
|
+
unbounded_endpoint :max, 2
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Unbounded
|
4
|
+
# Extensions for the standard Ruby `Range` class.
|
5
|
+
module RangeExtension
|
6
|
+
extend ::ActiveSupport::Concern
|
7
|
+
|
8
|
+
# @return [String] humanized string of the range
|
9
|
+
def humanized
|
10
|
+
"#{self.begin} \u2013 #{self.end}"
|
11
|
+
end
|
12
|
+
|
13
|
+
# Transform this into an Unbounded::Range.
|
14
|
+
# @return [Unbounded::Range]
|
15
|
+
def unbounded
|
16
|
+
::Unbounded::Range.new(self.min, self.max, exclude_end?)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Range.send(:include, Unbounded::RangeExtension)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Unbounded
|
4
|
+
# Shared utility methods
|
5
|
+
module Utility
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
# Convert matchdata to a hash.
|
9
|
+
# @param [MatchData] match
|
10
|
+
# @return [Hash]
|
11
|
+
def match_to_hash(match)
|
12
|
+
Hash[match.names.zip(match.captures)]
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [Object] thing something to numerify
|
16
|
+
# @param [Object] default_value a value to return if `thing` fails to numerify.
|
17
|
+
# If it is `:original`, it will return the value of `thing` as-is.
|
18
|
+
# @return [Numeric, Object, nil]
|
19
|
+
def numerify(thing, default_value = nil)
|
20
|
+
return thing if thing.is_a?(Numeric)
|
21
|
+
|
22
|
+
if thing.respond_to?(:include?) && thing.include?('.')
|
23
|
+
Float(thing)
|
24
|
+
else
|
25
|
+
Integer(thing)
|
26
|
+
end
|
27
|
+
rescue ArgumentError, TypeError
|
28
|
+
if default_value == :original
|
29
|
+
thing
|
30
|
+
else
|
31
|
+
default_value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
extend self
|
36
|
+
end
|
37
|
+
end
|
data/lib/unbounded.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
require 'active_support/core_ext/object/blank'
|
5
|
+
require 'active_support/core_ext/object/try'
|
6
|
+
require 'active_support/core_ext/range'
|
7
|
+
|
8
|
+
require 'ostruct'
|
9
|
+
|
10
|
+
require 'unbounded/version'
|
11
|
+
|
12
|
+
# A library for working with unbounded (infinite) ranges,
|
13
|
+
# with support for PostgreSQL-style notation.
|
14
|
+
module Unbounded
|
15
|
+
# Infinity
|
16
|
+
INFINITY = 1.0 / 0.0
|
17
|
+
|
18
|
+
# Negative infinity
|
19
|
+
NINFINITY = -INFINITY
|
20
|
+
|
21
|
+
autoload :Formats, 'unbounded/formats'
|
22
|
+
autoload :InfiniteEnumerator, 'unbounded/infinite_enumerator'
|
23
|
+
autoload :Range, 'unbounded/range'
|
24
|
+
autoload :Utility, 'unbounded/utility'
|
25
|
+
|
26
|
+
require 'unbounded/range_extension'
|
27
|
+
|
28
|
+
# @!parse extend Unbounded::Utility
|
29
|
+
extend Unbounded::Utility
|
30
|
+
|
31
|
+
# @return [Unbounded::Range]
|
32
|
+
def Unbounded.range(*args, &block)
|
33
|
+
Unbounded::Range.new(*args, &block)
|
34
|
+
end
|
35
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'simplecov'
|
4
|
+
SimpleCov.start
|
5
|
+
|
6
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
7
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
8
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
9
|
+
# loaded once.
|
10
|
+
#
|
11
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
12
|
+
require 'bundler/setup'
|
13
|
+
require 'unbounded'
|
14
|
+
require 'pry'
|
15
|
+
|
16
|
+
Dir["./spec/support/**/*.rb"].sort.each {|f| require f }
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
20
|
+
config.run_all_when_everything_filtered = true
|
21
|
+
config.filter_run :focus
|
22
|
+
config.alias_it_should_behave_like_to :it_has_behavior, 'has behavior:'
|
23
|
+
|
24
|
+
# Run specs in random order to surface order dependencies. If you find an
|
25
|
+
# order dependency and want to debug it, you can fix the order by providing
|
26
|
+
# the seed, which is printed after each run.
|
27
|
+
# --seed 1234
|
28
|
+
config.order = 'random'
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
shared_context 'postgres', pg: :true do
|
6
|
+
subject { described_class.new example.metadata[:postgres] }
|
7
|
+
|
8
|
+
let(:equivalent_range) { example.metadata[:equiv] }
|
9
|
+
|
10
|
+
it '== an equivalent range' do
|
11
|
+
should == equivalent_range
|
12
|
+
end
|
13
|
+
|
14
|
+
it_behaves_like 'postgres range exclusivity'
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
shared_examples_for 'a finite maximum' do
|
6
|
+
describe '#last' do
|
7
|
+
specify 'given no parameters, it returns max' do
|
8
|
+
subject.last.should eq subject.max
|
9
|
+
end
|
10
|
+
|
11
|
+
specify 'given an integer, it defers to super' do
|
12
|
+
subject.last(10).should be_a_kind_of Array
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
shared_examples_for 'a finite minimum' do
|
6
|
+
describe '#first' do
|
7
|
+
specify 'given no parameters, it returns #min' do
|
8
|
+
subject.first.should == subject.min
|
9
|
+
end
|
10
|
+
|
11
|
+
specify 'given an integer, it defers to super' do
|
12
|
+
subject.first(10).should be_a_kind_of Array
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
shared_examples_for 'an infinite range' do
|
6
|
+
it { should be_infinite }
|
7
|
+
|
8
|
+
it_has_behavior 'humanized (infinite)'
|
9
|
+
|
10
|
+
it 'it should include any number' do
|
11
|
+
should include infinity, negative_infinity, *(positive_numbers + negative_numbers)
|
12
|
+
end
|
13
|
+
|
14
|
+
it_should_behave_like 'any unbounded range'
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
shared_examples_for 'an unbounded maximum' do
|
6
|
+
it_should_behave_like 'any unbounded range'
|
7
|
+
|
8
|
+
it_has_behavior 'humanized (n+)'
|
9
|
+
|
10
|
+
it 'should include any positive number' do
|
11
|
+
should include *positive_numbers
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should not include negative numbers' do
|
15
|
+
should_not include *negative_numbers
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#last' do
|
19
|
+
specify 'given no parameters, it should be INFINITY' do
|
20
|
+
subject.last.should == infinity
|
21
|
+
end
|
22
|
+
|
23
|
+
specify 'given an integer, it returns Array.new(n, INFINITY)' do
|
24
|
+
subject.last(10).should == Array.new(10, infinity)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
shared_examples_for 'an unbounded minimum' do
|
6
|
+
it_should_behave_like 'any unbounded range'
|
7
|
+
|
8
|
+
it 'should include any negative number' do
|
9
|
+
should include *negative_numbers
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should not include positive numbers' do
|
13
|
+
should_not include *positive_numbers
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#first' do
|
17
|
+
specify 'given no parameters, it should be -INFINITY' do
|
18
|
+
subject.first.should == negative_infinity
|
19
|
+
end
|
20
|
+
|
21
|
+
specify 'given an integer, it returns Array.new(n, -INFINITY)' do
|
22
|
+
subject.first(10).should == Array.new(10, negative_infinity)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
shared_examples_for 'humanized string' do
|
6
|
+
describe '#humanized' do
|
7
|
+
it 'humanizes correctly' do
|
8
|
+
subject.humanized.should match expected_format
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
shared_examples_for 'humanized (n or fewer)' do
|
14
|
+
let(:expected_format) { %r,\d+ or fewer, }
|
15
|
+
include_examples 'humanized string'
|
16
|
+
end
|
17
|
+
|
18
|
+
shared_examples_for "humanized (hyphenated)" do
|
19
|
+
let(:expected_format) { %r,\d+ #{"\u2013"} \d+, }
|
20
|
+
include_examples 'humanized string'
|
21
|
+
end
|
22
|
+
|
23
|
+
shared_examples_for 'humanized (fewer than n)' do
|
24
|
+
let(:expected_format) { %r,fewer than \d+, }
|
25
|
+
include_examples 'humanized string'
|
26
|
+
end
|
27
|
+
|
28
|
+
shared_examples_for 'humanized (infinite)' do
|
29
|
+
let(:expected_format) { %r,infinite, }
|
30
|
+
include_examples 'humanized string'
|
31
|
+
end
|
32
|
+
|
33
|
+
shared_examples_for 'humanized (n+)' do
|
34
|
+
let(:expected_format) { %r,\d+\+, }
|
35
|
+
include_examples 'humanized string'
|
36
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
shared_examples_for 'numerification' do |opts = {}|
|
6
|
+
if opts.key?(:want)
|
7
|
+
let(:wanted) { opts[:want] }
|
8
|
+
|
9
|
+
it 'converts to the expected value' do
|
10
|
+
should == wanted
|
11
|
+
end
|
12
|
+
elsif opts.key?(:default)
|
13
|
+
if opts[:default] == :original
|
14
|
+
it 'should return the original' do
|
15
|
+
should eq provided
|
16
|
+
end
|
17
|
+
else
|
18
|
+
it 'returns the default' do
|
19
|
+
should eq default
|
20
|
+
end
|
21
|
+
end
|
22
|
+
else
|
23
|
+
it 'returns nil' do
|
24
|
+
should be_nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:provided) do
|
29
|
+
opts.fetch(:provide) do
|
30
|
+
opts.fetch(:want).presence.to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
let (:default) { opts[:default] }
|
35
|
+
|
36
|
+
subject { dummy_class.numerify(provided, default) }
|
37
|
+
|
38
|
+
if opts.key? :klass
|
39
|
+
let(:klass) { opts[:klass] }
|
40
|
+
|
41
|
+
it "coerces to a #{opts[:klass]}" do
|
42
|
+
should be_a_kind_of klass
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
shared_examples_for 'postgres range exclusivity' do |*args|
|
6
|
+
exclusivity = args.shift || {}
|
7
|
+
|
8
|
+
exclusivity = OpenStruct.new exclusivity
|
9
|
+
|
10
|
+
exclusivity.max = metadata.fetch(:postgres, '').end_with?(')') if exclusivity.min.nil?
|
11
|
+
|
12
|
+
exclusivity.min = metadata.fetch(:postgres, '').start_with?('(') if exclusivity.min.nil?
|
13
|
+
|
14
|
+
let!(:provided_range_start) { ( example.metadata.fetch(:postgres, '')[/^.(\d)+,/, 1] || 0).to_i }
|
15
|
+
|
16
|
+
let!(:expected_range_start) { provided_range_start + (exclusivity.min ? 1 : 0) }
|
17
|
+
|
18
|
+
if exclusivity.min
|
19
|
+
specify 'it should have a different start than provided' do
|
20
|
+
subject.min.should_not == provided_range_start
|
21
|
+
subject.min.should == expected_range_start
|
22
|
+
end
|
23
|
+
|
24
|
+
specify 'it should not include the provided minimum' do
|
25
|
+
subject.should_not include provided_range_start
|
26
|
+
end
|
27
|
+
else
|
28
|
+
|
29
|
+
specify 'it should have the provided minimum' do
|
30
|
+
subject.min.should == provided_range_start
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
if exclusivity.max
|
36
|
+
specify 'the end is excluded' do
|
37
|
+
subject.should_not include subject.end
|
38
|
+
end
|
39
|
+
else
|
40
|
+
specify 'the end is included' do
|
41
|
+
subject.should include subject.max
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
its(:exclude_end?) { should exclusivity.max ? be_true : be_false }
|
46
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Unbounded::RangeExtension do
|
6
|
+
let(:standard_range) { 1..10 }
|
7
|
+
|
8
|
+
subject { standard_range }
|
9
|
+
|
10
|
+
it_has_behavior 'humanized (hyphenated)'
|
11
|
+
|
12
|
+
describe '#unbounded' do
|
13
|
+
its(:unbounded) { should be_a_kind_of Unbounded::Range }
|
14
|
+
its(:unbounded) { should == standard_range }
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Unbounded::Range do
|
6
|
+
let(:negative_infinity) { Unbounded::NINFINITY }
|
7
|
+
let(:infinity) { Unbounded::INFINITY }
|
8
|
+
let(:positive_numbers) { [42, 2**64] }
|
9
|
+
let(:negative_numbers) { [-2**64, -42] }
|
10
|
+
|
11
|
+
describe '#unbounded' do
|
12
|
+
specify 'returns self' do
|
13
|
+
subject.unbounded.should be subject
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '.humanized' do
|
18
|
+
specify { described_class.humanized(0, 10).should be_a_kind_of String }
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '.new' do
|
22
|
+
context 'given ()' do
|
23
|
+
it_should_behave_like 'an infinite range'
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'given (nil, nil)' do
|
27
|
+
subject { described_class.new nil, nil }
|
28
|
+
|
29
|
+
it_should_behave_like 'an infinite range'
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'given (0, nil)' do
|
33
|
+
subject { described_class.new 0, nil }
|
34
|
+
|
35
|
+
it_should_behave_like 'a finite minimum'
|
36
|
+
|
37
|
+
it_should_behave_like 'an unbounded maximum'
|
38
|
+
|
39
|
+
it_has_behavior 'humanized (n+)'
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'given (0, nil, true)' do
|
43
|
+
subject { described_class.new 0, nil, true }
|
44
|
+
|
45
|
+
it_should_behave_like 'an excluded end'
|
46
|
+
|
47
|
+
it_should_behave_like 'a finite minimum'
|
48
|
+
|
49
|
+
it_should_behave_like 'an unbounded maximum'
|
50
|
+
|
51
|
+
it_has_behavior 'humanized (n+)'
|
52
|
+
|
53
|
+
describe '#minmax' do
|
54
|
+
specify 'the last element is infinity' do
|
55
|
+
subject.minmax.last == infinity
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'given (nil, 0)' do
|
61
|
+
subject { described_class.new nil, 0 }
|
62
|
+
|
63
|
+
it_should_behave_like 'an unbounded minimum'
|
64
|
+
|
65
|
+
it_should_behave_like 'a finite maximum'
|
66
|
+
|
67
|
+
it_has_behavior 'humanized (n or fewer)'
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'given (nil, 0, true)' do
|
71
|
+
subject { described_class.new nil, 0, true }
|
72
|
+
|
73
|
+
it_should_behave_like 'an excluded end'
|
74
|
+
|
75
|
+
it_should_behave_like 'an unbounded minimum'
|
76
|
+
|
77
|
+
it_should_behave_like 'a finite maximum'
|
78
|
+
|
79
|
+
it_has_behavior 'humanized (fewer than n)'
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'with a postgres-style string argument' do
|
83
|
+
context '()', :pg,
|
84
|
+
:start_excluded,
|
85
|
+
:end_excluded,
|
86
|
+
postgres: '(1,10)',
|
87
|
+
equiv: 2...10
|
88
|
+
|
89
|
+
context '[)', :pg,
|
90
|
+
:end_excluded,
|
91
|
+
start_excluded: false,
|
92
|
+
postgres: '[1,10)',
|
93
|
+
equiv: 1...10
|
94
|
+
|
95
|
+
context '(]', :pg,
|
96
|
+
:start_excluded,
|
97
|
+
end_excluded: false,
|
98
|
+
postgres: '(1,10]',
|
99
|
+
equiv: 2..10
|
100
|
+
|
101
|
+
context '[]', :pg,
|
102
|
+
end_excluded: false,
|
103
|
+
start_excluded: false,
|
104
|
+
postgres: '[1,10]',
|
105
|
+
equiv: 1..10
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'with a simple-style string argument' do
|
109
|
+
context 'given 0...Infinity' do
|
110
|
+
subject { described_class.new('0...Infinity') }
|
111
|
+
|
112
|
+
it_should_behave_like 'a finite minimum'
|
113
|
+
it_should_behave_like 'an unbounded maximum'
|
114
|
+
it_should_behave_like 'an excluded end'
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'given 1..10' do
|
118
|
+
subject { described_class.new('1..10') }
|
119
|
+
|
120
|
+
it_should_behave_like 'a finite minimum'
|
121
|
+
it_should_behave_like 'a finite maximum'
|
122
|
+
|
123
|
+
it_has_behavior 'humanized (hyphenated)'
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Unbounded::Utility do
|
6
|
+
let(:dummy_class) { Module.new { extend Unbounded::Utility } }
|
7
|
+
|
8
|
+
describe '#match_to_hash' do
|
9
|
+
let(:regex) { %r:^(?<first_word>\w+)[^\w]*(?<second_word>\w+): }
|
10
|
+
let(:test_string) { 'Hello, world!' }
|
11
|
+
let(:match_data) { test_string.match(regex) }
|
12
|
+
|
13
|
+
subject { dummy_class.match_to_hash(match_data) }
|
14
|
+
|
15
|
+
it 'converts `MatchData` to a hash' do
|
16
|
+
subject.should be_a_kind_of Hash
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'has the expected contents' do
|
20
|
+
should include 'first_word' => 'Hello', 'second_word' => 'world'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#numerify' do
|
25
|
+
context 'given a numeric value' do
|
26
|
+
it 'returns the same' do
|
27
|
+
dummy_class.numerify(1).should eq 1
|
28
|
+
dummy_class.numerify(2.0).should eq 2.0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'given a float-containing string' do
|
33
|
+
it_has_behavior 'numerification', :want => 1.0, :klass => Float
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'given an integer-containing string' do
|
37
|
+
it_has_behavior 'numerification', :want => 1, :klass => Integer
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'on a failed conversion' do
|
41
|
+
context 'given no default' do
|
42
|
+
it_has_behavior 'numerification', :provide => 'anything else'
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'default: :original' do
|
46
|
+
it_has_behavior 'numerification', :provide => 'some string', :default => :original
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'default: some other string' do
|
50
|
+
it_has_behavior 'numerification', :provide => 'some string', :default => 'some other string'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/unbounded.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'unbounded/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "unbounded"
|
8
|
+
spec.version = Unbounded::VERSION
|
9
|
+
spec.authors = ["Alexa Grey"]
|
10
|
+
spec.email = ["devel@mouse.vc"]
|
11
|
+
spec.description = %q{Unbounded (infinite) ranges, ruby-style}
|
12
|
+
spec.summary = %q{Unbounded (infinite) ranges, ruby-style}
|
13
|
+
spec.homepage = "https://github.com/scryptmouse/unbounded"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "active_support", ">= 3.0"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "rspec", "~> 2.14"
|
26
|
+
spec.add_development_dependency "pry"
|
27
|
+
spec.add_development_dependency "simplecov"
|
28
|
+
spec.add_development_dependency 'yard'
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: unbounded
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexa Grey
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-09-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: active_support
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.14'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.14'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: yard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: Unbounded (infinite) ranges, ruby-style
|
112
|
+
email:
|
113
|
+
- devel@mouse.vc
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- .gitignore
|
119
|
+
- .pryrc
|
120
|
+
- .rspec
|
121
|
+
- .yardopts
|
122
|
+
- Gemfile
|
123
|
+
- LICENSE.txt
|
124
|
+
- README.md
|
125
|
+
- Rakefile
|
126
|
+
- lib/unbounded.rb
|
127
|
+
- lib/unbounded/formats.rb
|
128
|
+
- lib/unbounded/infinite_enumerator.rb
|
129
|
+
- lib/unbounded/range.rb
|
130
|
+
- lib/unbounded/range_extension.rb
|
131
|
+
- lib/unbounded/utility.rb
|
132
|
+
- lib/unbounded/version.rb
|
133
|
+
- spec/spec_helper.rb
|
134
|
+
- spec/support/shared_context_for_postgres_style_ranges.rb
|
135
|
+
- spec/support/shared_examples_for_a_finite_maximum.rb
|
136
|
+
- spec/support/shared_examples_for_a_finite_minimum.rb
|
137
|
+
- spec/support/shared_examples_for_an_excluded_end.rb
|
138
|
+
- spec/support/shared_examples_for_an_infinite_range.rb
|
139
|
+
- spec/support/shared_examples_for_an_unbounded_maximum.rb
|
140
|
+
- spec/support/shared_examples_for_an_unbounded_minimum.rb
|
141
|
+
- spec/support/shared_examples_for_any_unbounded_range.rb
|
142
|
+
- spec/support/shared_examples_for_humanized_string.rb
|
143
|
+
- spec/support/shared_examples_for_numerification.rb
|
144
|
+
- spec/support/shared_examples_for_postgres_range_exclusivity.rb
|
145
|
+
- spec/unbounded/range_extension_spec.rb
|
146
|
+
- spec/unbounded/range_spec.rb
|
147
|
+
- spec/unbounded/utility_spec.rb
|
148
|
+
- spec/unbounded_spec.rb
|
149
|
+
- unbounded.gemspec
|
150
|
+
homepage: https://github.com/scryptmouse/unbounded
|
151
|
+
licenses:
|
152
|
+
- MIT
|
153
|
+
metadata: {}
|
154
|
+
post_install_message:
|
155
|
+
rdoc_options: []
|
156
|
+
require_paths:
|
157
|
+
- lib
|
158
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - '>='
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '0'
|
163
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - '>='
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: '0'
|
168
|
+
requirements: []
|
169
|
+
rubyforge_project:
|
170
|
+
rubygems_version: 2.0.3
|
171
|
+
signing_key:
|
172
|
+
specification_version: 4
|
173
|
+
summary: Unbounded (infinite) ranges, ruby-style
|
174
|
+
test_files:
|
175
|
+
- spec/spec_helper.rb
|
176
|
+
- spec/support/shared_context_for_postgres_style_ranges.rb
|
177
|
+
- spec/support/shared_examples_for_a_finite_maximum.rb
|
178
|
+
- spec/support/shared_examples_for_a_finite_minimum.rb
|
179
|
+
- spec/support/shared_examples_for_an_excluded_end.rb
|
180
|
+
- spec/support/shared_examples_for_an_infinite_range.rb
|
181
|
+
- spec/support/shared_examples_for_an_unbounded_maximum.rb
|
182
|
+
- spec/support/shared_examples_for_an_unbounded_minimum.rb
|
183
|
+
- spec/support/shared_examples_for_any_unbounded_range.rb
|
184
|
+
- spec/support/shared_examples_for_humanized_string.rb
|
185
|
+
- spec/support/shared_examples_for_numerification.rb
|
186
|
+
- spec/support/shared_examples_for_postgres_range_exclusivity.rb
|
187
|
+
- spec/unbounded/range_extension_spec.rb
|
188
|
+
- spec/unbounded/range_spec.rb
|
189
|
+
- spec/unbounded/utility_spec.rb
|
190
|
+
- spec/unbounded_spec.rb
|
191
|
+
has_rdoc:
|