RubyRanges 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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