active_period 7.0.0 → 7.1.2
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 +4 -4
- data/Gemfile.lock +7 -7
- data/README.md +38 -0
- data/active_period.gemspec +1 -1
- data/config/locales/fr.yml +3 -3
- data/lib/active_period/comparable.rb +3 -1
- data/lib/active_period/period.rb +64 -6
- data/lib/active_period/version.rb +1 -1
- data/lib/period.rb +36 -7
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e197dc4558fd215fc7090f5a14ad074e769457abfa7665b4b2393e5ba2a23d52
|
4
|
+
data.tar.gz: 54c3de6172706d0f08e2885b6401b3f1c964526498193a63c13af4fbfab6eb99
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a228d61ffcb56d1f9206127e22775e2a3ec109822ce0b8242e6e778d8edb08531dd9b2834cc7def80cb42ec7cf148b2abc8776059228c5fb7b7d9cb90ec68562
|
7
|
+
data.tar.gz: 25550cbd55706774bfd34e68690be5d39b2e01b6681f33b3134d3c7169949c9e1d017ac40cadd24d041eef2c85d3ce6c05dbb8fe76761d6a2fabebe501230e24
|
data/Gemfile.lock
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
active_period (7.
|
5
|
-
activesupport (
|
4
|
+
active_period (7.1.1)
|
5
|
+
activesupport (>= 5, < 8)
|
6
6
|
i18n (~> 1)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
activesupport (7.0.
|
11
|
+
activesupport (7.0.4)
|
12
12
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
13
13
|
i18n (>= 1.6, < 2)
|
14
14
|
minitest (>= 5.1)
|
15
15
|
tzinfo (~> 2.0)
|
16
16
|
builder (3.2.4)
|
17
|
-
concurrent-ruby (1.1.
|
17
|
+
concurrent-ruby (1.1.10)
|
18
18
|
cucumber (7.0.0)
|
19
19
|
builder (~> 3.2, >= 3.2.4)
|
20
20
|
cucumber-core (~> 10.0, >= 10.0.1)
|
@@ -48,17 +48,17 @@ GEM
|
|
48
48
|
cucumber-messages (~> 17.0, >= 17.0.1)
|
49
49
|
diff-lcs (1.4.4)
|
50
50
|
ffi (1.15.3)
|
51
|
-
i18n (1.
|
51
|
+
i18n (1.12.0)
|
52
52
|
concurrent-ruby (~> 1.0)
|
53
53
|
mime-types (3.3.1)
|
54
54
|
mime-types-data (~> 3.2015)
|
55
55
|
mime-types-data (3.2021.0704)
|
56
|
-
minitest (5.
|
56
|
+
minitest (5.16.3)
|
57
57
|
multi_test (0.1.2)
|
58
58
|
rake (10.5.0)
|
59
59
|
sys-uname (1.2.2)
|
60
60
|
ffi (~> 1.1)
|
61
|
-
tzinfo (2.0.
|
61
|
+
tzinfo (2.0.5)
|
62
62
|
concurrent-ruby (~> 1.0)
|
63
63
|
|
64
64
|
PLATFORMS
|
data/README.md
CHANGED
@@ -82,6 +82,9 @@ Period.new(params[:start_date]..params[:end_date])
|
|
82
82
|
|
83
83
|
# or from a range
|
84
84
|
('01/01/2000'...'01/02/2000').to_period
|
85
|
+
|
86
|
+
# you can also use [] if .new is too long for you
|
87
|
+
Period['01/01/2000'...'01/02/2000']
|
85
88
|
```
|
86
89
|
|
87
90
|
**Note** : `to_period` will always return a **FreePeriod**
|
@@ -204,6 +207,41 @@ These methods return a **StandardPeriod** who include the current period
|
|
204
207
|
Period.this_year.months.second.weeks.last.days.third
|
205
208
|
```
|
206
209
|
|
210
|
+
## Period Combination with `&` and `|`
|
211
|
+
|
212
|
+
You can use `&` to combine overlapping periods
|
213
|
+
And `|` to combine overlapping and tail to head periods
|
214
|
+
If the given periods cannot combine, then `nil` will be return
|
215
|
+
The period we take the ending date from, determine if the ending date is included or excluded
|
216
|
+
|
217
|
+
#### Example for `&`
|
218
|
+
```ruby
|
219
|
+
# Overlapping periods
|
220
|
+
(Period['01/01/2021'..'20/01/2021'] & Period['10/01/2021'...'30/01/2021']).to_s
|
221
|
+
=> "From the 10 January 2021 to the 20 January 2021 included"
|
222
|
+
|
223
|
+
# Theses period cannot combine
|
224
|
+
Period.this_month & Period.next_month
|
225
|
+
=> nil
|
226
|
+
```
|
227
|
+
|
228
|
+
#### Example for `|`
|
229
|
+
```ruby
|
230
|
+
# Overlapping periods
|
231
|
+
(Period['01/01/2021'..'20/01/2021'] | Period['10/01/2021'...'30/01/2021']).to_s
|
232
|
+
=> "From the 01 January 2021 to the 30 January 2021 excluded"
|
233
|
+
|
234
|
+
# Example with tail to head
|
235
|
+
(Period.this_month | Period.next_month).to_s
|
236
|
+
=> "From the 01 September 2022 to the 31 October 2022 included"
|
237
|
+
(Period['01/01/2021'..'09/01/2021'] | Period['10/01/2021'..'20/01/2021']).to_s
|
238
|
+
=> "From the 01 January 2021 to the 20 January 2021 included"
|
239
|
+
|
240
|
+
# Theses period cannot combine
|
241
|
+
Period['01/01/2021'...'09/01/2021'] | Period['10/01/2021'..'20/01/2021']
|
242
|
+
=> nil
|
243
|
+
```
|
244
|
+
|
207
245
|
## Boundless Period
|
208
246
|
|
209
247
|
Boundless period are fully supported and work as you expect them to do
|
data/active_period.gemspec
CHANGED
@@ -38,7 +38,7 @@ Gem::Specification.new do |spec|
|
|
38
38
|
|
39
39
|
spec.required_ruby_version = '>= 2.7'
|
40
40
|
|
41
|
-
spec.add_runtime_dependency 'activesupport', '>= 5', '
|
41
|
+
spec.add_runtime_dependency 'activesupport', '>= 5', '< 8'
|
42
42
|
spec.add_runtime_dependency 'i18n', '~> 1'
|
43
43
|
|
44
44
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
data/config/locales/fr.yml
CHANGED
@@ -5,7 +5,7 @@ fr:
|
|
5
5
|
comparable:
|
6
6
|
incomparable_error: Les arguments ne sont pas comparables
|
7
7
|
collection:
|
8
|
-
param_period_must_be_a_period:
|
8
|
+
param_period_must_be_a_period: Le paramètre doit hériter de la class Period
|
9
9
|
period:
|
10
10
|
base_class_is_abstract: Period est une class abstraite qui ne doit pas être utiliser directement
|
11
11
|
begin_date_is_invalid: Date de début invalide
|
@@ -15,8 +15,8 @@ fr:
|
|
15
15
|
param_must_be_a_range: Le paramètre doit hériter de la class Range
|
16
16
|
free_period:
|
17
17
|
default_format: Du %{from} au %{to} %{ending}
|
18
|
-
beginless_format: Jusqu'au %{
|
19
|
-
endless_format: Depuis le %{
|
18
|
+
beginless_format: Jusqu'au %{to} %{ending}
|
19
|
+
endless_format: Depuis le %{from}
|
20
20
|
boundless_format: Sans limite de temps
|
21
21
|
standard_period:
|
22
22
|
base_class_is_abstract: StandardPeriod est une class abstraite qui ne doit pas être utiliser directement
|
@@ -16,12 +16,14 @@ module ActivePeriod
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def <=>(other)
|
19
|
+
raise ArgumentError, I18n.t(:incomparable_error, scope: %i[active_period comparable]) unless other.is_a?(ActiveSupport::Duration)
|
20
|
+
|
19
21
|
if other.is_a?(ActiveSupport::Duration) || other.is_a?(Numeric)
|
20
22
|
to_i <=> other.to_i
|
21
23
|
elsif self.class != other.class
|
22
24
|
raise ArgumentError, I18n.t(:incomparable_error, scope: %i[active_period comparable])
|
23
25
|
else
|
24
|
-
(
|
26
|
+
(self.begin <=> other)
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
data/lib/active_period/period.rb
CHANGED
@@ -9,7 +9,7 @@ class ActivePeriod::Period < Range
|
|
9
9
|
# @param range [Range] A valid range
|
10
10
|
# @param allow_beginless [Boolean] Is it allow to creat a beginless range
|
11
11
|
# @param allow_endless [Boolean] Is it allow to creat an endless range
|
12
|
-
# @return [self] A new instance of ActivePeriod::
|
12
|
+
# @return [self] A new instance of ActivePeriod::Period
|
13
13
|
# @raise ArgumentError if the params range is not a Range
|
14
14
|
# @raise ArgumentError if the params range is invalid
|
15
15
|
def initialize(range, allow_beginless: true, allow_endless: true)
|
@@ -25,8 +25,6 @@ class ActivePeriod::Period < Range
|
|
25
25
|
raise ::ArgumentError, I18n.t(:end_date_is_invalid, scope: %i[active_period period]) if !allow_endless && to.nil?
|
26
26
|
to = to.try(:end_of_day) || to
|
27
27
|
|
28
|
-
# raise ::ArgumentError, I18n.t(:endless_excluded_end_is_forbiden, scope: %i[active_period period]) if to.nil? && range.exclude_end?
|
29
|
-
# to = to.prev_day if range.exclude_end?
|
30
28
|
if range.exclude_end? && from && to && from.to_date == to.to_date
|
31
29
|
raise ::ArgumentError, I18n.t(:start_is_equal_to_end_excluded, scope: %i[active_period period])
|
32
30
|
end
|
@@ -70,19 +68,79 @@ class ActivePeriod::Period < Range
|
|
70
68
|
raise NotImplementedError
|
71
69
|
end
|
72
70
|
|
73
|
-
# @param other [ActivePeriod::
|
71
|
+
# @param other [ActivePeriod::Period, ActiveSupport::Duration, Numeric] Any kind of ActivePeriod::Period, ActiveSupport::Duration or Numeric (in seconds)
|
74
72
|
# @return [Boolean] true if period are equals, false otherwise
|
75
|
-
# @raise ArgumentError if params other is not a ActivePeriod::
|
73
|
+
# @raise ArgumentError if params other is not a ActivePeriod::Period of some kind
|
76
74
|
def ==(other)
|
77
75
|
if other.class.ancestors.include?(ActivePeriod::Period)
|
78
76
|
from == other.from && self.calculated_end == other.calculated_end
|
79
|
-
elsif other.is_a?(ActiveSupport::Duration)
|
77
|
+
elsif other.is_a?(ActiveSupport::Duration)
|
80
78
|
super(other)
|
79
|
+
elsif other.is_a?(Numeric)
|
80
|
+
super(other.seconds)
|
81
81
|
else
|
82
82
|
raise ArgumentError
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
|
+
# @param other [ActivePeriod::Period] Any kind of ActivePeriod::Period object
|
87
|
+
# @return [Boolean] true if period and class are the same, false otherwise
|
88
|
+
# @raise ArgumentError if params other is not a ActivePeriod::Period of some kind
|
89
|
+
def ===(other)
|
90
|
+
self == other && self.class == other.class
|
91
|
+
end
|
92
|
+
|
93
|
+
# @param other [ActivePeriod::Period] Any kind of ActivePeriod::Period object
|
94
|
+
# @return [ActivePeriod::Period, nil] Any kind of ActivePeriod::FreePeriod if period overlap, nil otherwise
|
95
|
+
# @raise ArgumentError if params other is not a ActivePeriod::Period of some kind
|
96
|
+
def &(other)
|
97
|
+
raise ArgumentError, I18n.t(:incomparable_error, scope: %i[active_period comparable]) unless other.class.ancestors.include?(ActivePeriod::Period)
|
98
|
+
|
99
|
+
# self 9------12
|
100
|
+
# other 1-----------10
|
101
|
+
if self.begin.in?(other) && !calculated_end.in?(other)
|
102
|
+
Period.new( Range.new(self.begin, other.to, other.exclude_end?) )
|
103
|
+
# self 1-----------10
|
104
|
+
# other 9------12
|
105
|
+
elsif !self.begin.in?(other) && calculated_end.in?(other)
|
106
|
+
Period.new( Range.new(other.begin, to, exclude_end?) )
|
107
|
+
# self 5-----8
|
108
|
+
# other 1-----------10
|
109
|
+
elsif other.include?(self)
|
110
|
+
other
|
111
|
+
# self 1-----------10
|
112
|
+
# other 5-----8
|
113
|
+
elsif self.include?(other)
|
114
|
+
self
|
115
|
+
else
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# @param other [ActivePeriod::Period] Any kind of ActivePeriod::Period object
|
121
|
+
# @return [ActivePeriod::Period, nil] Any kind of ActivePeriod::FreePeriod if period overlap, nil otherwise
|
122
|
+
# @raise ArgumentError if params other is not a ActivePeriod::Period of some kind
|
123
|
+
def |(other)
|
124
|
+
raise ArgumentError, I18n.t(:incomparable_error, scope: %i[active_period comparable]) unless other.class.ancestors.include?(ActivePeriod::Period)
|
125
|
+
|
126
|
+
# overlapping or tail to head
|
127
|
+
if self.begin.in?(other) || self.calculated_end.in?(other) || (
|
128
|
+
((self.calculated_end+1.day).beginning_of_day == other.from) ^
|
129
|
+
((other.calculated_end+1.day).beginning_of_day == self.from)
|
130
|
+
)
|
131
|
+
Period.new(
|
132
|
+
Range.new(
|
133
|
+
[self.begin, other.begin].min,
|
134
|
+
[self.to, other.to].max,
|
135
|
+
self.calculated_end > other.calculated_end ? self.exclude_end? : other.exclude_end?
|
136
|
+
)
|
137
|
+
)
|
138
|
+
# no overlapping
|
139
|
+
else
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
86
144
|
# @raise NotImplementedError This method should be implemented in daughter class
|
87
145
|
def strftime
|
88
146
|
raise NotImplementedError
|
data/lib/period.rb
CHANGED
@@ -12,6 +12,11 @@ module Period
|
|
12
12
|
ActivePeriod::FreePeriod.new(range, allow_beginless: false, allow_endless: false)
|
13
13
|
end
|
14
14
|
|
15
|
+
# Shorthand to Period.new
|
16
|
+
def self.[](range)
|
17
|
+
Period.new(range)
|
18
|
+
end
|
19
|
+
|
15
20
|
# env_time provide a Fallback if the project dont specify any Time.zone
|
16
21
|
def self.env_time
|
17
22
|
(Time.zone || Time)
|
@@ -45,32 +50,56 @@ module Period
|
|
45
50
|
alias tomorrow next_day
|
46
51
|
alias today this_day
|
47
52
|
|
48
|
-
|
53
|
+
LAST_OR_NEXT_REGEX = /^(:?last|next)_(\d+)_(day|week|month|quarter|year)s?(_from_now)?$/
|
54
|
+
LAST_AND_NEXT_REGEX = /^last_(\d+)_(day|week|month|quarter|year)s?_to_next_(\d+)_(day|week|month|quarter|year)s?$/
|
55
|
+
|
56
|
+
# Experimenta l non-documented feature
|
49
57
|
# Inpired form ActiveRecord dynamic find_by_x like User.find_by_name
|
50
58
|
# Example: Period.last_3_weeks_from_now == Period.mew(2.weeks.ago.beginning_of_week..Time.now.end_of_week)
|
51
|
-
# Note : Maybe it should return a collection of StandardPeriod
|
52
59
|
def method_missing(method_name, *arguments, &block)
|
53
|
-
|
54
|
-
|
60
|
+
if method_name.match? LAST_OR_NEXT_REGEX
|
61
|
+
missing_last_or_next(method_name)
|
62
|
+
elsif method_name.match? LAST_AND_NEXT_REGEX
|
63
|
+
missing_last_and_next(method_name)
|
64
|
+
else
|
65
|
+
super
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def missing_last_or_next(method_name)
|
70
|
+
last_next, count, klass, from_now = method_name.to_s.scan(LAST_OR_NEXT_REGEX).flatten
|
55
71
|
klass = klass.singularize
|
56
72
|
|
57
73
|
case last_next
|
58
74
|
when 'last'
|
59
75
|
from = count.to_i.send(klass).ago.send("beginning_of_#{klass}")
|
60
76
|
to = env_time.now
|
61
|
-
to -= 1.send(klass) unless
|
77
|
+
to -= 1.send(klass) unless from_now
|
62
78
|
to = to.send("end_of_#{klass}")
|
63
79
|
when 'next'
|
64
80
|
from = env_time.now
|
65
|
-
from += 1.send(klass) unless
|
81
|
+
from += 1.send(klass) unless from_now
|
66
82
|
from = from.send("beginning_of_#{klass}")
|
67
83
|
to = count.to_i.send(klass).from_now.send("end_of_#{klass}")
|
68
84
|
end
|
69
85
|
self.new(from..to)
|
70
86
|
end
|
71
87
|
|
88
|
+
def missing_last_and_next(method_name)
|
89
|
+
last_count, last_klass, next_count, next_klass = method_name.to_s.scan(LAST_AND_NEXT_REGEX).flatten
|
90
|
+
last_klass = last_klass.singularize
|
91
|
+
next_klass = next_klass.singularize
|
92
|
+
|
93
|
+
from = last_count.to_i.send(last_klass).ago.send("beginning_of_#{last_klass}")
|
94
|
+
to = next_count.to_i.send(next_klass).from_now.send("end_of_#{next_klass}")
|
95
|
+
|
96
|
+
self.new(from..to)
|
97
|
+
end
|
98
|
+
|
72
99
|
def respond_to_missing?(method_name, include_private = false)
|
73
|
-
method_name.match?(/(last|next)_\d+_(day|week|month|quarter|year)s?(_from_now)?/) ||
|
100
|
+
method_name.match?(/(last|next)_\d+_(day|week|month|quarter|year)s?(_from_now)?/) ||
|
101
|
+
method_name.match?(/(last)_\d+_(day|week|month|quarter|year)s?_to_next_\d+_(day|week|month|quarter|year)s?/ ) ||
|
102
|
+
super
|
74
103
|
end
|
75
104
|
end
|
76
105
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_period
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.
|
4
|
+
version: 7.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- billau_l
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-05-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -17,9 +17,9 @@ dependencies:
|
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '5'
|
20
|
-
- - "
|
20
|
+
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
22
|
+
version: '8'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -27,9 +27,9 @@ dependencies:
|
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '5'
|
30
|
-
- - "
|
30
|
+
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
32
|
+
version: '8'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: i18n
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -159,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
159
159
|
- !ruby/object:Gem::Version
|
160
160
|
version: '0'
|
161
161
|
requirements: []
|
162
|
-
rubygems_version: 3.
|
162
|
+
rubygems_version: 3.3.22
|
163
163
|
signing_key:
|
164
164
|
specification_version: 4
|
165
165
|
summary: Manage time ranges without brain damage.
|