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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/Gemfile +5 -0
  4. data/Gemfile.lock +36 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +342 -0
  7. data/Rakefile +10 -0
  8. data/active_period.gemspec +44 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +8 -0
  11. data/config/locales/en.yml +36 -0
  12. data/config/locales/fr.yml +59 -0
  13. data/lib/.DS_Store +0 -0
  14. data/lib/active_period/belongs_to/month.rb +12 -0
  15. data/lib/active_period/belongs_to/quarter.rb +12 -0
  16. data/lib/active_period/belongs_to/week.rb +12 -0
  17. data/lib/active_period/belongs_to/year.rb +12 -0
  18. data/lib/active_period/belongs_to.rb +7 -0
  19. data/lib/active_period/collection/free_period.rb +37 -0
  20. data/lib/active_period/collection/holiday_period.rb +44 -0
  21. data/lib/active_period/collection/standard_period.rb +33 -0
  22. data/lib/active_period/collection.rb +60 -0
  23. data/lib/active_period/comparable.rb +51 -0
  24. data/lib/active_period/day.rb +38 -0
  25. data/lib/active_period/free_period.rb +86 -0
  26. data/lib/active_period/has_many/days.rb +14 -0
  27. data/lib/active_period/has_many/holidays.rb +15 -0
  28. data/lib/active_period/has_many/months.rb +14 -0
  29. data/lib/active_period/has_many/quarters.rb +14 -0
  30. data/lib/active_period/has_many/weeks.rb +15 -0
  31. data/lib/active_period/has_many/years.rb +15 -0
  32. data/lib/active_period/has_many.rb +7 -0
  33. data/lib/active_period/holiday.rb +80 -0
  34. data/lib/active_period/month.rb +37 -0
  35. data/lib/active_period/period.rb +158 -0
  36. data/lib/active_period/quarter.rb +42 -0
  37. data/lib/active_period/standard_period.rb +51 -0
  38. data/lib/active_period/version.rb +5 -0
  39. data/lib/active_period/week.rb +41 -0
  40. data/lib/active_period/year.rb +34 -0
  41. data/lib/active_period.rb +28 -0
  42. data/lib/numeric.rb +6 -0
  43. data/lib/period.rb +63 -0
  44. data/lib/range.rb +8 -0
  45. 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,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -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,7 @@
1
+ # @author Lucas Billaudot <billau_l@modulotech.fr>
2
+ # @note This module define all period of time, who include the current period
3
+ module ActivePeriod
4
+ module BelongsTo
5
+
6
+ end
7
+ 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