composite_range 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.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in composite_range.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rspec'
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Alex Svirin
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.
@@ -0,0 +1,53 @@
1
+ Overview
2
+ ========
3
+
4
+ composite_range is a simple class if you need to deal with more than one range at once.
5
+
6
+ Installation
7
+ ============
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'composite_range'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install composite_range
20
+
21
+ Usage
22
+ =====
23
+
24
+ You can simply create a composite range by passing all ranges that you plan to deal with:
25
+
26
+ range = CompositeRange.new([1..10, 12..45, 37..98])
27
+
28
+ If you have some ranges to exclude from the main one:
29
+
30
+ range = CompositeRange.new(1..20, exclude: [3..5, 9..14])
31
+
32
+ After creating of instance, you can do the range operations: include?, cover?, begin, end, first, last.
33
+
34
+ Adding ranges dynamically:
35
+
36
+ range << (2..12)
37
+
38
+ or
39
+
40
+ range[] = (2..12)
41
+
42
+ Getting range by index:
43
+
44
+ range[index]
45
+
46
+ Contributing
47
+ ============
48
+
49
+ 1. Fork it
50
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
51
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
52
+ 4. Push to the branch (`git push origin my-new-feature`)
53
+ 5. Create new Pull Request
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,15 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.authors = ["Hck"]
5
+ gem.homepage = "http://github.com/hck/composite_range"
6
+ gem.description = %q{CompositeRange class allows you to work with few ranges in one container}
7
+ gem.summary = %q{Simple composite ranges}
8
+
9
+ gem.files = `git ls-files`.split($\)
10
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
11
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
12
+ gem.name = "composite_range"
13
+ gem.require_paths = ["lib"]
14
+ gem.version = '0.1'
15
+ end
@@ -0,0 +1,94 @@
1
+ class CompositeRange
2
+ include Enumerable
3
+
4
+ attr_accessor :ranges
5
+
6
+ def initialize(args = [], options={})
7
+ unless options[:exclude]
8
+ @ranges = args.is_a?(Array) ? args : [args]
9
+ return
10
+ end
11
+
12
+ options[:exclude] = [options[:exclude]] unless options[:exclude].is_a? Array
13
+
14
+ @ranges = options[:exclude].inject([args]) do |res, r|
15
+ res.map do |range|
16
+ if range.include?(r.begin) and range.include?(r.end)
17
+ val = []
18
+ val << (range.begin..r.begin) if range.begin != r.begin
19
+ val << (r.end..range.end) if range.end != r.end
20
+ val
21
+ elsif range.include?(r.begin)
22
+ (range.begin..r.begin) if range.begin != r.begin
23
+ elsif range.include?(r.end)
24
+ (r.end..range.end) if range.end != r.end
25
+ else
26
+ range
27
+ end
28
+ end.flatten.compact
29
+ end
30
+ end
31
+
32
+ def ==(obj)
33
+ obj.respond_to?(:ranges) ? self.ranges == obj.ranges : self.ranges == obj
34
+ end
35
+
36
+ def eql?(obj)
37
+ obj.is_a?(self.class) and self == obj
38
+ end
39
+
40
+ def [](key)
41
+ @ranges[key]
42
+ end
43
+
44
+ def []=(val)
45
+ if val.is_a?(::Range) or val.is_a?(self.class)
46
+ @ranges << val
47
+ else
48
+ raise 'Only arguments with class Range or CompositeRange allowed'
49
+ end
50
+ end
51
+
52
+ def <<(val)
53
+ if val.is_a?(::Range) or val.is_a?(self.class)
54
+ @ranges.push(val)
55
+ else
56
+ raise 'Only arguments with class Range or CompositeRange allowed'
57
+ end
58
+ end
59
+
60
+ def each(&block)
61
+ @ranges.each(&block)
62
+ end
63
+
64
+ def cover?(val)
65
+ select_ranges{|r| r.cover?(val)}.size > 0
66
+ end
67
+
68
+ def include?(val)
69
+ select_ranges{|r| r.include?(val)}.size > 0
70
+ end
71
+ alias :member? :include?
72
+ alias :=== :include?
73
+
74
+ def begin
75
+ ranges.min_by{|r| r.send(__method__)}.send(__method__)
76
+ end
77
+
78
+ def end
79
+ ranges.max_by{|r| r.send(__method__)}.send(__method__)
80
+ end
81
+
82
+ def first(n=1)
83
+ n == 1 ? ranges.first : ranges.first(n)
84
+ end
85
+
86
+ def last(n=1)
87
+ n == 1 ? ranges.last : ranges.last(n)
88
+ end
89
+
90
+ private
91
+ def select_ranges(&block)
92
+ @ranges.select(&block)
93
+ end
94
+ end
@@ -0,0 +1,210 @@
1
+ require 'spec_helper'
2
+
3
+ describe CompositeRange do
4
+ it 'creates new object and fills ranges if passed' do
5
+ subject.class.new.ranges.should eq([])
6
+ subject.class.new((1..5)).ranges.should eq([(1..5)])
7
+ end
8
+
9
+ it 'creates new object and splits passed range to smaller ones by ranges, passed in :exclude option' do
10
+ subject.class.new(1..20, exclude: 3..5).ranges.should eq([1..3, 5..20])
11
+ subject.class.new(1..20, exclude: 3..20).ranges.should eq([1..3])
12
+ subject.class.new(1..20, exclude: [3..5, 12..13, 16..19]).ranges.should eq([1..3, 5..12, 13..16, 19..20])
13
+ subject.class.new(1..20, exclude: [3..5, 12..13, 16..20]).ranges.should eq([1..3, 5..12, 13..16])
14
+ end
15
+
16
+ describe '#[]' do
17
+ before(:each) do
18
+ @range = subject.class.new [(1..10), (13..21)]
19
+ end
20
+
21
+ it 'gets element by index' do
22
+ @range[0].should eq(1..10)
23
+ @range[1].should eq(13..21)
24
+ end
25
+
26
+ it 'gets nil if index not exists' do
27
+ @range[2].should be_nil
28
+ end
29
+
30
+ it 'raises exception if not integer index provided' do
31
+ ->{@range['key']}.should raise_error
32
+ end
33
+ end
34
+
35
+ describe '#[]=' do
36
+ before(:each) do
37
+ @range = subject.class.new
38
+ end
39
+
40
+ it 'assigns element is it is CompositeRange or Range object' do
41
+ @range[] = (1..10)
42
+ @range[] = subject.class.new
43
+
44
+ @range[0].should eq(1..10)
45
+ @range[1].should eq(subject.class.new)
46
+ end
47
+
48
+ it 'raises error if element is not Range or CompositeRange' do
49
+ ->{@range[] = 1}.should raise_error
50
+ end
51
+ end
52
+
53
+ describe '#<<' do
54
+ before(:each) do
55
+ @range = subject.class.new
56
+ end
57
+
58
+ it 'pushes element to composite range if it is CompositeRange or Range object' do
59
+ @range << (1..10)
60
+ @range << subject.class.new
61
+ @range[0].should eq(1..10)
62
+ @range[1].should eq(subject.class.new)
63
+ end
64
+
65
+ it 'raises error if element is not Range or CompositeRange' do
66
+ ->{@range << 1}.should raise_error
67
+ end
68
+ end
69
+
70
+ describe '#each' do
71
+ before(:each) do
72
+ @range = subject.class.new([1..5, 8..10, 23..45])
73
+ res = []
74
+ @range.each{|r| res << r.begin + 5}
75
+ res.should == [6, 13, 28]
76
+ end
77
+
78
+ it 'yields block for each element in @ranges' do
79
+
80
+ end
81
+ end
82
+
83
+ describe '#cover?' do
84
+ before(:each) do
85
+ @range = subject.class.new([1..10, 12..45, -18..-3])
86
+ end
87
+
88
+ it 'returns true if one of @ranges covers passed object' do
89
+ @range.cover?(10).should be_true
90
+ @range.cover?(12).should be_true
91
+ @range.cover?(15).should be_true
92
+ @range.cover?(-17).should be_true
93
+ end
94
+
95
+ it 'returns false if passed object is not covered in any of @ranges' do
96
+ @range.cover?(0).should be_false
97
+ @range.cover?(11).should be_false
98
+ @range.cover?(46).should be_false
99
+ @range.cover?(-2).should be_false
100
+ end
101
+ end
102
+
103
+ describe '#include?' do
104
+ before(:each) do
105
+ @range = subject.class.new([1..10, 12..45, -18..-3])
106
+ end
107
+
108
+ it 'returns true if one of @ranges includes passed object' do
109
+ @range.include?(10).should be_true
110
+ @range.include?(12).should be_true
111
+ @range.include?(15).should be_true
112
+ @range.include?(-17).should be_true
113
+ end
114
+
115
+ it 'returns false if passed object is not included in any of @ranges' do
116
+ @range.include?(0).should be_false
117
+ @range.include?(11).should be_false
118
+ @range.include?(46).should be_false
119
+ @range.include?(-2).should be_false
120
+ end
121
+ end
122
+
123
+ describe '#begin' do
124
+ before(:each) do
125
+ @range = subject.class.new
126
+ end
127
+
128
+ it 'returns min object from all ranges start points' do
129
+ @range << (1..10)
130
+ @range << (3..12)
131
+ @range << (21..85)
132
+ @range.begin.should eq(1)
133
+ end
134
+ end
135
+
136
+ describe '#end' do
137
+ before(:each) do
138
+ @range = subject.class.new
139
+ end
140
+
141
+ it 'returns min object from all ranges end points' do
142
+ @range << (1..10)
143
+ @range << (3..12)
144
+ @range << (21..85)
145
+ @range.end.should eq(85)
146
+ end
147
+ end
148
+
149
+ describe '#first' do
150
+ before(:each) do
151
+ @range = subject.class.new
152
+ @range << (1..10)
153
+ @range << (3..12)
154
+ @range << (21..85)
155
+ end
156
+
157
+ it 'returns first element if n is not specified' do
158
+ @range.first.should eq(1..10)
159
+ end
160
+
161
+ it 'returns first n element if n is specified' do
162
+ @range.first(2).should eq([1..10, 3..12])
163
+ end
164
+ end
165
+
166
+ describe '#last' do
167
+ before(:each) do
168
+ @range = subject.class.new
169
+ @range << (1..10)
170
+ @range << (3..12)
171
+ @range << (21..85)
172
+ end
173
+
174
+ it 'returns last element if n is not specified' do
175
+ @range.last.should eq(21..85)
176
+ end
177
+
178
+ it 'returns last n element if n is specified' do
179
+ @range.last(2).should eq([3..12, 21..85])
180
+ end
181
+ end
182
+
183
+ describe '#==' do
184
+ it 'composite ranges are == if @ranges are empty' do
185
+ @range1 = subject.class.new
186
+ @range2 = subject.class.new
187
+ @range1.should == @range2
188
+ end
189
+
190
+ it 'composite ranges are == if @ranges are equal' do
191
+ @range1 = subject.class.new([1..5, 7..10])
192
+ @range2 = subject.class.new([1..5, 7..10])
193
+ @range1.should == @range2
194
+ end
195
+ end
196
+
197
+ describe '#eql?' do
198
+ it 'composite ranges are eql? if @ranges are equal' do
199
+ @range1 = subject.class.new([1..5, 7..10])
200
+ @range2 = subject.class.new([1..5, 7..10])
201
+ @range1.should == @range2
202
+ end
203
+
204
+ it 'ranges are not eql? if they are not of the same class' do
205
+ @range1 = subject.class.new(1..5)
206
+ @range2 = (1..5)
207
+ @range1.should_not == @range2
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
4
+ require 'rspec'
5
+ require 'composite_range'
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: composite_range
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Hck
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-28 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: CompositeRange class allows you to work with few ranges in one container
15
+ email:
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - .gitignore
21
+ - .rspec
22
+ - Gemfile
23
+ - LICENSE
24
+ - README.md
25
+ - Rakefile
26
+ - composite_range.gemspec
27
+ - lib/composite_range.rb
28
+ - spec/composite_range_spec.rb
29
+ - spec/spec_helper.rb
30
+ homepage: http://github.com/hck/composite_range
31
+ licenses: []
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubyforge_project:
50
+ rubygems_version: 1.8.24
51
+ signing_key:
52
+ specification_version: 3
53
+ summary: Simple composite ranges
54
+ test_files:
55
+ - spec/composite_range_spec.rb
56
+ - spec/spec_helper.rb