RubyRanges 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.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in RubyRanges.gemspec
4
+ gemspec
data/README.textile ADDED
@@ -0,0 +1,98 @@
1
+ h1. RubyRanges
2
+
3
+ RubyRanges is my attempt to move names of Rails gems back to sane naming
4
+ conventions. Just because you can name a gem something fun, doesn't mean I can
5
+ find it. I could have named it LoneRanges, HomeOnTheRanges, or RangingOut, but I
6
+ didn't. I named it what it is: RubyRanges.
7
+
8
+ :)
9
+
10
+ h2. For the serious documentation:
11
+
12
+ The real reason I created the ranges is to be able to treat ranges similar to
13
+ arrays instead of using "to_a" -- which causes inefficiencies. I think of
14
+ adding Ranges as a consolidating overlay. I think of subtracting arrays as
15
+ a shark bite -- whatever that means to you.
16
+
17
+ h3. Quick Examples
18
+
19
+ Below are some examples of work, take a look at the tests for more:
20
+
21
+ h4. Ultimately, I wanted to do something like this:
22
+
23
+ <pre>
24
+ (4..88) - (33..45) + (115..122) + (85..110) => [4..33, 45..110, 115..122]
25
+ </pre>
26
+
27
+ h4. But wait, it works with dates too:
28
+
29
+ <pre>
30
+ (Time.now..Time.now + 180) + (Time.now + 360..Time.now + 400) - (Time.now + 380..Time.now + 395) =>
31
+ [Sat Aug 07 00:45:09 -0500 2010..Sat Aug 07 00:48:09 -0500 2010, Sat Aug 07 00:51:09 -0500 2010..Sat Aug 07 00:51:49 -0500 2010]
32
+ </pre>
33
+
34
+ h4. And it works with letters: (which I really don't have a usage for)
35
+
36
+ <pre>
37
+ ("A".."FB") + ("P".."DE") - ("BC".."MA") => "A".."BC"
38
+ </pre>
39
+
40
+ (Psst. . . it works with anything Ruby creates a range with)
41
+
42
+ h3. Long Examples
43
+
44
+ h4. Addition
45
+
46
+ <pre>
47
+ (1..9) + (4..7) => (1..9)
48
+ (4..7) + (1..9) => (1..9)
49
+ (1..9) + (5..12) => (1..12)
50
+ (1..9) + (-5..5) => (-5..9)
51
+ </pre>
52
+
53
+ h4. Addition with non-continuous ranges
54
+
55
+ <pre>
56
+ (1..4) + (8..9) => RubyRanges::Array.new(1..4, 8..9)
57
+ </pre>
58
+
59
+ h4. Subtraction
60
+
61
+ <pre>
62
+ (1..9) - (5..7) => RubyRanges::Ranges.new(1..5, 7..9)
63
+
64
+ (1..9) - (6..12) => 1..6
65
+ (1..9) - (-1..4) => 4..9
66
+ (1..9) - (-1..10) => nil
67
+
68
+ (1..9) - (11..12) => 1..9
69
+ </pre>
70
+
71
+ h4. Now for RubyRanges::Array
72
+
73
+ I created the RubyRanges::Array to give some special actions to +, -, and flatten_ranges
74
+ for sets of Ranges. Below are some usage examples.
75
+
76
+ <pre>
77
+ RubyRanges::Array.new((1..4), (18..20)) + RubyRanges::Array.new((12..15), (6..9)) =>
78
+ RubyRanges::Array.new((1..4), (6..9), (12..15), (18..20))
79
+
80
+ RubyRanges::Array.new((1..10), (15..25)) + RubyRanges::Array.new((8..23), (25..28)) => 1..28
81
+
82
+ RubyRanges::Array.new(10..15, 20..25) + RubyRanges::Array.new(5..13, 18..22) =>
83
+ RubyRanges::Array.new(5..15, 18..25)
84
+
85
+ RubyRanges::Array.new(10..15, 20..25) + RubyRanges::Array.new(13..18, 23..28) =>
86
+ RubyRanges::Array.new(10..18, 20..28)
87
+ end
88
+
89
+ RubyRanges::Array.new(10..15, 20..25) + (28..30) =>
90
+ RubyRanges::Array.new(10..15, 20..25, 28..30)
91
+
92
+ RubyRanges::Array.new(10..15, 20..25) + (13..22) => 10..25
93
+
94
+ RubyRanges::Array.new(10..15, 20..25) + (18..22) => RubyRanges::Array.new(10..15, 18..25)
95
+ </pre>
96
+
97
+ Have fun, please donate if you fix / change. Licenses are for the lawyers, if you're
98
+ a lawyer, this is GPLv3. If you're a coder, this is code.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ruby_ranges/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "RubyRanges"
7
+ s.version = RubyRanges::VERSION
8
+ s.authors = ["Chris Winslett"]
9
+ s.email = ["christopherwinslett@gmail.com"]
10
+ s.homepage = "https://github.com/Winslett/RubyRanges"
11
+ s.summary = %q{Allows array type actions on ranges and arrays of ranges}
12
+ s.description = %q{See https://github.com/Winslett/RubyRanges}
13
+
14
+ s.rubyforge_project = "RubyRanges"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ end
@@ -0,0 +1,81 @@
1
+ class Array
2
+ alias :old_plus :+
3
+ end
4
+
5
+ module RubyRanges
6
+ class Array < Array
7
+
8
+ alias :old_plus :+
9
+
10
+ #
11
+ # Takes the same arguments as Array.new:
12
+ #
13
+ # RubyRanges::Array.new(35..38, 55..72)
14
+ # RubyRanges::Array.new(Time.now..3.minutes.from_now, Time.now..3.minutes.ago)
15
+ #
16
+ # If you have a question whether it works. . . try it. It might.
17
+ #
18
+ def initialize(*args)
19
+ args.each { |arg| self << arg }
20
+ end
21
+
22
+ #
23
+ # Takes either a Range or Array of Ranges
24
+ #
25
+ # RubyRanges::Array.new(1..4, 8..11) + RubyRanges::Array.new(3..6, 7..9) =>
26
+ # RubyRanges::Array.new(1..6, 7..9)
27
+ #
28
+ # RubyRanges::Array.new(1..4, 8..11) + (3..9) => 1..9
29
+ #
30
+ # This method will "flatten" any RubyRanges::Array which compasses the smallest to the largest. It
31
+ # will also compress an ranges which overlap.
32
+ #
33
+ def +(object)
34
+ raise "Expecting RubyRanges::Array or Range, but was #{object.class} : #{object.inspect}" unless object.is_a?(Range) || object.is_a?(Array)
35
+ array_of_ranges = Array.new((object.is_a?(Range) ? (self.old_plus([object])) : super(object)))
36
+ array_of_ranges.flatten_ranges
37
+ end
38
+
39
+ #
40
+ # Similar to +, takes either Range or Array of Ranges.
41
+ #
42
+ # This method splits apart ranges with an all encompassed subtrahend
43
+ #
44
+ def -(object)
45
+ raise "Expecting RubyRanges::Array or Range, but was #{object.class} : #{object.inspect}" unless object.is_a?(Range) || object.is_a?(Array)
46
+ if object.is_a?(Range)
47
+ array_of_ranges = inject([]) do |new, range|
48
+ new.old_plus([range - object])
49
+ end
50
+ else
51
+ other = self
52
+ object.each do |other_range|
53
+ other = Array.new(other - other_range).flatten
54
+ end
55
+ other
56
+ end
57
+ end
58
+
59
+ #
60
+ # Flattens a RubyRanges::Array with overlapping Ranges.
61
+ #
62
+ def flatten_ranges
63
+ new = []
64
+
65
+ flatten.sort_by(&:begin).each do |range|
66
+ if new.last.nil?
67
+ new << range
68
+ else
69
+ last = new.pop + range
70
+
71
+ new = last.is_a?(Array) ?
72
+ new.old_plus(last) : (new << last)
73
+ end
74
+ end
75
+
76
+ array = Array.new(new).flatten
77
+ array.length == 1 ? array[0] : array
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,9 @@
1
+ module RubyRanges
2
+
3
+ class WhollyIncluded; def self.to_value; true; end; end
4
+ class InverseWhollyIncluded; def self.to_value; nil; end; end
5
+ class UpwardIncluded; def self.to_value; 1; end; end
6
+ class DownwardIncluded; def self.to_value; -1; end; end
7
+ class MutuallyExcluded; def self.to_value; false; end; end
8
+
9
+ end
@@ -0,0 +1,64 @@
1
+ module RubyRanges
2
+ module Range
3
+
4
+ def self.included(base)
5
+ base.send(:define_method, :include?) do |object|
6
+ object.is_a?(Range) ? include_range?(object) : super(object)
7
+ end
8
+ end
9
+
10
+ def +(range)
11
+ add_range(range)
12
+ end
13
+
14
+ def -(range)
15
+ subtract_range(range)
16
+ end
17
+
18
+ def include_range?(range)
19
+ case
20
+ when self.include?(range.begin) && self.include?(range.end) # wholly inclusive
21
+ RubyRanges::WhollyIncluded
22
+ when range.include?(self.begin) && range.include?(self.end) # inverse wholly inclusive
23
+ RubyRanges::InverseWhollyIncluded
24
+ when self.include?(range.begin) && self.end < range.end # upload inclusive
25
+ RubyRanges::UpwardIncluded
26
+ when self.include?(range.end) && self.begin > range.begin # downward inclusive
27
+ RubyRanges::DownwardIncluded
28
+ else # exclusive range
29
+ RubyRanges::MutuallyExcluded
30
+ end
31
+ end
32
+
33
+ private
34
+ def add_range(range)
35
+ case self.include?(range).to_value
36
+ when RubyRanges::WhollyIncluded.to_value # self swallows smaller range
37
+ self
38
+ when RubyRanges::InverseWhollyIncluded.to_value # self swallowed by larger range
39
+ range
40
+ when RubyRanges::UpwardIncluded.to_value # add upward inclusive range
41
+ self.begin..range.end
42
+ when RubyRanges::DownwardIncluded.to_value # add downward inclusive range
43
+ range.begin..self.end
44
+ else # mutually exclusive
45
+ Array.new(self, range)
46
+ end
47
+ end
48
+
49
+ def subtract_range(range)
50
+ case self.include?(range).to_value
51
+ when NilClass # self removed by larger range
52
+ nil
53
+ when TrueClass # self split by wholly inclusive range
54
+ Array.new(self.begin..range.begin, range.end..self.end)
55
+ when 1 # self shortened by upload inclusive range
56
+ self.begin..range.begin
57
+ when -1 # self shortened by downward inclusive range
58
+ range.end..self.end
59
+ else # mutually exclusive
60
+ self
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,3 @@
1
+ module RubyRanges
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,8 @@
1
+ require File.dirname(__FILE__) + '/ruby_ranges/version.rb'
2
+ require File.dirname(__FILE__) + '/ruby_ranges/inclusive.rb'
3
+ require File.dirname(__FILE__) + '/ruby_ranges/range.rb'
4
+ require File.dirname(__FILE__) + '/ruby_ranges/array.rb'
5
+
6
+ class Range
7
+ include RubyRanges::Range
8
+ end
@@ -0,0 +1,86 @@
1
+ require File.dirname(__FILE__) + '/../lib/ruby_ranges'
2
+ require 'test/unit'
3
+
4
+ class RubyRanges::ArrayTest < Test::Unit::TestCase
5
+
6
+ def test_adding_mutually_exclusive_ruby_ranges_arrays
7
+ output = RubyRanges::Array.new((1..4), (18..20)) + RubyRanges::Array.new((12..15), (6..9))
8
+ assert_equal RubyRanges::Array.new((1..4), (6..9), (12..15), (18..20)), output
9
+ end
10
+
11
+ def test_adding_spanning_ruby_ranges_arrays
12
+ output = RubyRanges::Array.new((1..10), (15..25)) + RubyRanges::Array.new((8..23), (25..28))
13
+ assert_equal 1..28, output
14
+ end
15
+
16
+ def test_adding_downward_inclusive_ruby_ranges_arrays
17
+ output = RubyRanges::Array.new(10..15, 20..25) + RubyRanges::Array.new(5..13, 18..22)
18
+ assert_equal RubyRanges::Array.new(5..15, 18..25), output
19
+ end
20
+
21
+ def test_adding_upward_inclusive_exclusive_ruby_ranges_arrays
22
+ output = RubyRanges::Array.new(10..15, 20..25) + RubyRanges::Array.new(13..18, 23..28)
23
+ assert_equal RubyRanges::Array.new(10..18, 20..28), output
24
+ end
25
+
26
+ def test_adding_mutually_exclusive_range_to_ruby_ranges_arrays
27
+ output = RubyRanges::Array.new(10..15, 20..25) + (28..30)
28
+ assert_equal RubyRanges::Array.new(10..15, 20..25, 28..30), output
29
+ end
30
+
31
+ def test_adding_spanning_range_to_ruby_ranges_arrays
32
+ output = RubyRanges::Array.new(10..15, 20..25) + (13..22)
33
+ assert_equal 10..25, output
34
+ end
35
+
36
+ def test_adding_downward_inclusive_range_to_ruby_ranges_arrays
37
+ output = RubyRanges::Array.new(10..15, 20..25) + (18..22)
38
+ assert_equal RubyRanges::Array.new(10..15, 18..25), output
39
+ end
40
+
41
+ def test_adding_upward_inclusive_range_to_ruby_ranges_arrays
42
+ output = RubyRanges::Array.new(10..15, 20..25) + (13..18)
43
+ assert_equal RubyRanges::Array.new(10..18, 20..25), output
44
+ end
45
+
46
+ def test_subtracting_mutually_exclusive_ruby_ranges_array
47
+ output = RubyRanges::Array.new(10..15, 20..25) - RubyRanges::Array.new(17..19, 27..30)
48
+ assert_equal RubyRanges::Array.new(10..15, 20..25), output
49
+ end
50
+
51
+ def test_subtracting_spanning_array_of_ranges
52
+ output = RubyRanges::Array.new(10..15, 20..25) - RubyRanges::Array.new(13..22, 24..30)
53
+ assert_equal RubyRanges::Array.new(10..13, 22..24), output
54
+ end
55
+
56
+ def test_subtracting_downward_inclusive_array_of_ranges
57
+ output = RubyRanges::Array.new(10..15, 20..25) - RubyRanges::Array.new(8..12, 18..22)
58
+ assert_equal RubyRanges::Array.new(12..15, 22..25), output
59
+ end
60
+
61
+ def test_subtracting_upward_inclusive_array_of_ranges
62
+ output = RubyRanges::Array.new(10..15, 20..25) - RubyRanges::Array.new(13..18, 24..30)
63
+ assert_equal RubyRanges::Array.new(10..13, 20..24), output
64
+ end
65
+
66
+ def test_subtracting_mutually_inclusive_range_from_array_of_ranges
67
+ output = RubyRanges::Array.new(10..15, 20..25) - (17..19)
68
+ assert_equal RubyRanges::Array.new(10..15, 20..25), output
69
+ end
70
+
71
+ def test_subtracting_spanning_range_from_array_of_ranges
72
+ output = RubyRanges::Array.new(10..15, 20..25) - (13..22)
73
+ assert_equal RubyRanges::Array.new(10..13, 22..25), output
74
+ end
75
+
76
+ def test_subtracting_downward_inclusive_range_from_array_of_ranges
77
+ output = RubyRanges::Array.new(10..15, 20..25) - (18..22)
78
+ assert_equal RubyRanges::Array.new(10..15, 22..25), output
79
+ end
80
+
81
+ def test_subtracting_upward_inclusive_range_from_array_of_ranges
82
+ output = RubyRanges::Array.new(10..15, 20..25) - (13..18)
83
+ assert_equal RubyRanges::Array.new(10..13, 20..25), output
84
+ end
85
+
86
+ end
@@ -0,0 +1,99 @@
1
+ require File.dirname(__FILE__) + '/../lib/ruby_ranges'
2
+ require 'test/unit'
3
+
4
+ class RubyRanges::RangeTest < Test::Unit::TestCase
5
+
6
+ include RubyRanges
7
+
8
+ def test_include_partial_downward_range
9
+ assert_equal -1, (1..9).include?(-1..3).to_value
10
+ assert_equal RubyRanges::DownwardIncluded, (1..9).include?(-1..3)
11
+ end
12
+
13
+ def test_include_partial_upward_range
14
+ assert_equal 1, (1..9).include?(7..11).to_value
15
+ assert_equal RubyRanges::UpwardIncluded, (1..9).include?(7..11)
16
+ end
17
+
18
+ def test_include_wholly_inclusive_range
19
+ assert_equal true, (1..9).include?(4..7).to_value
20
+ assert_equal RubyRanges::WhollyIncluded, (1..9).include?(4..7)
21
+ end
22
+
23
+ def test_include_inverse_wholly_inclusive_range
24
+ assert_equal nil, (4..7).include?(1..9).to_value
25
+ assert_equal RubyRanges::InverseWhollyIncluded, (4..7).include?(1..9)
26
+ end
27
+
28
+ def test_include_exclusive_range
29
+ assert_equal false, (1..9).include?(10..12).to_value
30
+ end
31
+
32
+ def test_adding_smaller_range
33
+ output = (1..9) + (4..7)
34
+ assert_equal (1..9), output
35
+ end
36
+
37
+ def test_adding_larger_range
38
+ output = (4..7) + (1..9)
39
+ assert_equal (1..9), output
40
+ end
41
+
42
+ def test_adding_an_upward_inclusive_range
43
+ assert_equal (1..12), (1..9) + (5..12)
44
+ end
45
+
46
+ def test_adding_a_downward_inclusive_range
47
+ assert_equal (-5..9), (1..9) + (-5..5)
48
+ end
49
+
50
+ def test_adding_exclusive_ranges
51
+ output = (1..4) + (8..9)
52
+ assert_equal RubyRanges::Array.new((1..4), (8..9)), output
53
+ end
54
+
55
+ def test_adding_ruby_ranges_array
56
+ pending
57
+ end
58
+
59
+ def test_subtracting_wholly_included_range
60
+ output = (1..9) - (5..7)
61
+ assert_equal RubyRanges::Array.new(1..5, 7..9), output
62
+ end
63
+
64
+ def test_subtracting_upward_inclusive_range
65
+ output = (1..9) - (6..12)
66
+ assert_equal 1..6, output
67
+ end
68
+
69
+ def test_subtracting_downward_inclusive_range
70
+ output = (1..9) - (-1..4)
71
+ assert_equal 4..9, output
72
+ end
73
+
74
+ def test_subtracting_surrounding_range
75
+ assert_equal nil, (1..9) - (-1..10)
76
+ end
77
+
78
+ def test_subtracting_exclusive_range
79
+ output = (1..9) - (11..12)
80
+ assert_equal 1..9, output
81
+ end
82
+
83
+ def test_subtracting_wholly_inclusive_ruby_ranges_array
84
+ pending
85
+ end
86
+
87
+ def test_subtracting_mutually_exclusive_ruby_ranges_array
88
+ pending
89
+ end
90
+
91
+ def test_subtracting_downward_exclusive_ruby_ranges_array
92
+ pending
93
+ end
94
+
95
+ def test_subtracting_upward_exclusive_ruby_ranges_array
96
+ pending
97
+ end
98
+
99
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: RubyRanges
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Chris Winslett
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-06-25 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: See https://github.com/Winslett/RubyRanges
23
+ email:
24
+ - christopherwinslett@gmail.com
25
+ executables: []
26
+
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - README.textile
35
+ - Rakefile
36
+ - RubyRanges.gemspec
37
+ - lib/ruby_ranges.rb
38
+ - lib/ruby_ranges/array.rb
39
+ - lib/ruby_ranges/inclusive.rb
40
+ - lib/ruby_ranges/range.rb
41
+ - lib/ruby_ranges/version.rb
42
+ - test/ruby_ranges_array_test.rb
43
+ - test/ruby_ranges_test.rb
44
+ has_rdoc: true
45
+ homepage: https://github.com/Winslett/RubyRanges
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options: []
50
+
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ hash: 3
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ requirements: []
72
+
73
+ rubyforge_project: RubyRanges
74
+ rubygems_version: 1.4.2
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: Allows array type actions on ranges and arrays of ranges
78
+ test_files:
79
+ - test/ruby_ranges_array_test.rb
80
+ - test/ruby_ranges_test.rb