turba_chronos 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +6 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +68 -0
  8. data/Rakefile +11 -0
  9. data/lib/turba_chronos.rb +31 -0
  10. data/lib/turba_chronos/core_ext/range.rb +5 -0
  11. data/lib/turba_chronos/day.rb +71 -0
  12. data/lib/turba_chronos/period.rb +44 -0
  13. data/lib/turba_chronos/period_converter.rb +69 -0
  14. data/lib/turba_chronos/periods.rb +43 -0
  15. data/lib/turba_chronos/rule.rb +52 -0
  16. data/lib/turba_chronos/stored_periods.rb +68 -0
  17. data/lib/turba_chronos/timeline.rb +139 -0
  18. data/lib/turba_chronos/version.rb +3 -0
  19. data/spec/fixtures/base_fixture.rb +31 -0
  20. data/spec/fixtures/disney_fixture.rb +19 -0
  21. data/spec/fixtures/empty_periods_fixture.rb +13 -0
  22. data/spec/fixtures/future_reopen_fixture.rb +19 -0
  23. data/spec/fixtures/lcl_fixture.rb +18 -0
  24. data/spec/fixtures/november_bug_fixture.rb +13 -0
  25. data/spec/fixtures/opened_fixture.rb +12 -0
  26. data/spec/lib/alt_timeline_spec.rb +48 -0
  27. data/spec/lib/disney_2_spec.rb +21 -0
  28. data/spec/lib/disney_3_spec.rb +21 -0
  29. data/spec/lib/disney_spec.rb +21 -0
  30. data/spec/lib/empty_periods_spec.rb +21 -0
  31. data/spec/lib/future_reopen_spec.rb +21 -0
  32. data/spec/lib/lcl_spec.rb +28 -0
  33. data/spec/lib/november_bug_spec.rb +20 -0
  34. data/spec/lib/opened_spec.rb +21 -0
  35. data/spec/lib/timeline_spec.rb +70 -0
  36. data/spec/spec_helper.rb +8 -0
  37. data/test/fixtures/base_fixture.rb +31 -0
  38. data/test/test_helper.rb +6 -0
  39. data/test/test_periods.rb +16 -0
  40. data/test/test_periods_converter.rb +62 -0
  41. data/test/test_rule.rb +33 -0
  42. data/test/test_timeline.rb +51 -0
  43. data/turba_chronos.gemspec +30 -0
  44. metadata +236 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 045e46b1f23ed3f38ce1781e8f8e97d2598e0f1a
4
+ data.tar.gz: 0b904b08a2cc996436f7af39acb24cf6dc5783da
5
+ SHA512:
6
+ metadata.gz: 51a32b800f6074844aa3fec0d365d3d9ea9bedad2365e89bc239bdf6603bc7eab5e74fa06f375f9a009de6cf9dcac45a19987ac31eb7d0e308530270a7e5d89f
7
+ data.tar.gz: ebc555d435e2082bdf28985f8431c04cb0d17ae07c60a19867ad493c7155995d1789590976ce77ef73202fab2e1e7c2d0c992a28d1bf5762fb8eaf5a8db78b37
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.3.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in turba_chronos.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,6 @@
1
+ guard :rspec, cmd: 'bundle exec rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/.*\.rb$}) { "spec" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
6
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Joffrey JAFFEUX
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # TurbaChronos
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'turba_chronos'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install turba_chronos
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+ periods_for_days
43
+
44
+ period_for_day
45
+
46
+
47
+ period_1
48
+ period_2
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # require "bundler/gem_tasks"
2
+
3
+
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new do |task|
7
+ task.libs << %w(test lib)
8
+ task.pattern = 'test/test_*.rb'
9
+ end
10
+
11
+ task :default => :test
@@ -0,0 +1,31 @@
1
+ require 'ostruct'
2
+ require 'active_support/all'
3
+ require 'turba_chronos/core_ext/range'
4
+ require 'turba_chronos/version'
5
+ require 'turba_chronos/timeline'
6
+ require 'turba_chronos/day'
7
+ require 'turba_chronos/periods'
8
+ require 'turba_chronos/period'
9
+ require 'turba_chronos/period_converter'
10
+ require 'turba_chronos/stored_periods'
11
+ require 'turba_chronos/rule'
12
+
13
+ module TurbaChronos
14
+ FULL_DAY_TIMERANGE = ['00:00', '23:59']
15
+
16
+ def self.load(periods, from, to, timezone = 'Europe/Paris')
17
+ Time.zone = timezone
18
+ TurbaChronos.current_numeric_time = Time.zone.now.strftime("%k%M").to_i
19
+
20
+ periods = periods.map!(&:with_indifferent_access)
21
+ @timeline = Timeline.new(periods, Time.parse(from).utc, Time.parse(to).utc)
22
+ end
23
+
24
+ def self.current_numeric_time
25
+ @current_numeric_time
26
+ end
27
+
28
+ def self.current_numeric_time=(current_numeric_time)
29
+ @current_numeric_time = current_numeric_time
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ class Range
2
+ def <=>(other)
3
+ [min.to_i, max.to_i] <=> [other.min.to_i, other.max.to_i]
4
+ end
5
+ end
@@ -0,0 +1,71 @@
1
+ module TurbaChronos
2
+ class Day
3
+ attr_reader :beginning_of_day
4
+ def initialize(beginning_of_day, periods, timeranges_for_days)
5
+ @beginning_of_day = beginning_of_day.to_time
6
+ @rules = get_rules_for_day(periods)
7
+ @timeranges_for_days = timeranges_for_days
8
+ end
9
+
10
+ def date
11
+ @date ||= @beginning_of_day.to_datetime.iso8601
12
+ end
13
+
14
+ def opened?
15
+ current_timeframe.level != 6
16
+ end
17
+
18
+ def timeframes
19
+ @timeframes ||= get_timeframes(:business_timeranges)
20
+ end
21
+
22
+ def current_timeframe
23
+ get_timeframes(:timeranges).each do |timeframe|
24
+ return timeframe if timeframe.timerange === TurbaChronos.current_numeric_time
25
+ end
26
+
27
+ range = (Time.now.utc.beginning_of_hour.strftime("%k%M").to_i..Time.now.utc.end_of_hour.strftime("%k%M").to_i)
28
+ return TempTimerange.new(range, 6)
29
+ end
30
+
31
+ def current_timeframe_index
32
+ timeframes.map {|x| x.timerange}.index(current_timeframe.timerange)
33
+ end
34
+
35
+ protected
36
+
37
+ def get_rules_for_day(periods)
38
+ periods.each do |period|
39
+ next if ([@beginning_of_day.yday] & period.yday_array).blank?
40
+ return period.rules
41
+ end
42
+ raise "Didn’t find rules for this day : #{@beginning_of_day}"
43
+ end
44
+
45
+ TempTimerange = Struct.new(:timerange, :level)do
46
+ def start_time
47
+ string_time(timerange.min)
48
+ end
49
+
50
+ def end_time
51
+ string_time(timerange.max)
52
+ end
53
+
54
+ def string_time(numeric_time)
55
+ numeric_time.to_s.rjust(4, '0').insert(2, ':')
56
+ end
57
+ end
58
+ def get_timeframes(type)
59
+ timeframes = []
60
+ @timeranges_for_days[type].each_with_index do |timerange, index|
61
+ @rules.each do |rule|
62
+ if rule.start_time.integer <= timerange.min && rule.end_time.integer >= timerange.max
63
+ level = rule.wdays[@beginning_of_day.to_datetime.cwday - 1]
64
+ timeframes.push(TempTimerange.new(timerange, level))
65
+ end
66
+ end
67
+ end
68
+ timeframes
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,44 @@
1
+ module TurbaChronos
2
+ class Period
3
+ attr_accessor :rules
4
+ attr_accessor :start_date
5
+ attr_accessor :end_date
6
+
7
+ def initialize(from, to, rules)
8
+ @rules = complete_rules(rules)
9
+ @start_date = from
10
+ @end_date = to.month < from.month ? to.next_year : to
11
+ end
12
+
13
+ def distance
14
+ @distance ||= end_yday - start_yday
15
+ end
16
+
17
+ def timerange
18
+ @timerange ||= @rules.map {|rule| rule.range.string_time_array}
19
+ end
20
+
21
+ def start_yday
22
+ @start_yday ||= @start_date.yday
23
+ end
24
+
25
+ def end_yday
26
+ @end_yday ||= @end_date.yday
27
+ end
28
+
29
+ def yday_array
30
+ @yday_array ||= (start_yday..end_yday).to_a
31
+ end
32
+
33
+ private
34
+
35
+ def complete_rules(rules)
36
+ completed_rules = rules.map {|rule| Rule.new(rule)}
37
+ ranges = completed_rules.map(&:range)
38
+ uniq_times = ranges.each_with_object([]) { |r,a| a << r.first << r.last }.uniq.sort
39
+ completed_rules.prepend(Rule.new([FULL_DAY_TIMERANGE.first, completed_rules.first.start_time.text, *[6]*7], true)) if uniq_times[0] != 0
40
+ completed_rules.push(Rule.new([completed_rules.last.end_time.text, FULL_DAY_TIMERANGE.last, *[6]*7], true)) if uniq_times[-1] != 2359
41
+ completed_rules
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,69 @@
1
+ module TurbaChronos
2
+ class PeriodConverter
3
+ Bounds = Struct.new(:from, :to)
4
+
5
+ def initialize(dates)
6
+ raise ArgumentError if dates.empty?
7
+ @dates = dates
8
+ end
9
+
10
+ def parse
11
+ return days_parser(*@dates.split(':')) if @dates.include?(':')
12
+ return day_parser(*@dates.split('/')) if @dates.include?('/')
13
+ return month_parser(@dates) if month_names.include?(@dates)
14
+ return months_parser(@dates.split('-')) if @dates.include?('-')
15
+ return stored_period_parser(@dates)
16
+ raise ArgumentError
17
+ end
18
+
19
+ private
20
+
21
+ def month_names
22
+ @month_names ||= Date::MONTHNAMES.compact.map(&:downcase)
23
+ end
24
+
25
+ def stored_period_parser(stored_period_name)
26
+ stored_period_dates = stored_periods.fetch(stored_period_name.to_sym)
27
+ dates = []
28
+ stored_period_dates.each do |date|
29
+ dates.push(day_parser(*date[0].split('/'))) if date.count == 1
30
+ dates.push(days_parser(*date)) if date.count == 2
31
+ end
32
+ dates.flatten
33
+ end
34
+
35
+ def stored_periods
36
+ @stored_periods ||= TurbaChronos::StoredPeriods.all
37
+ end
38
+
39
+ def days_parser(first_day, second_day)
40
+ first_day = first_day.split('/')
41
+ second_day = second_day.split('/')
42
+ year = Time.now.utc.year
43
+ first_parsed_day = Time.local(year, first_day[1], first_day[0]).utc
44
+ second_parsed_day = Time.local(year, second_day[1], second_day[0]).utc
45
+ if second_parsed_day < first_parsed_day
46
+ second_parsed_day = second_parsed_day.next_year
47
+ end
48
+
49
+ [Bounds.new(first_parsed_day, second_parsed_day)]
50
+ end
51
+
52
+ def day_parser(day, month)
53
+ parsed_day = Time.local(Time.now.utc.year, month, day).utc
54
+ [Bounds.new(parsed_day, parsed_day)]
55
+ end
56
+
57
+ def months_parser(months)
58
+ from = Time.parse(months[0]).utc
59
+ to = Time.parse(months[1]).utc.end_of_month
60
+ raise ArgumentError if from > to
61
+ [Bounds.new(from, to)]
62
+ end
63
+
64
+ def month_parser(month)
65
+ parsed_month = Time.parse(month).utc
66
+ [Bounds.new(parsed_month, parsed_month.end_of_month)]
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,43 @@
1
+ module TurbaChronos
2
+ class Periods
3
+
4
+ def initialize(periods)
5
+ @raw_periods = raw_periods_from_dates(periods)
6
+ end
7
+
8
+ def raw_periods
9
+ @raw_periods
10
+ end
11
+
12
+ def complete
13
+ start_year = Time.now.utc.beginning_of_year
14
+ end_year = Time.now.utc.end_of_year
15
+
16
+ @raw_periods.map! do |period|
17
+ if period[1].year > period[0].year
18
+ end_date = Time.local(Time.now.utc.year, period[1].month, period[1].day).utc
19
+ [
20
+ Period.new(start_year, end_date, period[2]),
21
+ Period.new(period[0], end_year, period[2])
22
+ ]
23
+ else
24
+ [Period.new(period[0], period[1], period[2])]
25
+ end
26
+ end.flatten!.sort_by!(&:distance)
27
+ end
28
+
29
+ private
30
+
31
+ def raw_periods_from_dates(periods)
32
+ raw_periods = []
33
+ periods.each do |period|
34
+ period[:dates].each do |dates|
35
+ PeriodConverter.new(dates).parse.each do |date|
36
+ raw_periods.push([date.from, date.to, period[:rules]])
37
+ end
38
+ end
39
+ end
40
+ raw_periods
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,52 @@
1
+ module TurbaChronos
2
+ class Rule
3
+ attr_accessor :start_time
4
+ attr_accessor :end_time
5
+ attr_accessor :range
6
+ attr_accessor :business_hours
7
+
8
+ def initialize(rule, business_hours=false)
9
+ @business_hours = business_hours
10
+ if rule.is_a?(Array)
11
+ @wdays = rule[2..-2]
12
+ self.start_time = rule[0]
13
+ self.end_time = rule[1]
14
+ else
15
+ @wdays = [rule[:mo], rule[:tu], rule[:we], rule[:th], rule[:fr], rule[:sa], rule[:su]]
16
+ self.start_time = rule[:s]
17
+ self.end_time = rule[:e]
18
+ end
19
+ end
20
+
21
+ def min
22
+ range.min
23
+ end
24
+
25
+ def max
26
+ range.max
27
+ end
28
+
29
+ def start_time=(start_time)
30
+ @start_time = text_time_to_hash(start_time)
31
+ end
32
+
33
+ def end_time=(end_time)
34
+ @end_time = text_time_to_hash(end_time)
35
+ end
36
+
37
+ def range
38
+ (start_time.integer..end_time.integer)
39
+ end
40
+
41
+ def wdays
42
+ @cachewdays ||= @wdays.map(&:to_i)
43
+ end
44
+
45
+ private
46
+
47
+ TextTime = Struct.new(:text, :integer)
48
+ def text_time_to_hash(text_time)
49
+ TextTime.new(text_time, text_time.gsub(':', '').to_i)
50
+ end
51
+ end
52
+ end