month_range 0.1.4 → 0.1.5

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.
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.