month_range 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f0794936fca957eccd5a5e26c3379a804f0d8366d8cc894b7b130c707d94029
4
- data.tar.gz: c9ea79c7184cc854ef6d5e420897659b3dea15be9340f9f06c10eccd48ea77f5
3
+ metadata.gz: 145bd280f50a75b41f7ee8db8764ad61df4abb453104342b111d3e318a446976
4
+ data.tar.gz: a67950427a8cbc5c36456cd2e7397126b0f0ac8ed7b30563b699d0a5da8349b9
5
5
  SHA512:
6
- metadata.gz: 45a38afb12b658a7780d92575f4ebad4bd5809ba119ad1c4c3b694cc45b39b51d59737fa383b80795c248e0c3fc99956f4d5599e57bce2ec4998177af6db044f
7
- data.tar.gz: 24003570f17366a2dcab778d2e4b361622e05a86ade6ddcca95bc5f2da858f5ccb7c3579a8f6d96a3b5e7f2b4046a1dad89ce093c6312cfa5ab4bd16473645bd
6
+ metadata.gz: 12085289adf414dbe060ae5dca642c0b1b86abf0142bb519eee399373422f1a64a0a1a6dae6e08009fee8ea254a18e190483fe38b0d8014b178ec5b839ba8865
7
+ data.tar.gz: 801af3b96e8306bde0c4f2e90dd7895455cd12dd055d0de645f95babb974a486a35d247d87fad75f3ef9babd255743dd4e507e2fec04aee1c42706c4f222bbf5
@@ -0,0 +1,30 @@
1
+ name: Ruby Gem
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ jobs:
10
+ build:
11
+ name: Build + Publish
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+ - name: Set up Ruby 2.6
17
+ uses: actions/setup-ruby@v1
18
+ with:
19
+ version: 2.6.x
20
+
21
+ - name: Publish to RubyGems
22
+ run: |
23
+ mkdir -p $HOME/.gem
24
+ touch $HOME/.gem/credentials
25
+ chmod 0600 $HOME/.gem/credentials
26
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
27
+ gem build *.gemspec
28
+ gem push *.gem
29
+ env:
30
+ GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
@@ -0,0 +1,16 @@
1
+ name: RSpec
2
+ on: [pull_request]
3
+ jobs:
4
+ build:
5
+ runs-on: ubuntu-latest
6
+ steps:
7
+ - uses: actions/checkout@v2
8
+ - uses: actions/setup-ruby@v1
9
+ with:
10
+ version: 2.6
11
+ - name: Build
12
+ run: |
13
+ gem install bundler
14
+ bundle install
15
+ - name: rspec
16
+ run: bundle exec rspec
@@ -0,0 +1,16 @@
1
+ name: RuboCop
2
+ on: [pull_request]
3
+ jobs:
4
+ build:
5
+ runs-on: ubuntu-latest
6
+ steps:
7
+ - uses: actions/checkout@v2
8
+ - uses: actions/setup-ruby@v1
9
+ with:
10
+ version: 2.6
11
+ - name: Build
12
+ run: |
13
+ gem install bundler
14
+ bundle install
15
+ - name: rubocop
16
+ run: bundle exec rubocop
@@ -6,10 +6,14 @@ Style/ClassAndModuleChildren:
6
6
  Style/AsciiComments:
7
7
  Enabled: false
8
8
  Layout/LineLength:
9
- Max: 120
9
+ Max: 393
10
+ Exclude:
11
+ - "spec/**/*"
10
12
  Style/Documentation:
11
13
  Enabled: false
12
-
14
+ Metrics/BlockLength:
15
+ Exclude:
16
+ - "spec/**/*"
13
17
  AllCops:
14
18
  Exclude:
15
19
  - tmp/**/*
@@ -1,31 +1,7 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2020-03-31 00:06:07 +0900 using RuboCop version 0.80.1.
3
+ # on 2020-04-12 14:58:59 +0900 using RuboCop version 0.80.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
-
9
- # Offense count: 6
10
- # Cop supports --auto-correct.
11
- # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
12
- # URISchemes: http, https
13
- Layout/LineLength:
14
- Max: 393
15
-
16
- # Offense count: 2
17
- Metrics/AbcSize:
18
- Max: 23
19
-
20
- # Offense count: 1
21
- Metrics/CyclomaticComplexity:
22
- Max: 8
23
-
24
- # Offense count: 1
25
- # Configuration parameters: CountComments, ExcludedMethods.
26
- Metrics/MethodLength:
27
- Max: 20
28
-
29
- # Offense count: 1
30
- Metrics/PerceivedComplexity:
31
- Max: 9
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- month_range (0.1.3)
4
+ month_range (0.1.5)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,4 +1,7 @@
1
1
  # MonthRange
2
+ ![](https://github.com/junara/month_range/workflows/RuboCop/badge.svg)
3
+ ![](https://github.com/junara/month_range/workflows/RSpec/badge.svg)
4
+
2
5
  Calculate month range union and difference including unterminated end.
3
6
 
4
7
 
@@ -1,107 +1,90 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class MonthRange::Collection
4
- attr_reader :collection_ranges
4
+ attr_reader :stored_m_ranges
5
5
 
6
- def initialize
7
- @collection_ranges = []
6
+ def initialize(m_ranges = [])
7
+ @stored_m_ranges = []
8
+ m_ranges.each { |m_range| add(m_range) }
8
9
  end
9
10
 
10
11
  def to_a
11
- @collection_ranges.map { |collection_range| [collection_range.start_month.to_date, collection_range.end_month.to_date] }
12
+ @stored_m_ranges.map do |collection_range|
13
+ [collection_range.start_month.to_date, collection_range.end_month.to_date]
14
+ end
12
15
  end
13
16
 
14
- def add(range)
15
- raise MonthRange::Error, 'Need MonthRange::MRange' unless range.is_a?(MonthRange::MRange)
16
- return @collection_ranges << range if @collection_ranges.empty?
17
-
18
- overlapped_collection_ranges = @collection_ranges.select { |collection_range| collection_range.overlap?(range) }
19
- return @collection_ranges if overlapped_collection_ranges.nil?
17
+ def add(m_range)
18
+ raise MonthRange::Error, 'Need MonthRange::MRange' unless m_range.is_a?(MonthRange::MRange)
19
+ return @stored_m_ranges << m_range if @stored_m_ranges.empty?
20
20
 
21
- update_collection_ranges(overlapped_collection_ranges, range) { |rs, r| [merge_range(rs, r)] }
21
+ update_stored_m_ranges(m_range) do |overlapped_collection_ranges|
22
+ [merge_range(overlapped_collection_ranges, m_range)]
23
+ end
22
24
  end
23
25
 
24
- def subtract(range)
25
- raise MonthRange::Error, 'Need MonthRange::MRange' unless range.is_a?(MonthRange::MRange)
26
- raise if @collection_ranges.empty?
27
-
28
- overlapped_collection_ranges = @collection_ranges.select { |collection_range| collection_range.overlap?(range) }
29
- return @collection_ranges if overlapped_collection_ranges.nil?
26
+ def subtract(m_range)
27
+ raise MonthRange::Error, 'Need MonthRange::MRange' unless m_range.is_a?(MonthRange::MRange)
28
+ return if @stored_m_ranges.empty?
30
29
 
31
- update_collection_ranges(overlapped_collection_ranges, range) { |rs, r| subtract_range(rs, r) }
32
- end
33
-
34
- def self.new_from_date_range_array(date_range_array)
35
- collection = new
36
- date_range_array.each do |range|
37
- start_month = range[0]
38
- end_month = range[1]
39
- m_range = MonthRange::MRange.new(start_month, end_month)
40
- collection.add(m_range)
30
+ update_stored_m_ranges(m_range) do |overlapped_collection_ranges|
31
+ subtract_range(overlapped_collection_ranges, m_range)
41
32
  end
42
- collection
43
33
  end
44
34
 
45
35
  private
46
36
 
47
- def update_collection_ranges(overlapped_collection_ranges, range)
48
- @collection_ranges -= overlapped_collection_ranges
49
- @collection_ranges += yield(overlapped_collection_ranges, range)
50
- @collection_ranges = sort_ranges_asc(@collection_ranges.flatten.compact)
51
- @collection_ranges = combine_continuous_range(@collection_ranges)
52
- @collection_ranges
37
+ def overlapped_collection_ranges(m_range)
38
+ @stored_m_ranges.select { |collection_range| collection_range.overlap?(m_range) }
39
+ end
40
+
41
+ def update_stored_m_ranges(m_range)
42
+ m_ranges = overlapped_collection_ranges(m_range)
43
+ delete_m_ranges_to_stored(m_ranges)
44
+ add_m_ranges_to_stored(yield(m_ranges))
53
45
  end
54
46
 
55
- def sort_ranges_asc(ranges)
56
- return ranges if ranges.size <= 1
47
+ def delete_m_ranges_to_stored(m_ranges)
48
+ @stored_m_ranges -= m_ranges
49
+ @stored_m_ranges = combine_continuous_range(sort_ranges_asc(@stored_m_ranges.flatten.compact))
50
+ end
57
51
 
58
- ranges.sort { |a, b| a.start_month <=> b.start_month }
52
+ def add_m_ranges_to_stored(m_ranges)
53
+ @stored_m_ranges += m_ranges
54
+ @stored_m_ranges = combine_continuous_range(sort_ranges_asc(@stored_m_ranges.flatten.compact))
59
55
  end
60
56
 
61
- def merge_range(ranges, range)
62
- start_month = (ranges + [range]).map(&:start_month).min { |a, b| a <=> b }
63
- end_month = (ranges + [range]).map(&:end_month).max { |a, b| a <=> b }
64
- MonthRange::MRange.new(start_month, end_month)
57
+ def sort_ranges_asc(m_ranges)
58
+ return m_ranges if m_ranges.size <= 1
59
+
60
+ m_ranges.sort { |a, b| a.start_month <=> b.start_month }
65
61
  end
66
62
 
67
- def subtract_range(ranges, range)
68
- output_range = []
69
- ranges.each do |r|
70
- output_range << r.subtract(range)
71
- end
72
- output_range.flatten.compact
63
+ def merge_range(m_ranges, m_range)
64
+ start_month = (m_ranges + [m_range]).map(&:start_month).min { |a, b| a <=> b }
65
+ end_month = (m_ranges + [m_range]).map(&:end_month).max { |a, b| a <=> b }
66
+ MonthRange::MRange.new(start_month, end_month)
73
67
  end
74
68
 
75
- def combine_continuous_range(ranges)
76
- return ranges if ranges.count <= 1
77
-
78
- temp_range = nil
79
- output_ranges = []
80
- ranges.each_with_index do |range, idx|
81
- (temp_range = range) && next if idx.zero?
82
-
83
- if idx == ranges.size - 1 && continuous?(temp_range, range)
84
- output_ranges << MonthRange::MRange.new(temp_range.start_month, range.end_month)
85
- next
86
- elsif idx == ranges.size - 1
87
- output_ranges << temp_range
88
- output_ranges << range
89
- next
90
- elsif continuous?(temp_range, range)
91
- temp_range = MonthRange::MRange.new(temp_range.start_month, range.end_month)
92
- next
93
- end
94
- output_ranges << temp_range
95
- temp_range = range
69
+ def subtract_range(m_ranges, m_range)
70
+ output_m_range = []
71
+ m_ranges.each do |r|
72
+ output_m_range << r.subtract(m_range)
96
73
  end
97
- output_ranges
74
+ output_m_range.flatten.compact
98
75
  end
99
76
 
100
- def continuous?(range, next_range)
101
- return false if range.nil? || next_range.nil?
102
- raise if range.start_month >= next_range.start_month
103
- return false if range.end_month.infinite?
77
+ def combine_continuous_range(m_ranges) # rubocop:disable Metrics/CyclomaticComplexity
78
+ return m_ranges if m_ranges.count <= 1
104
79
 
105
- range.end_month == next_range.just_before
80
+ before_m_range = nil
81
+ output_m_ranges = []
82
+ m_ranges.each_with_index do |m_range, idx|
83
+ (before_m_range = m_range) && next if idx.zero?
84
+ (before_m_range = before_m_range.combine(m_range)) && next if before_m_range.continuous?(m_range)
85
+ output_m_ranges << before_m_range
86
+ before_m_range = m_range
87
+ end
88
+ output_m_ranges << before_m_range unless output_m_ranges.include?(before_m_range)
106
89
  end
107
90
  end
@@ -7,6 +7,8 @@ class MonthRange::MRange < Range
7
7
  attr_reader :start_month, :end_month
8
8
  class InvalidStartEnd < MonthRange::Error
9
9
  end
10
+ class NotMRange < MonthRange::Error
11
+ end
10
12
 
11
13
  def initialize(start_month, end_month)
12
14
  raise InvalidStartEnd unless start_month.is_a?(MonthRange::Month)
@@ -18,12 +20,10 @@ class MonthRange::MRange < Range
18
20
  @end_month = end_month
19
21
  end
20
22
 
21
- def overlap?(range)
22
- raise "#{range} must be MRange." unless range.is_a?(MonthRange::MRange)
23
+ def overlap?(m_range)
24
+ raise NotMRange, "#{m_range} must be MRange." unless m_range.is_a?(MonthRange::MRange)
23
25
 
24
- if cover?(range.start_month) || cover?(range.end_month) || (range.start_month <= start_month && end_month <= range.end_month)
25
- true
26
- end
26
+ true if cover?(m_range.start_month) || cover?(m_range.end_month) || m_range.cover?(self)
27
27
  end
28
28
 
29
29
  def non_terminated?
@@ -34,12 +34,12 @@ class MonthRange::MRange < Range
34
34
  !end_month.infinite?
35
35
  end
36
36
 
37
- def subtract(range)
38
- return self unless overlap?(range)
37
+ def subtract(m_range) # rubocop:disable Metrics/AbcSize
38
+ return self unless overlap?(m_range)
39
39
 
40
40
  output_range = []
41
- output_range << MonthRange::MRange.new(start_month, range.just_before) if cover?(range.start_month)
42
- output_range << MonthRange::MRange.new(range.just_after, end_month) if cover?(range.end_month) && range.terminated?
41
+ output_range << MonthRange::MRange.new(start_month, m_range.just_before) if cover?(m_range.just_before)
42
+ output_range << MonthRange::MRange.new(m_range.just_after, end_month) if cover?(m_range.just_after) && m_range.terminated?
43
43
  output_range.flatten.compact
44
44
  end
45
45
 
@@ -51,6 +51,22 @@ class MonthRange::MRange < Range
51
51
  non_terminated? ? end_month : end_month.next_month(1)
52
52
  end
53
53
 
54
+ def continuous?(m_range)
55
+ return false if m_range.nil?
56
+ raise if start_month >= m_range.start_month
57
+ return false if end_month.infinite?
58
+
59
+ end_month == m_range.just_before
60
+ end
61
+
62
+ def combine(m_range)
63
+ if start_month < m_range.start_month
64
+ MonthRange::MRange.new(start_month, m_range.end_month)
65
+ else
66
+ MonthRange::MRange.new(m_range.start_month, end_month)
67
+ end
68
+ end
69
+
54
70
  private
55
71
 
56
72
  def valid_start_end_relation?(start_month, end_month)
@@ -19,21 +19,11 @@ class MonthRange::Month < Date
19
19
  Date.new(year, month, mday)
20
20
  end
21
21
 
22
- class Infinity < Numeric
23
- def initialize(d = 1)
24
- @d = d <=> 0
25
- end
26
-
27
- attr_reader :d
28
-
29
- def zero?
30
- false
31
- end
32
-
33
- def finite?
34
- false
35
- end
22
+ def infinite?
23
+ false
24
+ end
36
25
 
26
+ class Infinity < Numeric
37
27
  def infinite?
38
28
  true
39
29
  end
@@ -57,21 +47,15 @@ class MonthRange::Month < Date
57
47
  def <=>(other)
58
48
  case other
59
49
  when Infinity
60
- return d <=> other.d
50
+ return 1
61
51
  when MonthRange::Month
62
- return d
52
+ return 1
63
53
  end
64
54
  nil
65
55
  end
66
56
 
67
57
  def to_f
68
- return 0 if @d.zero?
69
-
70
- if @d.positive?
71
- Float::INFINITY
72
- else
73
- -Float::INFINITY
74
- end
58
+ Float::INFINITY
75
59
  end
76
60
  end
77
61
  end
@@ -30,12 +30,21 @@ class MonthRange::Service
30
30
  # MonthRange::Service.add(range_array, from_range_arrays)
31
31
  #
32
32
  # #=> [[#<Date: 2020-01-01 ((2458850j,0s,0n),+0s,2299161j)>, #<Date: 2020-04-01 ((2458941j,0s,0n),+0s,2299161j)>]]
33
- def self.subtraction(range_array, from_range_arrays)
34
- collection = range_arrays_to_collection(from_range_arrays)
35
- start_month = MonthRange::Month.create(range_array[0])
36
- end_month = MonthRange::Month.create(range_array[1])
37
- m_range = MonthRange::MRange.new(start_month, end_month)
38
- collection.subtract(m_range)
33
+ def self.subtraction(range_array, from_range_arrays) # rubocop:disable Metrics/MethodLength:
34
+ m_ranges = from_range_arrays.map do |range|
35
+ MonthRange::MRange.new(
36
+ MonthRange::Month.create(range[0]),
37
+ MonthRange::Month.create(range[1])
38
+ )
39
+ end
40
+ collection = MonthRange::Collection.new(m_ranges)
41
+
42
+ collection.subtract(
43
+ MonthRange::MRange.new(
44
+ MonthRange::Month.create(range_array[0]),
45
+ MonthRange::Month.create(range_array[1])
46
+ )
47
+ )
39
48
  collection.to_a
40
49
  end
41
50
 
@@ -69,26 +78,19 @@ class MonthRange::Service
69
78
  # MonthRange::Service.add(range_array, from_range_arrays)
70
79
  #
71
80
  # #=> [[#<Date: 2020-01-01 ((2458850j,0s,0n),+0s,2299161j)>, nil]]
72
- def self.add(range_array, from_range_arrays)
73
- collection = range_arrays_to_collection(from_range_arrays)
74
- start_month = MonthRange::Month.create(range_array[0])
75
- end_month = MonthRange::Month.create(range_array[1])
76
- m_range = MonthRange::MRange.new(start_month, end_month)
77
- collection.add(m_range)
78
- collection.to_a
79
- end
80
-
81
- # Change to MonthRange::Collection
82
- # @param [[[Date, Date], [Date, Date],]] range_arrays
83
- # @return [MonthRange::Collection]
84
- def self.range_arrays_to_collection(range_arrays)
85
- collection = MonthRange::Collection.new
86
- range_arrays.each do |range|
87
- start_month = MonthRange::Month.create(range[0])
88
- end_month = MonthRange::Month.create(range[1])
89
- m_range = MonthRange::MRange.new(start_month, end_month)
90
- collection.add(m_range)
81
+ def self.add(range_array, from_range_arrays) # rubocop:disable Metrics/MethodLength
82
+ m_ranges = from_range_arrays.map do |range|
83
+ MonthRange::MRange.new(
84
+ MonthRange::Month.create(range[0]),
85
+ MonthRange::Month.create(range[1])
86
+ )
91
87
  end
92
- collection
88
+ collection = MonthRange::Collection.new(m_ranges)
89
+
90
+ collection.add(MonthRange::MRange.new(
91
+ MonthRange::Month.create(range_array[0]),
92
+ MonthRange::Month.create(range_array[1])
93
+ ))
94
+ collection.to_a
93
95
  end
94
96
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module MonthRange
4
4
  class Version
5
- VERSION = '0.1.4'
5
+ VERSION = '0.1.5'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: month_range
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - jungo araki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-04-05 00:00:00.000000000 Z
11
+ date: 2020-04-12 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -17,6 +17,9 @@ executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
+ - ".github/workflows/gempush.yml"
21
+ - ".github/workflows/rspec.yml"
22
+ - ".github/workflows/rubocop.yml"
20
23
  - ".gitignore"
21
24
  - ".rspec"
22
25
  - ".rubocop.yml"
@@ -61,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
61
64
  - !ruby/object:Gem::Version
62
65
  version: '0'
63
66
  requirements: []
64
- rubygems_version: 3.1.2
67
+ rubygems_version: 3.0.3
65
68
  signing_key:
66
69
  specification_version: 4
67
70
  summary: Addition and subtraction for month range.