active_period 6.1.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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +36 -0
- data/LICENSE.txt +21 -0
- data/README.md +342 -0
- data/Rakefile +10 -0
- data/active_period.gemspec +44 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/config/locales/en.yml +36 -0
- data/config/locales/fr.yml +59 -0
- data/lib/.DS_Store +0 -0
- data/lib/active_period/belongs_to/month.rb +12 -0
- data/lib/active_period/belongs_to/quarter.rb +12 -0
- data/lib/active_period/belongs_to/week.rb +12 -0
- data/lib/active_period/belongs_to/year.rb +12 -0
- data/lib/active_period/belongs_to.rb +7 -0
- data/lib/active_period/collection/free_period.rb +37 -0
- data/lib/active_period/collection/holiday_period.rb +44 -0
- data/lib/active_period/collection/standard_period.rb +33 -0
- data/lib/active_period/collection.rb +60 -0
- data/lib/active_period/comparable.rb +51 -0
- data/lib/active_period/day.rb +38 -0
- data/lib/active_period/free_period.rb +86 -0
- data/lib/active_period/has_many/days.rb +14 -0
- data/lib/active_period/has_many/holidays.rb +15 -0
- data/lib/active_period/has_many/months.rb +14 -0
- data/lib/active_period/has_many/quarters.rb +14 -0
- data/lib/active_period/has_many/weeks.rb +15 -0
- data/lib/active_period/has_many/years.rb +15 -0
- data/lib/active_period/has_many.rb +7 -0
- data/lib/active_period/holiday.rb +80 -0
- data/lib/active_period/month.rb +37 -0
- data/lib/active_period/period.rb +158 -0
- data/lib/active_period/quarter.rb +42 -0
- data/lib/active_period/standard_period.rb +51 -0
- data/lib/active_period/version.rb +5 -0
- data/lib/active_period/week.rb +41 -0
- data/lib/active_period/year.rb +34 -0
- data/lib/active_period.rb +28 -0
- data/lib/numeric.rb +6 -0
- data/lib/period.rb +63 -0
- data/lib/range.rb +8 -0
- metadata +146 -0
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'active_period'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
en:
|
2
|
+
active_period:
|
3
|
+
included: included
|
4
|
+
excluded: excluded
|
5
|
+
comparable:
|
6
|
+
incomparable_error: Cannot compare Arguments
|
7
|
+
collection:
|
8
|
+
param_period_must_be_a_period: The parameter perod must inherit from the Period class
|
9
|
+
period:
|
10
|
+
base_class_is_abstract: Period is not intended to be used directly
|
11
|
+
begin_date_is_invalid: The beginning date is invalid
|
12
|
+
end_date_is_invalid: The ending date is invalid
|
13
|
+
start_is_equal_to_end_excluded: The beginning date and ending date cannot be equal when the ending date is excluded
|
14
|
+
start_is_greater_than_end: The beginning date is greater than the ending date
|
15
|
+
param_must_be_a_range: The parameter must inherit from the Range class
|
16
|
+
free_period:
|
17
|
+
default_format: From the %{from} to the %{to} %{ending}
|
18
|
+
beginless_format: Until the %{to} %{ending}
|
19
|
+
endless_format: From the %{from}
|
20
|
+
boundless_format: Limitless time range
|
21
|
+
standard_period:
|
22
|
+
base_class_is_abstract: StandardPeriod is not intended to be used directly
|
23
|
+
date_is_invalid: The date is invalid
|
24
|
+
day:
|
25
|
+
default_format: '%{wday} %{day} %{month} %{year}'
|
26
|
+
week:
|
27
|
+
default_format: Week %{week} of %{year}
|
28
|
+
month:
|
29
|
+
default_format: '%{month} %{year}'
|
30
|
+
quarter:
|
31
|
+
default_format: '%{quarter_nb} quarter %{year}'
|
32
|
+
year:
|
33
|
+
default_format: '%{year}'
|
34
|
+
holiday_period:
|
35
|
+
gem_require: 'The gem "holidays" is needed for this feature to work'
|
36
|
+
default_format: '%{name} - %{wday} %{day} %{month} %{year}'
|
@@ -0,0 +1,59 @@
|
|
1
|
+
fr:
|
2
|
+
active_period:
|
3
|
+
included: inclus
|
4
|
+
excluded: exclue
|
5
|
+
comparable:
|
6
|
+
incomparable_error: Les arguments ne sont pas comparables
|
7
|
+
collection:
|
8
|
+
param_period_must_be_a_period: Le paramètre doit hériter de la class Period
|
9
|
+
period:
|
10
|
+
base_class_is_abstract: Period est une class abstraite qui ne doit pas être utiliser directement
|
11
|
+
begin_date_is_invalid: Date de début invalide
|
12
|
+
end_date_is_invalid: Date de fin invalide
|
13
|
+
start_is_equal_to_end_excluded: La date de début et de fin ne peuvent pas être égale quand la date de fin est exclue
|
14
|
+
start_is_greater_than_end: Date de début supérieur à la date de fin
|
15
|
+
param_must_be_a_range: Le paramètre doit hériter de la class Range
|
16
|
+
free_period:
|
17
|
+
default_format: Du %{from} au %{to} %{ending}
|
18
|
+
beginless_format: Jusqu'au %{from} %{ending}
|
19
|
+
endless_format: Depuis le %{to}
|
20
|
+
boundless_format: Sans limite de temps
|
21
|
+
standard_period:
|
22
|
+
base_class_is_abstract: StandardPeriod est une class abstraite qui ne doit pas être utiliser directement
|
23
|
+
date_is_invalid: La date est invalide
|
24
|
+
day:
|
25
|
+
default_format: '%{wday} %{day} %{month} %{year}'
|
26
|
+
week:
|
27
|
+
default_format: Semaine %{week} de l'année %{year}
|
28
|
+
month:
|
29
|
+
default_format: '%{month} %{year}'
|
30
|
+
quarter:
|
31
|
+
default_format: '%{quarter_nb} semestre %{year}'
|
32
|
+
year:
|
33
|
+
default_format: '%{year}'
|
34
|
+
holiday_period:
|
35
|
+
gem_require: 'La gem "holidays" est requise pour activer cette fonctionnalitée'
|
36
|
+
default_format: '%{name} - %{wday} %{day} %{month} %{year}'
|
37
|
+
date:
|
38
|
+
month_names:
|
39
|
+
-
|
40
|
+
- janvier
|
41
|
+
- février
|
42
|
+
- mars
|
43
|
+
- avril
|
44
|
+
- mai
|
45
|
+
- juin
|
46
|
+
- juillet
|
47
|
+
- août
|
48
|
+
- septembre
|
49
|
+
- octobre
|
50
|
+
- novembre
|
51
|
+
- décembre
|
52
|
+
day_names:
|
53
|
+
- dimanche
|
54
|
+
- lundi
|
55
|
+
- mardi
|
56
|
+
- mercredi
|
57
|
+
- jeudi
|
58
|
+
- vendredi
|
59
|
+
- samedi
|
data/lib/.DS_Store
ADDED
Binary file
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ActivePeriod
|
2
|
+
module BelongsTo
|
3
|
+
# @author Lucas Billaudot <billau_l@modulotech.fr>
|
4
|
+
# @note when include this module provide access to the month of the
|
5
|
+
# FreePeriod
|
6
|
+
module Month
|
7
|
+
def month
|
8
|
+
ActivePeriod::Month.new(self.begin)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ActivePeriod
|
2
|
+
module BelongsTo
|
3
|
+
# @author Lucas Billaudot <billau_l@modulotech.fr>
|
4
|
+
# @note when include this module provide access to the quarter of the
|
5
|
+
# FreePeriod
|
6
|
+
module Quarter
|
7
|
+
def quarter
|
8
|
+
ActivePeriod::Quarter.new(self.begin)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ActivePeriod
|
2
|
+
module BelongsTo
|
3
|
+
# @author Lucas Billaudot <billau_l@modulotech.fr>
|
4
|
+
# @note when include this module provide access to the week of the
|
5
|
+
# FreePeriod
|
6
|
+
module Week
|
7
|
+
def week
|
8
|
+
ActivePeriod::Week.new(self.begin)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ActivePeriod
|
2
|
+
module BelongsTo
|
3
|
+
# @author Lucas Billaudot <billau_l@modulotech.fr>
|
4
|
+
# @note when include this module provide access to the year of the
|
5
|
+
# FreePeriod
|
6
|
+
module Year
|
7
|
+
def year
|
8
|
+
ActivePeriod::Year.new(self.begin)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ActivePeriod
|
2
|
+
module Collection
|
3
|
+
class FreePeriod
|
4
|
+
include ActivePeriod::Collection
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def enumerator
|
9
|
+
raise RangeError.new "cannot get the first element of beginless range" if period.beginless?
|
10
|
+
|
11
|
+
Enumerator.new do |yielder|
|
12
|
+
current = klass.new(period.begin)
|
13
|
+
while period.calculated_end.nil? || period.include?(current.begin) || period.include?(current.calculated_end)
|
14
|
+
yielder << current
|
15
|
+
current = current.next
|
16
|
+
end
|
17
|
+
# At the end (if there is one) the Collection will be return
|
18
|
+
self
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def reverse_enumerator
|
23
|
+
raise RangeError.new "cannot get the last element of endless range" if period.endless?
|
24
|
+
|
25
|
+
Enumerator.new do |yielder|
|
26
|
+
current = klass.new(period.end)
|
27
|
+
while period.begin.nil? || period.include?(current.begin) || period.include?(current.calculated_end)
|
28
|
+
yielder << current
|
29
|
+
current = current.prev
|
30
|
+
end
|
31
|
+
# At the end (if there is one) the Collection will be return
|
32
|
+
self
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module ActivePeriod
|
2
|
+
module Collection
|
3
|
+
class HolidayPeriod
|
4
|
+
include ActivePeriod::Collection
|
5
|
+
|
6
|
+
# @!attribute [r] options
|
7
|
+
# @return [Array] The array of options for Holidays.on
|
8
|
+
attr_reader :options
|
9
|
+
|
10
|
+
def initialize(klass, period, *args)
|
11
|
+
super(klass, period)
|
12
|
+
@options = args
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def enumerator
|
18
|
+
Enumerator.new do |yielder|
|
19
|
+
days = period.try(:days) || [period]
|
20
|
+
days.each do |day|
|
21
|
+
Holidays.on(day.begin.to_date, *options).each do |hash|
|
22
|
+
yielder << ActivePeriod::Holiday.new(**hash)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
# At the end (if there is one) the Collection will be return
|
26
|
+
self
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def reverse_enumerator
|
31
|
+
Enumerator.new do |yielder|
|
32
|
+
days = period.try(:days) || [period]
|
33
|
+
days.reverse_each do |day|
|
34
|
+
Holidays.on(day.begin.to_date, *options).each do |hash|
|
35
|
+
yielder << ActivePeriod::Holiday.new(**hash)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
# At the end (if there is one) the Collection will be return
|
39
|
+
self
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ActivePeriod
|
2
|
+
module Collection
|
3
|
+
class StandardPeriod
|
4
|
+
include ActivePeriod::Collection
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def enumerator
|
9
|
+
Enumerator.new do |yielder|
|
10
|
+
current = klass.new(period.begin)
|
11
|
+
while current.end <= period.end || period.include?(current)
|
12
|
+
yielder << current if period.include?(current)
|
13
|
+
current = current.next
|
14
|
+
end
|
15
|
+
# At the end (if there is one) the Collection will be return
|
16
|
+
self
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def reverse_enumerator
|
21
|
+
Enumerator.new do |yielder|
|
22
|
+
current = klass.new(period.end)
|
23
|
+
while current.begin <= period.begin || period.include?(current)
|
24
|
+
yielder << current if period.include?(current)
|
25
|
+
current = current.prev
|
26
|
+
end
|
27
|
+
# At the end (if there is one) the Collection will be return
|
28
|
+
self
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module ActivePeriod
|
2
|
+
module Collection
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
# @!attribute [r] klass
|
6
|
+
# @return [ActivePeriod::StandardPeriod] Any kind of ActivePeriod::StandardPeriod object
|
7
|
+
attr_reader :klass
|
8
|
+
|
9
|
+
# @!attribute [r] period
|
10
|
+
# @return [ActivePeriod::FreePeriod] Any kind of ActivePeriod::FreePeriod object
|
11
|
+
attr_reader :period
|
12
|
+
|
13
|
+
def self.new(klass, period)
|
14
|
+
if period.is_a? ActivePeriod::StandardPeriod
|
15
|
+
ActivePeriod::Collection::StandardPeriod.new(klass, period)
|
16
|
+
else
|
17
|
+
ActivePeriod::Collection::FreePeriod.new(klass, period)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(klass, period)
|
22
|
+
raise I18n.t(:param_period_must_be_a_period, scope: %i[active_period collection]) unless period.class.ancestors.include?(ActivePeriod::Period)
|
23
|
+
|
24
|
+
@klass = klass
|
25
|
+
@period = period
|
26
|
+
end
|
27
|
+
|
28
|
+
def second
|
29
|
+
find_nth(1)
|
30
|
+
end
|
31
|
+
|
32
|
+
def third
|
33
|
+
find_nth(2)
|
34
|
+
end
|
35
|
+
|
36
|
+
def fourth
|
37
|
+
find_nth(3)
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_nth(limit)
|
41
|
+
each.with_index do |element, index|
|
42
|
+
return element if index == limit
|
43
|
+
end
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def each(&block)
|
48
|
+
block_given? ? enumerator.each(&block) : enumerator
|
49
|
+
end
|
50
|
+
|
51
|
+
def reverse_each(&block)
|
52
|
+
block_given? ? reverse_enumerator.each(&block) : reverse_enumerator
|
53
|
+
end
|
54
|
+
|
55
|
+
def last
|
56
|
+
reverse_each.first
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module ActivePeriod
|
2
|
+
module Comparable
|
3
|
+
include ::Comparable
|
4
|
+
|
5
|
+
def include?(other)
|
6
|
+
case other
|
7
|
+
when DateTime, Time, ActiveSupport::TimeWithZone
|
8
|
+
include_time?(other)
|
9
|
+
when Date
|
10
|
+
include_period?(ActivePeriod::Day.new(other))
|
11
|
+
when ActivePeriod::Period
|
12
|
+
include_period?(other)
|
13
|
+
else
|
14
|
+
raise ArgumentError, I18n.t(:incomparable_error, scope: %i[active_period comparable])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def <=>(other)
|
19
|
+
if other.is_a?(ActiveSupport::Duration) || other.is_a?(Numeric)
|
20
|
+
to_i <=> other.to_i
|
21
|
+
elsif self.class != other.class
|
22
|
+
raise ArgumentError, I18n.t(:incomparable_error, scope: %i[active_period comparable])
|
23
|
+
else
|
24
|
+
(from <=> other)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def include_period?(other)
|
31
|
+
if self.class.in?([Month, Quarter, Year]) && other.is_a?(Week)
|
32
|
+
self.include_time?(other.include_date)
|
33
|
+
# elsif (other.class.in?([Month, Quarter, Year]) && self.is_a?(Week)
|
34
|
+
# other.include_time?(self.include_date)
|
35
|
+
else
|
36
|
+
self.include_time?(other.begin) && self.include_time?(other.calculated_end)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# this method could been shorten by chaining condition with ||
|
41
|
+
# yet it would make it less readable
|
42
|
+
def include_time?(other)
|
43
|
+
return true if self.boundless?
|
44
|
+
return true if self.endless? && self.begin <= other
|
45
|
+
return true if self.beginless? && self.calculated_end >= other
|
46
|
+
return true if self.begin.to_i <= other.to_i && other.to_i <= self.calculated_end.to_i
|
47
|
+
false
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative 'standard_period.rb'
|
2
|
+
require_relative 'belongs_to.rb'
|
3
|
+
require_relative 'belongs_to/week.rb'
|
4
|
+
require_relative 'belongs_to/month.rb'
|
5
|
+
require_relative 'belongs_to/quarter.rb'
|
6
|
+
require_relative 'belongs_to/year.rb'
|
7
|
+
|
8
|
+
class ActivePeriod::Day < ActivePeriod::StandardPeriod
|
9
|
+
include ActivePeriod::BelongsTo::Week
|
10
|
+
include ActivePeriod::BelongsTo::Month
|
11
|
+
include ActivePeriod::BelongsTo::Quarter
|
12
|
+
include ActivePeriod::BelongsTo::Year
|
13
|
+
|
14
|
+
def strftime(format)
|
15
|
+
from.strftime(format)
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s(format: '%d/%m/%Y')
|
19
|
+
strftime(format)
|
20
|
+
end
|
21
|
+
|
22
|
+
def i18n(&block)
|
23
|
+
return yield(from, to) if block.present?
|
24
|
+
|
25
|
+
I18n.t(:default_format,
|
26
|
+
scope: i18n_scope,
|
27
|
+
wday: I18n.l(from, format: '%A').capitalize,
|
28
|
+
day: from.day,
|
29
|
+
month: I18n.l(from, format: '%B').capitalize,
|
30
|
+
year: from.year)
|
31
|
+
end
|
32
|
+
|
33
|
+
def holiday?(...)
|
34
|
+
raise I18n.t(:gem_require, scope: %i[active_period holiday_period]) unless Object.const_defined?('Holidays')
|
35
|
+
holidays(...).first.present?
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|