range_utils 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e1af9e6d4733a5b64c0f6f44036636c215b1708f
4
+ data.tar.gz: 7c74535167ad2a1ab35b63dff3c27dc4c9112a31
5
+ SHA512:
6
+ metadata.gz: 84ee08a6267adbf7e1922e172ed0e12648f3a816255f410162fae6fa31dc9b4eba82568618bb73b7c76b444a3604cca4014e360c5791c41a6e50f70039e2c6bb
7
+ data.tar.gz: f2af9fa0b3678920c1b1fa0315bc3e57871dc4c8b14ad6f0edf61901e6a16e2ec78b8bcf9ee6cf672397fa36a8e6374b58aef819a10a270aa532a60e5c0ac387
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,10 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1.1
7
+ - 2.1.2
8
+ - jruby
9
+ cache: bundler
10
+ sudo: false
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Add dependencies to develop your gem here.
4
+ # Include everything needed to run rake, tests, features, etc.
5
+ group :development do
6
+ gem "rspec", "~> 3.0"
7
+ gem "rdoc", "~> 3.12"
8
+ gem "bundler", "~> 1.0"
9
+ gem "jeweler", "~> 2.0.1"
10
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2015 Julik Tarkhanov
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,44 @@
1
+ # range_utils
2
+
3
+ There is more to a Range than meets the eye. Or more to the eye than meets the Range. Or whatever.
4
+ `RangeUtils` is a non-intrusive module for doing useful things to Range objects.
5
+
6
+ It is mainly useful for working with batches of IDs and HTTP content ranges.
7
+
8
+ ## Usage
9
+
10
+ Combine consecutive Range objects:
11
+
12
+ RangeUtils.splice([0..0, 0..4, 5..14, 16..20]) #=> [0..14, 16..20]
13
+
14
+ Get the range for a given size of the collection:
15
+
16
+ RangeUtils.range_for_size(14) #=> 0..13
17
+
18
+ Get the Ranges of maximum size for a given number of elements:
19
+
20
+ RangeUtils.ranges_of_offfsets_for_size(3, 1) #=> [0..0, 1..1, 2..2]
21
+
22
+ Split a large Range into smaller ranges of given maximum size:
23
+
24
+ RangeUtils.split_range_into_subranges_of(0..7, 3) #=> [0..2, 3..5, 5..7]
25
+
26
+ Prepare a number of HTTP Range headers (each request will be 1 byte):
27
+
28
+ RangeUtils.http_ranges_for_size(3, 1) #=> [0..0, 1..1, 2..2]
29
+
30
+ ## Contributing to range_utils
31
+
32
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
33
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
34
+ * Fork the project.
35
+ * Start a feature/bugfix branch.
36
+ * Commit and push until you are happy with your contribution.
37
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
38
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
39
+
40
+ ## Copyright
41
+
42
+ Copyright (c) 2015 Julik Tarkhanov. See LICENSE.txt for
43
+ further details.
44
+
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require File.dirname(__FILE__) + '/lib/range_utils'
15
+
16
+ require 'jeweler'
17
+ Jeweler::Tasks.new do |gem|
18
+ # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
19
+ gem.version = RangeUtils::VERSION
20
+ gem.name = "range_utils"
21
+ gem.homepage = "http://github.com/julik/range_utils"
22
+ gem.license = "MIT"
23
+ gem.summary = %Q{There is a whole range of things you can do with a Range}
24
+ gem.description = %Q{Range splice, split and other niceties}
25
+ gem.email = "me@julik.nl"
26
+ gem.authors = ["Julik Tarkhanov"]
27
+ # dependencies defined in Gemfile
28
+ end
29
+ Jeweler::RubygemsDotOrgTasks.new
30
+
31
+ require 'rspec/core'
32
+ require 'rspec/core/rake_task'
33
+ RSpec::Core::RakeTask.new(:spec) do |spec|
34
+ spec.pattern = FileList['spec/**/*_spec.rb']
35
+ end
36
+
37
+ task :default => :spec
38
+
39
+ require 'rdoc/task'
40
+ Rake::RDocTask.new do |rdoc|
41
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
42
+
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = "range_utils #{version}"
45
+ rdoc.rdoc_files.include('README*')
46
+ rdoc.rdoc_files.include('lib/**/*.rb')
47
+ end
@@ -0,0 +1,87 @@
1
+ module RangeUtils
2
+ VERSION = '1.0.0'
3
+
4
+ # Tells whether the +item+ is included in the +range+, without enumerating
5
+ # through the +range+ (performing a quick bounds check).
6
+ # The first value of the range and the +item+ have to support +<=>+
7
+ def range_includes_item?(range, item)
8
+ range.begin <= item && item <= range.end
9
+ end
10
+
11
+ # Splits the given Range into subranges of +size+.
12
+ #
13
+ # split_range_into_subranges_of(0..7, 3) #=> [0..2, 3..5, 5..7]
14
+ #
15
+ # Range members must support +<=>+ and aruthmetic with integers.
16
+ # +size+ has to be > 0.
17
+ def split_range_into_subranges_of(range, size)
18
+ raise ArgumentError, "Chunk size should be > 0, was #{size}" unless size > 0
19
+
20
+ num_values = range.end - range.begin + 1
21
+ ranges = []
22
+ at = range.begin
23
+ loop do
24
+ if at > range.end
25
+ return ranges
26
+ else
27
+ end_of_chunk = (at + size - 1)
28
+ current = at..(end_of_chunk > range.end ? range.end : end_of_chunk)
29
+ at = (at + size)
30
+ ranges << current
31
+ yield(current) if block_given?
32
+ end
33
+ end
34
+ end
35
+
36
+ # Returns ranges for the given size. The returned ranges start at zero.
37
+ # Can be used to split a Content-Length of an HTTP resource into
38
+ # ranges usable in Range: header for instance.
39
+ #
40
+ # Since the initial offset is 0 the resulting ranges will always end at
41
+ # +size - 1+
42
+ #
43
+ # ranges_of_offfsets_for_size(3, 1) #=> [0..0, 1..1, 2..2]
44
+ # ranges_of_offfsets_for_size(3, 2) #=> [0..1, 2..2]
45
+ #
46
+ # Range members must support +<=>+ and aruthmetic with integers.
47
+ # +size+ has to be > 0.
48
+ def ranges_of_offfsets_for_size(number_of_items, chunk_size)
49
+ raise ArgumentError, "Chunk size should be > 0, was #{chunk_size}" unless chunk_size > 0
50
+ split_range_into_subranges_of(range_for_size_of(number_of_items), chunk_size)
51
+ end
52
+
53
+ # Creates a Range that can be used to grab N first elements from, say,
54
+ # an Array or a String.
55
+ # range_for_size(14) #=> 0..13
56
+ # "abcd"[range_for_size(2)] #=> "ab"
57
+ #
58
+ # +number_of_items+ should be >= 0. For the +number_of_items+
59
+ # a special Range of 0..-1 will be returned (note that this Range cannot fetch anything).
60
+ def range_for_size_of(number_of_items)
61
+ raise ArgumentError, "Number of items should be at least 0, was #{number_of_items}" if number_of_items < 0
62
+ (0..(number_of_items - 1))
63
+ end
64
+
65
+ # Combine ranges with adjacent or overlapping values (create a union range).
66
+ # splice([0..0, 0..4, 5..14, 16..20]) #=> [0..14, 16..20]
67
+ # Range members must support +<=>+ and aruthmetic with integers.
68
+ def splice(ranges)
69
+ ranges.sort_by(&:begin).inject([]) do | spliced, r |
70
+ if spliced.empty?
71
+ spliced + [r]
72
+ else
73
+ last = spliced.pop
74
+ if last.end >= (r.begin - 1)
75
+ ends = [last.end, last.begin, r.begin, r.end].sort
76
+ new_end, new_begin = ends.shift, ends.pop
77
+ spliced + [(new_end..new_begin)]
78
+ else
79
+ spliced + [last, r]
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ alias_method :http_ranges_for_size, :ranges_of_offfsets_for_size
86
+ extend self
87
+ end
@@ -0,0 +1,125 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+
3
+ require 'rspec'
4
+ require 'range_utils'
5
+
6
+ RSpec.configure {|c| c.order = 'random' }
7
+
8
+ describe "RangeUtils" do
9
+ let(:subject) { RangeUtils }
10
+
11
+ context '.range_includes_item?' do
12
+ it 'properly detects inclusion' do
13
+ range = 5..98
14
+ expect(subject.range_includes_item?(range, 5)).to eq(true)
15
+ expect(subject.range_includes_item?(range, 98)).to eq(true)
16
+ expect(subject.range_includes_item?(range, 97)).to eq(true)
17
+
18
+ expect(subject.range_includes_item?(range, 1)).to eq(false)
19
+ expect(subject.range_includes_item?(range, 4)).to eq(false)
20
+ end
21
+ end
22
+
23
+ context '.split_range_into_subranges_of' do
24
+ it 'raises an ArgumentError when the size is < 1' do
25
+ expect {
26
+ subject.split_range_into_subranges_of(15..456, -2)
27
+ }.to raise_error(ArgumentError, 'Chunk size should be > 0, was -2')
28
+
29
+ expect {
30
+ subject.split_range_into_subranges_of(15..456, 0)
31
+ }.to raise_error(ArgumentError, 'Chunk size should be > 0, was 0')
32
+ end
33
+
34
+ it 'splits a range into ranges' do
35
+ expect(subject.split_range_into_subranges_of(0..7, 3)).to eq([0..2, 3..5, 6..7])
36
+ expect(subject.split_range_into_subranges_of(0..786, 324)).to eq([0..323, 324..647, 648..786])
37
+ expect(subject.split_range_into_subranges_of(245..786, 324)).to eq([245..568, 569..786])
38
+ end
39
+
40
+ it 'yields each subrange in succession' do
41
+ expect { |b|
42
+ subject.split_range_into_subranges_of(0..786, 324, &b)
43
+ }.to yield_successive_args(0..323, 324..647, 648..786)
44
+ end
45
+
46
+ it 'splits a range that spans 1 item' do
47
+ expect(subject.split_range_into_subranges_of(0..0, 1)).to eq([0..0])
48
+ expect(subject.split_range_into_subranges_of(0..0, 15)).to eq([0..0])
49
+ end
50
+
51
+ it 'handles all division cases without raising exceptions' do
52
+ (1..300).each do | chunk_size |
53
+ subject.split_range_into_subranges_of(0..786, chunk_size)
54
+ end
55
+ end
56
+
57
+ it 'is usable for HTTP ranges' do
58
+ bytes_total = 3087
59
+ chunk_size = 991
60
+ range = 128..(bytes_total + 128)
61
+
62
+ ranges = subject.split_range_into_subranges_of(range, chunk_size)
63
+ expect(ranges).to eq([128..1118, 1119..2109, 2110..3100, 3101..3215])
64
+ end
65
+ end
66
+
67
+ context '.splice' do
68
+ it 'splices adjacent and overlapping ranges, regardless of their ordering in the argument array' do
69
+ expect( subject.splice([500..600, 601..999]) ).to eq([500..999])
70
+ expect( subject.splice([601..999, 500..600]) ).to eq([500..999])
71
+ expect( subject.splice([500..700, 601..999]) ).to eq([500..999])
72
+ end
73
+
74
+ it 'creates gaps when the ranges cannot be spliced' do
75
+ expect( subject.splice([500..600, 602..999]) ).to eq([500..600, 602..999])
76
+ expect( subject.splice([601..999, 500..600, 10..44]) ).to eq([10..44, 500..999])
77
+ end
78
+ end
79
+
80
+ context '.ranges_of_offfsets_for_size' do
81
+ it 'raises with a size < 1' do
82
+ expect {
83
+ subject.ranges_of_offfsets_for_size(3, 0)
84
+ }.to raise_error(ArgumentError, 'Chunk size should be > 0, was 0')
85
+
86
+ expect {
87
+ subject.ranges_of_offfsets_for_size(3, -1)
88
+ }.to raise_error(ArgumentError, 'Chunk size should be > 0, was -1')
89
+ end
90
+
91
+ it 'creates the ranges that cover the given number of elements' do
92
+ expect(subject.ranges_of_offfsets_for_size(3, 1)).to eq([0..0, 1..1, 2..2])
93
+ expect(subject.ranges_of_offfsets_for_size(3, 2)).to eq([0..1, 2..2])
94
+ end
95
+
96
+ it 'splits a bigger size' do
97
+ bytes_total = 3087
98
+ chunk_size = 991
99
+ ranges = subject.ranges_of_offfsets_for_size(bytes_total, chunk_size)
100
+ expect(ranges).to eq([0..990, 991..1981, 1982..2972, 2973..3086])
101
+ end
102
+ end
103
+
104
+ context '.range_for_size_of' do
105
+ it 'raises on size being < 0' do
106
+ expect {
107
+ subject.range_for_size_of(-4)
108
+ }.to raise_error(ArgumentError)
109
+ end
110
+
111
+ it 'returns a special range for size 0 case' do
112
+ expect(subject.range_for_size_of(0)).to eq(0..-1)
113
+ end
114
+
115
+ it 'returns the right range for the size' do
116
+ expect(subject.range_for_size_of(456)).to eq(0..455)
117
+ end
118
+ end
119
+
120
+ context '.http_ranges_for_resource_size' do
121
+ it 'get returned without including the last value' do
122
+ expect(subject.http_ranges_for_size(785, 324)).to eq([0..323, 324..647, 648..784])
123
+ end
124
+ end
125
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: range_utils
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Julik Tarkhanov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :development
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: rdoc
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.12'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.12'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: jeweler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 2.0.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 2.0.1
69
+ description: Range splice, split and other niceties
70
+ email: me@julik.nl
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files:
74
+ - LICENSE.txt
75
+ - README.md
76
+ files:
77
+ - ".document"
78
+ - ".rspec"
79
+ - ".travis.yml"
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - lib/range_utils.rb
85
+ - spec/range_utils_spec.rb
86
+ homepage: http://github.com/julik/range_utils
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 2.2.2
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: There is a whole range of things you can do with a Range
110
+ test_files: []