ice_cube_chosko 0.1.0

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