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