ice_cube_chosko 0.1.0

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 (53) hide show
  1. checksums.yaml +7 -0
  2. data/config/locales/en.yml +178 -0
  3. data/config/locales/es.yml +176 -0
  4. data/config/locales/ja.yml +107 -0
  5. data/lib/ice_cube.rb +92 -0
  6. data/lib/ice_cube/builders/hash_builder.rb +27 -0
  7. data/lib/ice_cube/builders/ical_builder.rb +59 -0
  8. data/lib/ice_cube/builders/string_builder.rb +76 -0
  9. data/lib/ice_cube/deprecated.rb +36 -0
  10. data/lib/ice_cube/errors/count_exceeded.rb +7 -0
  11. data/lib/ice_cube/errors/until_exceeded.rb +7 -0
  12. data/lib/ice_cube/flexible_hash.rb +40 -0
  13. data/lib/ice_cube/i18n.rb +24 -0
  14. data/lib/ice_cube/null_i18n.rb +28 -0
  15. data/lib/ice_cube/occurrence.rb +101 -0
  16. data/lib/ice_cube/parsers/hash_parser.rb +91 -0
  17. data/lib/ice_cube/parsers/ical_parser.rb +91 -0
  18. data/lib/ice_cube/parsers/yaml_parser.rb +19 -0
  19. data/lib/ice_cube/rule.rb +123 -0
  20. data/lib/ice_cube/rules/daily_rule.rb +16 -0
  21. data/lib/ice_cube/rules/hourly_rule.rb +16 -0
  22. data/lib/ice_cube/rules/minutely_rule.rb +16 -0
  23. data/lib/ice_cube/rules/monthly_rule.rb +16 -0
  24. data/lib/ice_cube/rules/secondly_rule.rb +15 -0
  25. data/lib/ice_cube/rules/weekly_rule.rb +16 -0
  26. data/lib/ice_cube/rules/yearly_rule.rb +16 -0
  27. data/lib/ice_cube/schedule.rb +529 -0
  28. data/lib/ice_cube/single_occurrence_rule.rb +28 -0
  29. data/lib/ice_cube/time_util.rb +328 -0
  30. data/lib/ice_cube/validated_rule.rb +184 -0
  31. data/lib/ice_cube/validations/count.rb +61 -0
  32. data/lib/ice_cube/validations/daily_interval.rb +54 -0
  33. data/lib/ice_cube/validations/day.rb +71 -0
  34. data/lib/ice_cube/validations/day_of_month.rb +55 -0
  35. data/lib/ice_cube/validations/day_of_week.rb +77 -0
  36. data/lib/ice_cube/validations/day_of_year.rb +61 -0
  37. data/lib/ice_cube/validations/fixed_value.rb +95 -0
  38. data/lib/ice_cube/validations/hour_of_day.rb +55 -0
  39. data/lib/ice_cube/validations/hourly_interval.rb +54 -0
  40. data/lib/ice_cube/validations/lock.rb +95 -0
  41. data/lib/ice_cube/validations/minute_of_hour.rb +54 -0
  42. data/lib/ice_cube/validations/minutely_interval.rb +54 -0
  43. data/lib/ice_cube/validations/month_of_year.rb +54 -0
  44. data/lib/ice_cube/validations/monthly_interval.rb +53 -0
  45. data/lib/ice_cube/validations/schedule_lock.rb +46 -0
  46. data/lib/ice_cube/validations/second_of_minute.rb +54 -0
  47. data/lib/ice_cube/validations/secondly_interval.rb +51 -0
  48. data/lib/ice_cube/validations/until.rb +57 -0
  49. data/lib/ice_cube/validations/weekly_interval.rb +67 -0
  50. data/lib/ice_cube/validations/yearly_interval.rb +53 -0
  51. data/lib/ice_cube/version.rb +5 -0
  52. data/spec/spec_helper.rb +64 -0
  53. metadata +166 -0
data/lib/ice_cube.rb ADDED
@@ -0,0 +1,92 @@
1
+ require 'date'
2
+ require 'ice_cube/deprecated'
3
+ require 'ice_cube/i18n'
4
+
5
+ IceCube::I18n.detect_backend!
6
+
7
+ module IceCube
8
+
9
+ autoload :VERSION, 'ice_cube/version'
10
+
11
+ autoload :TimeUtil, 'ice_cube/time_util'
12
+ autoload :FlexibleHash, 'ice_cube/flexible_hash'
13
+
14
+ autoload :Rule, 'ice_cube/rule'
15
+ autoload :Schedule, 'ice_cube/schedule'
16
+ autoload :Occurrence, 'ice_cube/occurrence'
17
+
18
+ autoload :IcalBuilder, 'ice_cube/builders/ical_builder'
19
+ autoload :HashBuilder, 'ice_cube/builders/hash_builder'
20
+ autoload :StringBuilder, 'ice_cube/builders/string_builder'
21
+
22
+ autoload :HashParser, 'ice_cube/parsers/hash_parser'
23
+ autoload :YamlParser, 'ice_cube/parsers/yaml_parser'
24
+ autoload :IcalParser, 'ice_cube/parsers/ical_parser'
25
+
26
+ autoload :CountExceeded, 'ice_cube/errors/count_exceeded'
27
+ autoload :UntilExceeded, 'ice_cube/errors/until_exceeded'
28
+
29
+ autoload :ValidatedRule, 'ice_cube/validated_rule'
30
+ autoload :SingleOccurrenceRule, 'ice_cube/single_occurrence_rule'
31
+
32
+ autoload :SecondlyRule, 'ice_cube/rules/secondly_rule'
33
+ autoload :MinutelyRule, 'ice_cube/rules/minutely_rule'
34
+ autoload :HourlyRule, 'ice_cube/rules/hourly_rule'
35
+ autoload :DailyRule, 'ice_cube/rules/daily_rule'
36
+ autoload :WeeklyRule, 'ice_cube/rules/weekly_rule'
37
+ autoload :MonthlyRule, 'ice_cube/rules/monthly_rule'
38
+ autoload :YearlyRule, 'ice_cube/rules/yearly_rule'
39
+
40
+ module Validations
41
+ autoload :FixedValue, 'ice_cube/validations/fixed_value'
42
+ autoload :ScheduleLock, 'ice_cube/validations/schedule_lock'
43
+
44
+ autoload :Count, 'ice_cube/validations/count'
45
+ autoload :Until, 'ice_cube/validations/until'
46
+
47
+ autoload :SecondlyInterval, 'ice_cube/validations/secondly_interval'
48
+ autoload :MinutelyInterval, 'ice_cube/validations/minutely_interval'
49
+ autoload :DailyInterval, 'ice_cube/validations/daily_interval'
50
+ autoload :WeeklyInterval, 'ice_cube/validations/weekly_interval'
51
+ autoload :MonthlyInterval, 'ice_cube/validations/monthly_interval'
52
+ autoload :YearlyInterval, 'ice_cube/validations/yearly_interval'
53
+ autoload :HourlyInterval, 'ice_cube/validations/hourly_interval'
54
+
55
+ autoload :HourOfDay, 'ice_cube/validations/hour_of_day'
56
+ autoload :MonthOfYear, 'ice_cube/validations/month_of_year'
57
+ autoload :MinuteOfHour, 'ice_cube/validations/minute_of_hour'
58
+ autoload :SecondOfMinute, 'ice_cube/validations/second_of_minute'
59
+ autoload :DayOfMonth, 'ice_cube/validations/day_of_month'
60
+ autoload :DayOfWeek, 'ice_cube/validations/day_of_week'
61
+ autoload :Day, 'ice_cube/validations/day'
62
+ autoload :DayOfYear, 'ice_cube/validations/day_of_year'
63
+ end
64
+
65
+ # Define some useful constants
66
+ ONE_SECOND = 1
67
+ ONE_MINUTE = ONE_SECOND * 60
68
+ ONE_HOUR = ONE_MINUTE * 60
69
+ ONE_DAY = ONE_HOUR * 24
70
+ ONE_WEEK = ONE_DAY * 7
71
+
72
+ # Defines the format used by IceCube when printing out Schedule#to_s.
73
+ # Defaults to '%B %e, %Y'
74
+ def self.to_s_time_format
75
+ IceCube::I18n.t("ice_cube.date.formats.default")
76
+ end
77
+
78
+ # Sets the format used by IceCube when printing out Schedule#to_s.
79
+ def self.to_s_time_format=(format)
80
+ @to_s_time_format = format
81
+ end
82
+
83
+ # Retain backwards compatibility for schedules exported from older versions
84
+ # This represents the version number, 11 = 0.11, 1.0 will be 100
85
+ def self.compatibility
86
+ @compatibility ||= IceCube::VERSION.scan(/\d+/)[0..1].join.to_i
87
+ end
88
+
89
+ def self.compatibility=(version)
90
+ @compatibility = version
91
+ end
92
+ end
@@ -0,0 +1,27 @@
1
+ module IceCube
2
+
3
+ class HashBuilder
4
+
5
+ def initialize(rule = nil)
6
+ @hash = { :validations => {}, :rule_type => rule.class.name }
7
+ end
8
+
9
+ def validations
10
+ @hash[:validations]
11
+ end
12
+
13
+ def []=(key, value)
14
+ @hash[key] = value
15
+ end
16
+
17
+ def validations_array(type)
18
+ validations[type] ||= []
19
+ end
20
+
21
+ def to_hash
22
+ @hash
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,59 @@
1
+ module IceCube
2
+
3
+ class IcalBuilder
4
+
5
+ ICAL_DAYS = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA']
6
+
7
+ def initialize
8
+ @hash = {}
9
+ end
10
+
11
+ def self.fixnum_to_ical_day(num)
12
+ ICAL_DAYS[num]
13
+ end
14
+
15
+ def [](key)
16
+ @hash[key] ||= []
17
+ end
18
+
19
+ # Build for a single rule entry
20
+ def to_s
21
+ arr = []
22
+ if freq = @hash.delete('FREQ')
23
+ arr << "FREQ=#{freq.join(',')}"
24
+ end
25
+ arr.concat(@hash.map do |key, value|
26
+ if value.is_a?(Array)
27
+ "#{key}=#{value.join(',')}"
28
+ end
29
+ end.compact)
30
+ arr.join(';')
31
+ end
32
+
33
+ def self.ical_utc_format(time)
34
+ time = time.dup.utc
35
+ IceCube::I18n.l(time, format: '%Y%m%dT%H%M%SZ') # utc time
36
+ end
37
+
38
+ def self.ical_format(time, force_utc)
39
+ time = time.dup.utc if force_utc
40
+ if time.utc?
41
+ ":#{IceCube::I18n.l(time, format: '%Y%m%dT%H%M%SZ')}" # utc time
42
+ else
43
+ ";TZID=#{IceCube::I18n.l(time, format: '%Z:%Y%m%dT%H%M%S')}" # local time specified
44
+ end
45
+ end
46
+
47
+ def self.ical_duration(duration)
48
+ hours = duration / 3600; duration %= 3600
49
+ minutes = duration / 60; duration %= 60
50
+ repr = ''
51
+ repr << "#{hours}H" if hours > 0
52
+ repr << "#{minutes}M" if minutes > 0
53
+ repr << "#{duration}S" if duration > 0
54
+ "PT#{repr}"
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,76 @@
1
+ module IceCube
2
+
3
+ class StringBuilder
4
+
5
+ attr_writer :base
6
+
7
+ def initialize
8
+ @types = {}
9
+ end
10
+
11
+ def piece(type, prefix = nil, suffix = nil)
12
+ @types[type] ||= []
13
+ end
14
+
15
+ def to_s
16
+ string = @base || ''
17
+ @types.each do |type, segments|
18
+ if f = self.class.formatter(type)
19
+ current = f.call(segments)
20
+ else
21
+ next if segments.empty?
22
+ current = self.class.sentence(segments)
23
+ end
24
+ f = IceCube::I18n.t('ice_cube.string.format')[type] ? type : 'default'
25
+ string = IceCube::I18n.t("ice_cube.string.format.#{f}", rest: string, current: current)
26
+ end
27
+ string
28
+ end
29
+
30
+ def self.formatter(type)
31
+ @formatters[type]
32
+ end
33
+
34
+ def self.register_formatter(type, &formatter)
35
+ @formatters ||= {}
36
+ @formatters[type] = formatter
37
+ end
38
+
39
+ module Helpers
40
+
41
+ # influenced by ActiveSupport's to_sentence
42
+ def sentence(array)
43
+ case array.length
44
+ when 0 ; ''
45
+ when 1 ; array[0].to_s
46
+ when 2 ; "#{array[0]}#{IceCube::I18n.t('ice_cube.array.two_words_connector')}#{array[1]}"
47
+ else ; "#{array[0...-1].join(IceCube::I18n.t('ice_cube.array.words_connector'))}#{IceCube::I18n.t('ice_cube.array.last_word_connector')}#{array[-1]}"
48
+ end
49
+ end
50
+
51
+ def nice_number(number)
52
+ literal_ordinal(number) || ordinalize(number)
53
+ end
54
+
55
+ def ordinalize(number)
56
+ IceCube::I18n.t('ice_cube.integer.ordinal', number: number, ordinal: ordinal(number))
57
+ end
58
+
59
+ def literal_ordinal(number)
60
+ IceCube::I18n.t("ice_cube.integer.literal_ordinals")[number]
61
+ end
62
+
63
+ def ordinal(number)
64
+ ord = IceCube::I18n.t("ice_cube.integer.ordinals")[number] ||
65
+ IceCube::I18n.t("ice_cube.integer.ordinals")[number % 10] ||
66
+ IceCube::I18n.t('ice_cube.integer.ordinals')[:default]
67
+ number >= 0 ? ord : IceCube::I18n.t("ice_cube.integer.negative", ordinal: ord)
68
+ end
69
+
70
+ end
71
+
72
+ extend Helpers
73
+
74
+ end
75
+
76
+ end
@@ -0,0 +1,36 @@
1
+ module IceCube
2
+ module Deprecated
3
+
4
+ # Define a deprecated alias for a method
5
+ # @param [Symbol] name - name of method to define
6
+ # @param [Symbol] replacement - name of method to replace (alias)
7
+ def deprecated_alias(name, replacement)
8
+ # Create a wrapped version
9
+ define_method(name) do |*args, &block|
10
+ warn "IceCube: #{self.class}##{name} is deprecated, please use ##{replacement} at: #{ caller[0] }"
11
+ send replacement, *args, &block
12
+ end
13
+ end
14
+
15
+ # Deprecate a defined method
16
+ # @param [Symbol] name - name of deprecated method
17
+ # @param [Symbol] replacement - name of the desired replacement
18
+ def deprecated(name, replacement)
19
+ # Replace old method
20
+ old_name = :"#{name}_without_deprecation"
21
+ alias_method old_name, name
22
+ # And replace it with a wrapped version
23
+ define_method(name) do |*args, &block|
24
+ warn "IceCube: #{self.class}##{name} is deprecated, please use ##{replacement} at: #{ caller[0] }"
25
+ send old_name, *args, &block
26
+ end
27
+ end
28
+
29
+ def self.schedule_options(schedule, options)
30
+ if options[:start_date_override]
31
+ warn "IceCube: :start_date_override option is deprecated, please use a block {|s| s.start_time = override }. at: #{ caller[0] }"
32
+ schedule.start_time = options[:start_date_override]
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,7 @@
1
+ module IceCube
2
+
3
+ # An exception for when a count on a Rule is passed
4
+ class CountExceeded < StopIteration
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ module IceCube
2
+
3
+ # An exception for when an until date on a Rule is passed
4
+ class UntilExceeded < StopIteration
5
+ end
6
+
7
+ end
@@ -0,0 +1,40 @@
1
+ require 'delegate'
2
+
3
+ module IceCube
4
+
5
+ # Find keys by symbol or string without symbolizing user input
6
+ # Due to the serialization format of ice_cube, this limited implementation
7
+ # is entirely sufficient
8
+
9
+ class FlexibleHash < SimpleDelegator
10
+
11
+ def [](key)
12
+ key = _match_key(key)
13
+ super
14
+ end
15
+
16
+ def fetch(key)
17
+ key = _match_key(key)
18
+ super
19
+ end
20
+
21
+ def delete(key)
22
+ key = _match_key(key)
23
+ super
24
+ end
25
+
26
+ private
27
+
28
+ def _match_key(key)
29
+ return key if __getobj__.has_key? key
30
+ if Symbol == key.class
31
+ __getobj__.keys.detect { |k| return k if k == key.to_s }
32
+ elsif String == key.class
33
+ __getobj__.keys.detect { |k| return k if k.to_s == key }
34
+ end
35
+ key
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,24 @@
1
+ module IceCube
2
+ module I18n
3
+ def self.t(*args)
4
+ backend.t(*args)
5
+ end
6
+
7
+ def self.l(*args)
8
+ backend.l(*args)
9
+ end
10
+
11
+ def self.backend
12
+ @backend
13
+ end
14
+
15
+ def self.detect_backend!
16
+ require 'i18n'
17
+ ::I18n.load_path += Dir[File.expand_path('../../../config/locales/*{rb,yml}', __FILE__)]
18
+ @backend = ::I18n
19
+ rescue LoadError
20
+ require 'ice_cube/null_i18n'
21
+ @backend = NullI18n
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ require 'yaml'
2
+
3
+ module IceCube
4
+ module NullI18n
5
+ def self.t(key, options = {})
6
+ base = key.to_s.split('.').reduce(config) { |hash, current_key| hash[current_key] }
7
+
8
+ base = base[options[:count] == 1 ? "one" : "other"] if options[:count]
9
+
10
+ if base.is_a?(Hash)
11
+ return base.each_with_object({}) do |(key, value), hash|
12
+ hash[key.is_a?(String) ? key.to_sym : key] = value
13
+ end
14
+ end
15
+
16
+ options.reduce(base) { |result, (find, replace)| result.gsub("%{#{find}}", "#{replace}") }
17
+ end
18
+
19
+ def self.l(date_or_time, options = {})
20
+ return date_or_time.strftime(options[:format]) if options[:format]
21
+ date_or_time.strftime(t('ice_cube.date.formats.default'))
22
+ end
23
+
24
+ def self.config
25
+ @config ||= YAML.load(File.read(File.join(File.dirname(__FILE__), '..', '..', 'config', 'locales', 'en.yml')))['en']
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,101 @@
1
+ require 'forwardable'
2
+ require 'delegate'
3
+
4
+ module IceCube
5
+
6
+ # Wraps start_time and end_time in a single concept concerning the duration.
7
+ # This delegates to the enclosed start_time so it behaves like a normal Time
8
+ # in almost all situations, however:
9
+ #
10
+ # Without ActiveSupport, it's necessary to cast the occurrence using
11
+ # +#to_time+ before doing arithmetic, else Time will try to subtract it
12
+ # using +#to_i+ and return a new time instead.
13
+ #
14
+ # Time.now - Occurrence.new(start_time) # => 1970-01-01 01:00:00
15
+ # Time.now - Occurrence.new(start_time).to_time # => 3600
16
+ #
17
+ # When ActiveSupport::Time core extensions are loaded, it's possible to
18
+ # subtract an Occurrence object directly from a Time to get the difference:
19
+ #
20
+ # Time.now - Occurrence.new(start_time) # => 3600
21
+ #
22
+ class Occurrence < SimpleDelegator
23
+
24
+ # Report class name as 'Time' to thwart type checking.
25
+ def self.name
26
+ 'Time'
27
+ end
28
+
29
+ # Optimize for common methods to avoid method_missing
30
+ extend Forwardable
31
+ def_delegators :start_time, :to_i, :<=>, :==
32
+ def_delegators :to_range, :cover?, :include?, :each, :first, :last
33
+
34
+ attr_reader :start_time, :end_time
35
+
36
+ def initialize(start_time, end_time=nil)
37
+ @start_time = start_time
38
+ @end_time = end_time || start_time
39
+ __setobj__ @start_time
40
+ end
41
+
42
+ def is_a?(klass)
43
+ klass == ::Time || super
44
+ end
45
+ alias_method :kind_of?, :is_a?
46
+
47
+ def intersects? other
48
+ if other.is_a?(Occurrence) || other.is_a?(Range)
49
+ lower_bound_1 = first + 1
50
+ upper_bound_1 = last # exclude end
51
+ lower_bound_2 = other.first + 1
52
+ upper_bound_2 = other.last + 1
53
+ if (lower_bound_2 <=> upper_bound_2) > 0
54
+ false
55
+ elsif (lower_bound_1 <=> upper_bound_1) > 0
56
+ false
57
+ else
58
+ (upper_bound_1 <=> lower_bound_2) >= 0 and
59
+ (upper_bound_2 <=> lower_bound_1) >= 0
60
+ end
61
+ else
62
+ cover? other
63
+ end
64
+ end
65
+
66
+ def comparable_time
67
+ start_time
68
+ end
69
+
70
+ def duration
71
+ end_time - start_time
72
+ end
73
+
74
+ def to_range
75
+ start_time..end_time
76
+ end
77
+
78
+ def to_time
79
+ start_time
80
+ end
81
+
82
+ # Shows both the start and end time if there is a duration.
83
+ # Optional format argument (e.g. :long, :short) supports Rails
84
+ # time formats and is only used when ActiveSupport is available.
85
+ #
86
+ def to_s(format=nil)
87
+ if format && to_time.public_method(:to_s).arity != 0
88
+ t0, t1 = start_time.to_s(format), end_time.to_s(format)
89
+ else
90
+ t0, t1 = start_time.to_s, end_time.to_s
91
+ end
92
+ duration > 0 ? "#{t0} - #{t1}" : t0
93
+ end
94
+
95
+ def overnight?
96
+ offset = start_time + 3600 * 24
97
+ midnight = Time.new(offset.year, offset.month, offset.day)
98
+ midnight < end_time
99
+ end
100
+ end
101
+ end