biz 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f865f5197acf9cc026d7d77c15626a7d03a5e8c1
4
- data.tar.gz: 5d150d70060e27513f373c3b32e96dbd0e4f68fc
3
+ metadata.gz: 33c5eb58e4e8d9ee6f5b42c071c99e8c8cf8ed5b
4
+ data.tar.gz: 05fdc0175b78e6147aaffcaea14d274955744a2d
5
5
  SHA512:
6
- metadata.gz: a3eb38f1f622c23d1d1524ad053ae07bd789277d0a3b5b5a8bb42a2aef7453d8a976eed7878e97b8207861b0e58860d6a6e5950bc5507468e44ea62ba82c6e3e
7
- data.tar.gz: 6871ae32bf137626682626e80f8f4ae115b162b1d7ac104ed1b615e0a3b06baa7979a3ea79ab5628d25c79b54d42910ddd415e28b2ce6e0bfc28de1a44b1a7fc
6
+ metadata.gz: c6556e0069a7f41c67a0cdf9d80f7886b622b2cc043ba6c65a1bb6201d65f6a39facc1a8073b5c4d612f0d2d152a05b72bdbd8d2183dfc40d4cfc98ff34c4fc1
7
+ data.tar.gz: b42dfdb7a0fbba2386b1fa13e45ced2314517ea8c35c9ec66873b5804c6ee7dea7b61f9c8ab03bbbf856428127a71e25ce9c9b7cc9eeb4718878536559e079c8
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
- # biz
1
+ ![biz](http://d26a57ydsghvgx.cloudfront.net/www/public/assets/images/bizlogo.png)
2
+
2
3
  [![Gem Version](https://badge.fury.io/rb/biz.svg)](http://badge.fury.io/rb/biz)
3
4
  [![Build Status](https://travis-ci.org/zendesk/biz.svg?branch=master)](https://travis-ci.org/zendesk/biz)
4
5
  [![Code Climate](https://codeclimate.com/github/zendesk/biz/badges/gpa.svg)](https://codeclimate.com/github/zendesk/biz)
@@ -75,6 +76,9 @@ Biz.time(30, :minutes).before(Time.utc(2015, 1, 1, 11, 45))
75
76
  # Find the time an amount of business time *after* a specified starting time
76
77
  Biz.time(2, :hours).after(Time.utc(2015, 12, 25, 9, 30))
77
78
 
79
+ # Calculations can be performed in seconds, minutes, hours, or days
80
+ Biz.time(1, :day).after(Time.utc(2015, 1, 8, 10))
81
+
78
82
  # Find the amount of business time between two times
79
83
  Biz.within(Time.utc(2015, 3, 7), Time.utc(2015, 3, 14)).in_seconds
80
84
 
@@ -89,9 +93,7 @@ which you can use to do your own custom calculations or just get a better idea
89
93
  of what's happening under the hood:
90
94
 
91
95
  ```ruby
92
- Biz.periods
93
- .after(Time.utc(2015, 1, 10, 10))
94
- .timeline.forward
96
+ Biz.periods.after(Time.utc(2015, 1, 10, 10)).timeline
95
97
  .until(Time.utc(2015, 1, 17, 10)).to_a
96
98
 
97
99
  #=> [#<Biz::TimeSegment start_time=2015-01-10 18:00:00 UTC end_time=2015-01-10 22:00:00 UTC>,
@@ -102,8 +104,7 @@ Biz.periods
102
104
  # #<Biz::TimeSegment start_time=2015-01-15 21:00:00 UTC end_time=2015-01-16 01:00:00 UTC>]
103
105
 
104
106
  Biz.periods
105
- .before(Time.utc(2015, 5, 5, 12, 34, 57))
106
- .timeline.backward
107
+ .before(Time.utc(2015, 5, 5, 12, 34, 57)).timeline
107
108
  .for(Biz::Duration.minutes(3_598)).to_a
108
109
 
109
110
  #=> [#<Biz::TimeSegment start_time=2015-05-05 07:00:00 UTC end_time=2015-05-05 12:34:57 UTC>,
data/lib/biz.rb CHANGED
@@ -4,6 +4,7 @@ require 'forwardable'
4
4
  require 'set'
5
5
 
6
6
  require 'abstract_type'
7
+ require 'clavius'
7
8
  require 'equalizer'
8
9
  require 'memoizable'
9
10
  require 'tzinfo'
@@ -13,8 +14,8 @@ module Biz
13
14
 
14
15
  extend Forwardable
15
16
 
16
- def configure(&block)
17
- Thread.current[:biz_schedule] = Schedule.new(&block)
17
+ def configure(&config)
18
+ Thread.current[:biz_schedule] = Schedule.new(&config)
18
19
  end
19
20
 
20
21
  delegate %i[
@@ -22,6 +23,8 @@ module Biz
22
23
  holidays
23
24
  time_zone
24
25
  periods
26
+ date
27
+ dates
25
28
  time
26
29
  within
27
30
  in_hours?
@@ -37,13 +40,12 @@ module Biz
37
40
  end
38
41
  end
39
42
 
40
- require 'biz/version'
41
-
42
43
  require 'biz/date'
43
44
  require 'biz/time'
44
45
 
45
46
  require 'biz/calculation'
46
47
  require 'biz/configuration'
48
+ require 'biz/dates'
47
49
  require 'biz/day'
48
50
  require 'biz/day_of_week'
49
51
  require 'biz/day_time'
@@ -56,3 +58,4 @@ require 'biz/timeline'
56
58
  require 'biz/time_segment'
57
59
  require 'biz/week'
58
60
  require 'biz/week_time'
61
+ require 'biz/version'
@@ -2,19 +2,21 @@ module Biz
2
2
  module Calculation
3
3
  class Active
4
4
 
5
- attr_reader :schedule,
6
- :time
7
-
8
5
  def initialize(schedule, time)
9
6
  @schedule = schedule
10
7
  @time = time
11
8
  end
12
9
 
13
- def active?
10
+ def result
14
11
  schedule.intervals.any? { |interval| interval.contains?(time) } &&
15
12
  schedule.holidays.none? { |holiday| holiday.contains?(time) }
16
13
  end
17
14
 
15
+ protected
16
+
17
+ attr_reader :schedule,
18
+ :time
19
+
18
20
  end
19
21
  end
20
22
  end
@@ -2,10 +2,9 @@ module Biz
2
2
  module Calculation
3
3
  class DurationWithin < SimpleDelegator
4
4
 
5
- def initialize(periods, calculation_period)
5
+ def initialize(schedule, calculation_period)
6
6
  super(
7
- periods.after(calculation_period.start_time)
8
- .timeline.forward
7
+ schedule.periods.after(calculation_period.start_time).timeline
9
8
  .until(calculation_period.end_time)
10
9
  .map(&:duration)
11
10
  .reduce(Duration.new(0), :+)
@@ -2,30 +2,104 @@ module Biz
2
2
  module Calculation
3
3
  class ForDuration
4
4
 
5
- attr_reader :periods,
6
- :duration
5
+ UNITS = Set.new(%i[second seconds minute minutes hour hours day days])
7
6
 
8
- def initialize(periods, duration)
9
- unless duration.positive?
10
- fail ArgumentError, 'Duration adjustment must be positive.'
7
+ include AbstractType
8
+
9
+ def self.with_unit(schedule, scalar, unit)
10
+ unless UNITS.include?(unit)
11
+ fail ArgumentError, 'The unit is not supported.'
11
12
  end
12
13
 
13
- @periods = periods
14
- @duration = duration
14
+ send(unit, schedule, scalar)
15
+ end
16
+
17
+ def self.unit
18
+ name.split('::').last.downcase.to_sym
19
+ end
20
+
21
+ def initialize(schedule, scalar)
22
+ @schedule = schedule
23
+ @scalar = scalar
15
24
  end
16
25
 
17
- def before(time)
18
- periods.before(time)
19
- .timeline.backward
20
- .for(duration).to_a
21
- .last.start_time
26
+ abstract_method :before,
27
+ :after
28
+
29
+ protected
30
+
31
+ attr_reader :schedule,
32
+ :scalar
33
+
34
+ private
35
+
36
+ def unit
37
+ self.class.unit
22
38
  end
23
39
 
24
- def after(time)
25
- periods.after(time)
26
- .timeline.forward
27
- .for(duration).to_a
28
- .last.end_time
40
+ [
41
+ *%i[second seconds minute minutes hour hours].map { |unit|
42
+ const_set(unit.to_s.capitalize,
43
+ Class.new(self) do
44
+ def before(time)
45
+ timeline(:before, time).last.start_time
46
+ end
47
+
48
+ def after(time)
49
+ timeline(:after, time).last.end_time
50
+ end
51
+
52
+ private
53
+
54
+ def timeline(direction, time)
55
+ schedule.periods.send(direction, time).timeline
56
+ .for(duration).to_a
57
+ end
58
+
59
+ def duration
60
+ Duration.send(unit, scalar)
61
+ end
62
+ end
63
+ )
64
+ },
65
+ *%i[day days].map { |unit|
66
+ const_set(unit.to_s.capitalize,
67
+ Class.new(self) do
68
+ def before(time)
69
+ periods(:before, time).first.end_time
70
+ end
71
+
72
+ def after(time)
73
+ periods(:after, time).first.start_time
74
+ end
75
+
76
+ private
77
+
78
+ def periods(direction, time)
79
+ schedule.periods.send(direction, advanced_date(direction, time))
80
+ end
81
+
82
+ def advanced_date(direction, time)
83
+ schedule.in_zone.on_date(
84
+ schedule.dates.days(scalar).send(direction, local(time)),
85
+ day_time(time)
86
+ )
87
+ end
88
+
89
+ def day_time(time)
90
+ DayTime.from_time(local(time))
91
+ end
92
+
93
+ def local(time)
94
+ schedule.in_zone.local(time)
95
+ end
96
+ end
97
+ )
98
+ }
99
+ ].each do |unit_class|
100
+ define_singleton_method(unit_class.unit) { |schedule, scalar|
101
+ unit_class.new(schedule, scalar)
102
+ }
29
103
  end
30
104
 
31
105
  end
@@ -4,7 +4,7 @@ module Biz
4
4
  include Memoizable
5
5
 
6
6
  def initialize
7
- @raw = Raw.new.tap do |raw| yield raw end
7
+ @raw = Raw.new.tap do |raw| yield raw if block_given? end
8
8
  end
9
9
 
10
10
  def intervals
@@ -21,6 +21,10 @@ module Biz
21
21
  TZInfo::TimezoneProxy.new(raw.time_zone)
22
22
  end
23
23
 
24
+ def weekdays
25
+ raw.hours.keys.to_set
26
+ end
27
+
24
28
  protected
25
29
 
26
30
  attr_reader :raw
@@ -44,7 +48,8 @@ module Biz
44
48
  end
45
49
 
46
50
  memoize :intervals,
47
- :holidays
51
+ :holidays,
52
+ :weekdays
48
53
 
49
54
  Raw = Struct.new(:hours, :holidays, :time_zone) do
50
55
  module Default
@@ -3,11 +3,7 @@ module Biz
3
3
  module Date
4
4
 
5
5
  def business_day?
6
- Biz.periods
7
- .after(Biz::Time.new(Biz.time_zone).on_date(self, DayTime.midnight))
8
- .timeline.forward
9
- .until(Biz::Time.new(Biz.time_zone).on_date(self, DayTime.endnight))
10
- .any?
6
+ Biz.dates.active?(self)
11
7
  end
12
8
 
13
9
  end
@@ -2,7 +2,7 @@ module Biz
2
2
  module CoreExt
3
3
  module Fixnum
4
4
 
5
- %i[second seconds minute minutes hour hours].each do |unit|
5
+ Calculation::ForDuration::UNITS.each do |unit|
6
6
  define_method("business_#{unit}") { Biz.time(self, unit) }
7
7
  end
8
8
 
@@ -0,0 +1,14 @@
1
+ module Biz
2
+ class Dates < SimpleDelegator
3
+
4
+ def initialize(schedule)
5
+ super(
6
+ Clavius::Schedule.new do |c|
7
+ c.weekdays = schedule.weekdays
8
+ c.excluded = schedule.holidays.map(&:date)
9
+ end
10
+ )
11
+ end
12
+
13
+ end
14
+ end
@@ -8,34 +8,38 @@ module Biz
8
8
 
9
9
  extend Forwardable
10
10
 
11
- def self.from_hour(hour)
12
- new(hour * Time::MINUTES_IN_HOUR)
13
- end
14
-
15
- def self.from_timestamp(timestamp)
16
- timestamp.match(TIMESTAMP_PATTERN) { |match|
17
- new(match[:hour].to_i * Time::MINUTES_IN_HOUR + match[:minute].to_i)
18
- }
19
- end
11
+ class << self
20
12
 
21
- def self.midnight
22
- MIDNIGHT
23
- end
13
+ def from_time(time)
14
+ new(time.hour * Time::MINUTES_IN_HOUR + time.min)
15
+ end
24
16
 
25
- def self.noon
26
- NOON
27
- end
17
+ def from_hour(hour)
18
+ new(hour * Time::MINUTES_IN_HOUR)
19
+ end
28
20
 
29
- def self.endnight
30
- ENDNIGHT
31
- end
21
+ def from_timestamp(timestamp)
22
+ timestamp.match(TIMESTAMP_PATTERN) { |match|
23
+ new(match[:hour].to_i * Time::MINUTES_IN_HOUR + match[:minute].to_i)
24
+ }
25
+ end
32
26
 
33
- class << self
27
+ def midnight
28
+ MIDNIGHT
29
+ end
34
30
 
35
31
  alias_method :am, :midnight
36
32
 
33
+ def noon
34
+ NOON
35
+ end
36
+
37
37
  alias_method :pm, :noon
38
38
 
39
+ def endnight
40
+ ENDNIGHT
41
+ end
42
+
39
43
  end
40
44
 
41
45
  attr_reader :day_minute
@@ -1,8 +1,6 @@
1
1
  module Biz
2
2
  class Duration
3
3
 
4
- UNITS = Set.new(%i[second seconds minute minutes hour hours day days])
5
-
6
4
  include Equalizer.new(:seconds)
7
5
  include Comparable
8
6
 
@@ -10,14 +8,6 @@ module Biz
10
8
 
11
9
  class << self
12
10
 
13
- def with_unit(scalar, unit)
14
- unless UNITS.include?(unit)
15
- fail ArgumentError, 'The unit is not supported.'
16
- end
17
-
18
- public_send(unit, scalar)
19
- end
20
-
21
11
  def seconds(seconds)
22
12
  new(seconds)
23
13
  end
@@ -36,12 +26,6 @@ module Biz
36
26
 
37
27
  alias_method :hour, :hours
38
28
 
39
- def days(days)
40
- new(days * Time::DAY)
41
- end
42
-
43
- alias_method :day, :days
44
-
45
29
  end
46
30
 
47
31
  attr_reader :seconds
@@ -64,10 +48,6 @@ module Biz
64
48
  seconds / Time::HOUR
65
49
  end
66
50
 
67
- def in_days
68
- seconds / Time::DAY
69
- end
70
-
71
51
  def +(other)
72
52
  self.class.new(seconds + other.seconds)
73
53
  end
@@ -22,5 +22,7 @@ module Biz
22
22
  )
23
23
  end
24
24
 
25
+ alias_method :to_date, :date
26
+
25
27
  end
26
28
  end
@@ -1,8 +1,6 @@
1
1
  module Biz
2
2
  class Periods
3
3
 
4
- attr_reader :schedule
5
-
6
4
  def initialize(schedule)
7
5
  @schedule = schedule
8
6
  end
@@ -15,6 +13,10 @@ module Biz
15
13
  Before.new(schedule, origin)
16
14
  end
17
15
 
16
+ protected
17
+
18
+ attr_reader :schedule
19
+
18
20
  end
19
21
  end
20
22
 
@@ -4,9 +4,6 @@ module Biz
4
4
 
5
5
  extend Forwardable
6
6
 
7
- attr_reader :schedule,
8
- :origin
9
-
10
7
  def initialize(schedule, origin)
11
8
  @schedule = schedule
12
9
  @origin = origin
@@ -20,6 +17,11 @@ module Biz
20
17
  Timeline.new(self)
21
18
  end
22
19
 
20
+ protected
21
+
22
+ attr_reader :schedule,
23
+ :origin
24
+
23
25
  private
24
26
 
25
27
  def periods
@@ -2,6 +2,10 @@ module Biz
2
2
  class Periods
3
3
  class After < Abstract
4
4
 
5
+ def timeline
6
+ super.forward
7
+ end
8
+
5
9
  private
6
10
 
7
11
  def weeks
@@ -2,6 +2,10 @@ module Biz
2
2
  class Periods
3
3
  class Before < Abstract
4
4
 
5
+ def timeline
6
+ super.backward
7
+ end
8
+
5
9
  private
6
10
 
7
11
  def weeks
@@ -3,40 +3,45 @@ module Biz
3
3
 
4
4
  extend Forwardable
5
5
 
6
- def initialize(&block)
7
- @configuration = Configuration.new(&block)
6
+ def initialize(&config)
7
+ @configuration = Configuration.new(&config)
8
8
  end
9
9
 
10
10
  delegate %i[
11
11
  intervals
12
12
  holidays
13
13
  time_zone
14
+ weekdays
14
15
  ] => :configuration
15
16
 
16
17
  def periods
17
18
  Periods.new(self)
18
19
  end
19
20
 
21
+ def dates
22
+ Dates.new(self)
23
+ end
24
+
25
+ alias_method :date, :dates
26
+
20
27
  def time(scalar, unit)
21
- Calculation::ForDuration.new(
22
- periods,
23
- Duration.with_unit(scalar, unit)
24
- )
28
+ Calculation::ForDuration.with_unit(self, scalar, unit)
25
29
  end
26
30
 
27
31
  def within(origin, terminus)
28
- Calculation::DurationWithin.new(
29
- periods,
30
- TimeSegment.new(origin, terminus)
31
- )
32
+ Calculation::DurationWithin.new(self, TimeSegment.new(origin, terminus))
32
33
  end
33
34
 
34
35
  def in_hours?(time)
35
- Calculation::Active.new(self, time).active?
36
+ Calculation::Active.new(self, time).result
36
37
  end
37
38
 
38
39
  alias_method :business_hours?, :in_hours?
39
40
 
41
+ def in_zone
42
+ Time.new(time_zone)
43
+ end
44
+
40
45
  protected
41
46
 
42
47
  attr_reader :configuration
@@ -1,8 +1,6 @@
1
1
  module Biz
2
2
  class Timeline
3
3
 
4
- attr_reader :periods
5
-
6
4
  def initialize(periods)
7
5
  @periods = periods
8
6
  end
@@ -15,6 +13,10 @@ module Biz
15
13
  Backward.new(periods)
16
14
  end
17
15
 
16
+ protected
17
+
18
+ attr_reader :periods
19
+
18
20
  end
19
21
  end
20
22
 
@@ -2,8 +2,6 @@ module Biz
2
2
  class Timeline
3
3
  class Abstract
4
4
 
5
- attr_reader :periods
6
-
7
5
  def initialize(periods)
8
6
  @periods = periods.lazy
9
7
  end
@@ -36,6 +34,10 @@ module Biz
36
34
  end
37
35
  end
38
36
 
37
+ protected
38
+
39
+ attr_reader :periods
40
+
39
41
  end
40
42
  end
41
43
  end
@@ -2,6 +2,10 @@ module Biz
2
2
  class Timeline
3
3
  class Backward < Abstract
4
4
 
5
+ def backward
6
+ self
7
+ end
8
+
5
9
  private
6
10
 
7
11
  def occurred?(period, time)
@@ -2,6 +2,10 @@ module Biz
2
2
  class Timeline
3
3
  class Forward < Abstract
4
4
 
5
+ def forward
6
+ self
7
+ end
8
+
5
9
  private
6
10
 
7
11
  def occurred?(period, time)
@@ -1,5 +1,5 @@
1
1
  module Biz
2
2
 
3
- VERSION = '1.1.0'
3
+ VERSION = '1.2.0'
4
4
 
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: biz
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Craig Little
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-02-26 00:00:00.000000000 Z
12
+ date: 2015-03-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: abstract_type
@@ -25,6 +25,20 @@ dependencies:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: 0.0.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: clavius
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.0'
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: equalizer
30
44
  requirement: !ruby/object:Gem::Requirement
@@ -115,6 +129,7 @@ files:
115
129
  - lib/biz/core_ext/fixnum.rb
116
130
  - lib/biz/core_ext/time.rb
117
131
  - lib/biz/date.rb
132
+ - lib/biz/dates.rb
118
133
  - lib/biz/day.rb
119
134
  - lib/biz/day_of_week.rb
120
135
  - lib/biz/day_time.rb