ice_cube_cron 0.0.2 → 0.0.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f7aa5090dd5b494487fe66f7f731bed0b90b59ce
4
- data.tar.gz: 7822df6b8b1d227cb23ad9e64f7db313084476b6
3
+ metadata.gz: 75073a57e6693975beb5f23a78daf4aa3e82fbf8
4
+ data.tar.gz: 8a6a29270f4b92bd20b4642307d3cf8e79d6c80b
5
5
  SHA512:
6
- metadata.gz: 92b1a9335aaa0dbc98bcb5ef7966d17da696567158c7afd6e1308b19d2118564928cd233e89e19017a4da8bba3003c1761057156a0f8d9d43d2e1a9031a08281
7
- data.tar.gz: f884daabb064dc2ab03802bef9cf9361cd05ac1b2e85d91e190b1d35d28fb67b19ac29940cb9f2e5561b44c0da6ce386ce9ebc5569ce45314a2657e89ce6c7c1
6
+ metadata.gz: 06a51ee2d28f4725be2058c6009ea2eedddd8a35634690492e001132078aef5e404651530a2286f7a9c606b37e14c60169d7e3ffadcf15703f12867ca931c1cd
7
+ data.tar.gz: aa95de45173bfb490d5e7fb29058126af81a2b9a2d8f4af5fbee8381435310117b5d5b3ce0f5338f0d633aa1f0960fbbb864c455ba71850e0a74ed18c4c5df63
data/.rdoc_options ADDED
@@ -0,0 +1,25 @@
1
+ --- !ruby/object:RDoc::Options
2
+ encoding: UTF-8
3
+ static_path: []
4
+ rdoc_include:
5
+ - ./README.md
6
+ - "/Users/matthewnichols/Code/ice_cube_cron/."
7
+ charset: UTF-8
8
+ exclude:
9
+ - ./Gemfile*
10
+ - ./Guardfile
11
+ hyperlink_all: false
12
+ line_numbers: false
13
+ locale:
14
+ locale_dir: locale
15
+ locale_name:
16
+ main_page: README.md
17
+ markup: markdown
18
+ output_decoration: true
19
+ page_dir:
20
+ show_hash: false
21
+ tab_width: 8
22
+ template_stylesheets: []
23
+ title:
24
+ visibility: :protected
25
+ webcvs:
data/.rubocop.yml CHANGED
@@ -8,6 +8,9 @@ Style/AccessModifierIndentation:
8
8
  EnforcedStyle: outdent
9
9
 
10
10
  Style/Documentation:
11
+ Enabled: true
12
+
13
+ Style/SignalException:
11
14
  Enabled: false
12
15
 
13
16
  Metrics/LineLength:
@@ -15,3 +18,4 @@ Metrics/LineLength:
15
18
 
16
19
  Metrics/MethodLength:
17
20
  Max: 15
21
+
data/Gemfile CHANGED
@@ -1,11 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
- # DEVUP--
5
- gem 'guard-zeus', :group => :development
6
- gem 'guard-rspec', :group => :development
7
- gem 'guard-rubocop', :group => :development
8
- gem 'rb-fsevent', :group => :development
9
- gem 'terminal-notifier-guard', :group => :development
10
- gem 'wirble', :group => :development
11
- # --DEVUP
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ice_cube_cron (0.0.1)
4
+ ice_cube_cron (0.0.2)
5
5
  activesupport (>= 3.0.0)
6
6
  ice_cube (= 0.13.0)
7
7
 
@@ -19,42 +19,11 @@ GEM
19
19
  parser (~> 2.2)
20
20
  coderay (1.1.0)
21
21
  diff-lcs (1.2.5)
22
- ffi (1.9.10)
23
- formatador (0.2.5)
24
- guard (2.13.0)
25
- formatador (>= 0.2.4)
26
- listen (>= 2.7, <= 4.0)
27
- lumberjack (~> 1.0)
28
- nenv (~> 0.1)
29
- notiffany (~> 0.0)
30
- pry (>= 0.9.12)
31
- shellany (~> 0.0)
32
- thor (>= 0.18.1)
33
- guard-compat (1.2.1)
34
- guard-rspec (4.6.4)
35
- guard (~> 2.1)
36
- guard-compat (~> 1.1)
37
- rspec (>= 2.99.0, < 4.0)
38
- guard-rubocop (1.2.0)
39
- guard (~> 2.0)
40
- rubocop (~> 0.20)
41
- guard-zeus (2.0.1)
42
- guard (~> 2.0)
43
- guard-compat (~> 1.1)
44
- zeus (~> 0)
45
22
  i18n (0.7.0)
46
23
  ice_cube (0.13.0)
47
24
  json (1.8.3)
48
- listen (3.0.3)
49
- rb-fsevent (>= 0.9.3)
50
- rb-inotify (>= 0.9)
51
- lumberjack (1.0.9)
52
25
  method_source (0.8.2)
53
26
  minitest (5.8.1)
54
- nenv (0.2.0)
55
- notiffany (0.0.8)
56
- nenv (~> 0.1)
57
- shellany (~> 0.0)
58
27
  parser (2.2.3.0)
59
28
  ast (>= 1.1, < 3.0)
60
29
  powerpack (0.1.1)
@@ -64,9 +33,6 @@ GEM
64
33
  slop (~> 3.4)
65
34
  rainbow (2.0.0)
66
35
  rake (10.4.2)
67
- rb-fsevent (0.9.6)
68
- rb-inotify (0.9.5)
69
- ffi (>= 0.5.0)
70
36
  rspec (3.3.0)
71
37
  rspec-core (~> 3.3.0)
72
38
  rspec-expectations (~> 3.3.0)
@@ -87,32 +53,20 @@ GEM
87
53
  rainbow (>= 1.99.1, < 3.0)
88
54
  ruby-progressbar (~> 1.4)
89
55
  ruby-progressbar (1.7.5)
90
- shellany (0.0.1)
91
56
  slop (3.6.0)
92
- terminal-notifier-guard (1.6.4)
93
- thor (0.19.1)
94
57
  thread_safe (0.3.5)
95
58
  tzinfo (1.2.2)
96
59
  thread_safe (~> 0.1)
97
- wirble (0.1.3)
98
- zeus (0.15.4)
99
- method_source (>= 0.6.7)
100
60
 
101
61
  PLATFORMS
102
62
  ruby
103
63
 
104
64
  DEPENDENCIES
105
- guard-rspec
106
- guard-rubocop
107
- guard-zeus
108
65
  ice_cube_cron!
109
66
  pry
110
67
  rake
111
- rb-fsevent
112
68
  rspec
113
69
  rubocop
114
- terminal-notifier-guard
115
- wirble
116
70
 
117
71
  BUNDLED WITH
118
72
  1.10.6
data/README.md CHANGED
@@ -1,10 +1,12 @@
1
+ [![Gem Version](https://badge.fury.io/rb/ice_cube_cron.svg)](https://badge.fury.io/rb/ice_cube_cron)
2
+
1
3
  # ice_cube_cron
2
4
 
3
5
  Adds new methods to ice_cube to create schedules using standard cron expressions.
4
6
 
5
7
  ## description
6
8
 
7
- **ice_cube** is a sold library for projecting and querying recurrence rules. **Cron** is _the_ standard for expressing recurrence rules. This gem will allow you to generate ice_cube schedules using standard cron expressions [explained here](https://en.wikipedia.org/wiki/Cron). This includes range expressions, series expressions, "last" day of month, Nth weekday of month, etc.
9
+ **[ice_cube](https://github.com/seejohnrun/ice_cube)** is a sold library for projecting and querying recurrence rules. **[Cron](https://en.wikipedia.org/wiki/Cron)** is _the_ standard for expressing recurrence rules. This gem will allow you to generate ice_cube schedules using standard cron expressions [explained here](https://en.wikipedia.org/wiki/Cron). This includes range expressions, series expressions, "last" day of month, Nth weekday of month, etc.
8
10
 
9
11
  PLEASE NOTE:
10
12
  This gem is a work-in-progress. Many features have yet to be implemented.
@@ -19,7 +21,6 @@ or add to Gemfile
19
21
 
20
22
  ## example
21
23
 
22
- Cron expression can be specified using a hash or a string in the format:
23
24
  ```
24
25
  # * * * * * * command to execute
25
26
  # │ │ │ │ │ │
@@ -31,9 +32,12 @@ Cron expression can be specified using a hash or a string in the format:
31
32
  # │ └──────────────────── hour (0 - 23)
32
33
  # └───────────────────────── min (0 - 59)
33
34
  ```
35
+
34
36
  *[chart source](https://en.wikipedia.org/wiki/Cron)*
35
37
 
36
38
 
39
+ Cron expression can be specified using a hash or a string in the format:
40
+
37
41
  ```ruby
38
42
  require 'ice_cube_cron'
39
43
 
@@ -1,4 +1,4 @@
1
- module IceCube
1
+ module IceCube # :nodoc: all
2
2
  class Schedule
3
3
  extend ::IceCubeCron::Extension
4
4
  end
@@ -1,92 +1,106 @@
1
- module IceCubeCron
1
+ module IceCubeCron # :nodoc:
2
+ ##
3
+ # Parses the incoming expression and splits it into meaningful parts.
4
+ #
2
5
  class ExpressionParser
3
- attr_accessor :expression_hash
4
-
5
- def initialize(exp)
6
- self.expression_hash = {
7
- :repeat_interval => 1,
8
- :repeat_year => nil,
9
- :repeat_month => nil,
10
- :repeat_day => nil,
11
- :repeat_weekday => nil
12
- }
13
-
14
- if exp.is_a?(::Hash)
15
- exp.each do |name, val|
16
- send("#{name}=", val)
17
- end
18
- elsif exp.is_a?(::String)
19
- fail ArgumentError, 'string cron expressions are not yet supported'
6
+ include ::IceCubeCron::ParserAttribute
7
+
8
+ EXPRESSION_PART_DEFAULTS = {
9
+ :repeat_interval => 1,
10
+ :repeat_year => nil,
11
+ :repeat_month => nil,
12
+ :repeat_day => nil,
13
+ :repeat_weekday => nil,
14
+ :repeat_until => nil
15
+ }
16
+
17
+ EXPRESSION_PART_KEYS = [
18
+ :repeat_minute,
19
+ :repeat_hour,
20
+ :repeat_day,
21
+ :repeat_month,
22
+ :repeat_weekday,
23
+ :repeat_year
24
+ ]
25
+
26
+ ##
27
+ # Create a parsed expression
28
+ #
29
+ # Takes a hash of cron expression parts.
30
+ #
31
+ # ### Expression values:
32
+ # - interval
33
+ # - year
34
+ # - month
35
+ # - day
36
+ # - weekday
37
+ def initialize(*expression)
38
+ expression_parts = expression.last.is_a?(::Hash) ? expression.last : {}
39
+
40
+ expression_str = expression[0].is_a?(::String) ? expression[0] : nil
41
+ expression_parts.merge!(string_to_expression_parts(expression_str))
42
+
43
+ self.expression_hash = EXPRESSION_PART_DEFAULTS.dup
44
+
45
+ expression_parts.each do |name, val|
46
+ send("#{name}=", val)
20
47
  end
21
48
  end
22
49
 
23
- def [](val_name)
24
- send(val_name)
50
+ parser_attribute_accessor :interval do |val|
51
+ ExpressionParser.sanitize_integer_param(val, 1)
25
52
  end
26
53
 
27
- def []=(val_name, new_val)
28
- send("#{val_name}=", new_val)
54
+ parser_attribute_accessor :day do |val|
55
+ ExpressionParser.sanitize_day_param(val)
29
56
  end
30
57
 
31
- def interval
32
- expression_hash[:repeat_interval]
58
+ parser_attribute_accessor :month do |val|
59
+ ExpressionParser.sanitize_integer_array_param(val)
33
60
  end
34
- alias_method :repeat_interval, :interval
35
61
 
36
- def interval=(val)
37
- expression_hash[:repeat_interval] = sanitize_integer_param(val, 1)
38
- end
39
- alias_method :repeat_interval=, :interval=
62
+ parser_attribute_accessor :until
40
63
 
41
- def day
42
- expression_hash[:repeat_day]
64
+ parser_attribute_accessor :year do |val|
65
+ ExpressionParser.sanitize_integer_array_param(val)
43
66
  end
44
- alias_method :repeat_day, :day
45
67
 
46
- def day=(val)
47
- expression_hash[:repeat_day] = sanitize_day_param(val)
68
+ parser_attribute_accessor :weekday do |val|
69
+ ExpressionParser.sanitize_week_day_param(val)
48
70
  end
49
- alias_method :repeat_day=, :day=
50
71
 
51
- def month
52
- expression_hash[:repeat_month]
53
- end
54
- alias_method :repeat_month, :month
72
+ ##
73
+ # Split string expression into parts
74
+ #
75
+ def string_to_expression_parts(expression_str)
76
+ return {} if expression_str.nil?
55
77
 
56
- def month=(val)
57
- expression_hash[:repeat_month] = sanitize_integer_array_param(val)
58
- end
59
- alias_method :repeat_month=, :month=
78
+ parts = expression_str.split(/ +/)
60
79
 
61
- def year
62
- expression_hash[:repeat_year]
63
- end
64
- alias_method :repeat_year, :year
65
-
66
- def year=(val)
67
- expression_hash[:repeat_year] = sanitize_integer_array_param(val)
68
- end
69
- alias_method :repeat_year=, :year=
80
+ expression_parts = ::Hash[EXPRESSION_PART_KEYS.zip(parts)]
81
+ expression_parts.select! do |_key, value|
82
+ next false if value.nil?
83
+ next false if value == '*'
70
84
 
71
- def weekday
72
- expression_hash[:repeat_weekday]
73
- end
74
- alias_method :repeat_weekday, :weekday
85
+ true
86
+ end
75
87
 
76
- def weekday=(val)
77
- expression_hash[:repeat_weekday] = sanitize_week_day_param(val)
88
+ expression_parts
78
89
  end
79
- alias_method :repeat_weekday=, :weekday=
80
-
81
- private
82
90
 
83
- def sanitize_integer_param(param, default = nil)
91
+ ##
92
+ # Sanitize given value to an integer
93
+ #
94
+ def self.sanitize_integer_param(param, default = nil)
84
95
  return default if param.blank?
85
96
 
86
97
  param.to_i
87
98
  end
88
99
 
89
- def sanitize_day_param(param)
100
+ ##
101
+ # Sanitize given value to a valid day parameter
102
+ #
103
+ def self.sanitize_day_param(param)
90
104
  return nil if param.blank?
91
105
  return param if param.is_a?(::Array)
92
106
  return [param] if param.is_a?(::Integer)
@@ -94,30 +108,47 @@ module IceCubeCron
94
108
  param.to_s.split(',').map do |element|
95
109
  next -1 if element == 'L'
96
110
 
97
- element.to_i
98
- end
111
+ ExpressionParser.sanitize_integer_array_param(element)
112
+ end.flatten.uniq
99
113
  end
100
114
 
101
- def sanitize_week_day_param(param)
115
+ ##
116
+ # Sanitize given value to a valid weekday parameter
117
+ #
118
+ def self.sanitize_week_day_param(param)
102
119
  return nil if param.blank?
103
120
  param.to_s.split(',').map do |element|
104
121
  if element =~ /[0-9]+#[0-9]+/
105
122
  parts = element.split('#')
106
- { sanitize_integer_param(parts[0]) => sanitize_integer_array_param(parts[1]) }
123
+ { ExpressionParser.sanitize_integer_param(parts[0]) => ExpressionParser.sanitize_integer_array_param(parts[1]) }
107
124
  elsif element =~ /[0-9]+L/
108
- { sanitize_integer_param(element) => [-1] }
125
+ { ExpressionParser.sanitize_integer_param(element) => [-1] }
109
126
  else
110
- sanitize_integer_param(element)
127
+ ExpressionParser.sanitize_integer_param(element)
111
128
  end
112
129
  end
113
130
  end
114
131
 
115
- def sanitize_integer_array_param(param)
132
+ ##
133
+ # Sanitize given value to an integer array
134
+ #
135
+ #--
136
+ # rubocop:disable Metrics/AbcSize
137
+ #++
138
+ def self.sanitize_integer_array_param(param)
116
139
  return nil if param.blank?
117
140
  return param if param.is_a?(::Array)
118
141
  return [param] if param.is_a?(::Integer)
119
142
 
120
- param.split(',').map(&:to_i)
143
+ param.split(',').map do |element|
144
+ if element =~ /[0-9]+\-[0-9]+/
145
+ parts = element.split('-')
146
+ (parts[0].to_i..parts[1].to_i).to_a
147
+ else
148
+ element.to_i
149
+ end
150
+ end.flatten.uniq
121
151
  end
152
+ # rubocop:enable Metrics/AbcSize
122
153
  end
123
154
  end
@@ -1,8 +1,26 @@
1
- module IceCubeCron
1
+ module IceCubeCron # :nodoc:
2
+ ##
3
+ # This adds the `.from_cron` extension to the `::IceCube::Schedule` class
4
+ #
2
5
  module Extension
3
- def from_cron(start_time, expression = {})
4
- expression_parser = ::IceCubeCron::ExpressionParser.new(expression)
5
- rule = ::IceCubeCron::RuleBuilder.new.build_rule(expression_parser)
6
+ ##
7
+ # Generates an ::IceCube::Schedule from a cron expression_parts
8
+ #
9
+ # :args: start_time, expression_parts
10
+ #
11
+ # ## Examples
12
+ #
13
+ # ```ruby
14
+ # schedule = ::IceCube::Schedule.from_cron(::Date.current, "* * * * 5")
15
+ #
16
+ # schedule = ::IceCube::Schedule.from_cron(::Date.new(2015, 1, 5), :repeat_day => 5)
17
+ #
18
+ # schedule.occurrences_between(::Date.new(2015, 3, 5), ::Date.new(2015, 6, 5))
19
+ # # => [2015-03-05 00:00:00 UTC, 2015-04-05 00:00:00 UTC, 2015-05-05 00:00:00 UTC, 2015-06-05 00:00:00 UTC]
20
+ #
21
+ def from_cron(start_time, *expression_parts)
22
+ expression = ::IceCubeCron::ExpressionParser.new(*expression_parts)
23
+ rule = ::IceCubeCron::RuleBuilder.new.build_rule(expression)
6
24
 
7
25
  schedule = ::IceCube::Schedule.new(::IceCubeCron::Util.sanitize_date_param(start_time))
8
26
  schedule.add_recurrence_rule(rule)
@@ -0,0 +1,28 @@
1
+ module IceCubeCron # :nodoc: all
2
+ module ParserAttribute # :nodoc:
3
+ def self.included(base)
4
+ base.extend(::IceCubeCron::ParserAttribute::ClassMethods)
5
+ end
6
+
7
+ attr_accessor :expression_hash
8
+
9
+ module ClassMethods # :nodoc:
10
+ def parser_attribute_accessor(attr_name, &cleanser)
11
+ getter = "repeat_#{attr_name}".to_sym
12
+ setter = "repeat_#{attr_name}=".to_sym
13
+
14
+ define_method(getter) do
15
+ expression_hash[getter]
16
+ end
17
+
18
+ define_method(setter) do |val|
19
+ val = cleanser.call(val) if cleanser
20
+ expression_hash[getter] = val
21
+ end
22
+
23
+ alias_method attr_name, getter
24
+ alias_method "#{attr_name}=".to_sym, setter
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,34 +1,54 @@
1
- module IceCubeCron
1
+ module IceCubeCron # :nodoc:
2
+ ##
3
+ # Generates Rules based on parsed expression
4
+ #
2
5
  class RuleBuilder
3
- def build_rule(params)
4
- rule = build_root_recurrence_rule(params)
5
- rule = rule.month_of_year(*params[:month]) unless params[:month].blank?
6
- rule = rule.day_of_month(*params[:day]) unless params[:day].blank?
7
- rule = build_weekday_rule(rule, params)
6
+ ##
7
+ # Generates a rule based on a parsed expression
8
+ #
9
+ def build_rule(expression)
10
+ rule = build_root_recurrence_rule(expression)
11
+ rule = build_yearly_rules(rule, expression)
12
+ rule = build_weekday_rule(rule, expression)
13
+ rule = rule.day_of_month(*expression.day) unless expression.day.blank?
14
+ rule = rule.until(expression.until) unless expression.until.blank?
8
15
 
9
16
  rule
10
17
  end
11
18
 
12
- def build_weekday_rule(rule, params)
13
- return rule.day_of_week(*params[:weekday]) if !params[:weekday].blank? && nth_day?(params[:weekday])
14
- return rule.day(*params[:weekday]) unless params[:weekday].blank?
19
+ # :nodoc:
20
+ def nth_day?(param)
21
+ return false if param.nil? || param.empty?
22
+ param[0].is_a?(::Hash)
23
+ end
24
+
25
+ private
26
+
27
+ # :nodoc:
28
+ def build_weekday_rule(rule, expression)
29
+ return rule.day_of_week(*expression.weekday) if !expression.weekday.blank? && nth_day?(expression.weekday)
30
+ return rule.day(*expression.weekday) unless expression.weekday.blank?
15
31
 
16
32
  rule
17
33
  end
18
34
 
19
- # rubocop:disable Metrics/AbcSize
20
- def build_root_recurrence_rule(params)
21
- return ::IceCube::Rule.yearly(params[:interval]) unless params[:month].blank?
22
- return ::IceCube::Rule.monthly(params[:interval]) if !params[:weekday].blank? && nth_day?(params[:weekday])
23
- return ::IceCube::Rule.weekly(params[:interval]) unless params[:weekday].blank?
35
+ # :nodoc:
36
+ def build_yearly_rules(rule, expression)
37
+ rule = rule.year(*expression.year) unless expression.year.blank?
38
+ rule = rule.month_of_year(*expression.month) unless expression.month.blank?
24
39
 
25
- ::IceCube::Rule.monthly(params[:interval])
40
+ rule
26
41
  end
27
- # rubocop:enable Metrics/AbcSize
28
42
 
29
- def nth_day?(param)
30
- return false if param.nil? || param.empty?
31
- param[0].is_a?(::Hash)
43
+ # rubocop:disable Metrics/AbcSize
44
+ def build_root_recurrence_rule(expression) # :nodoc:
45
+ return ::IceCube::Rule.yearly(expression.interval) unless expression.month.blank?
46
+ return ::IceCube::Rule.monthly(expression.interval) if !expression.weekday.blank? && nth_day?(expression.weekday)
47
+ return ::IceCube::Rule.monthly(expression.interval) unless expression.day.blank?
48
+ return ::IceCube::Rule.weekly(expression.interval) unless expression.weekday.blank?
49
+
50
+ ::IceCube::Rule.monthly(expression.interval)
32
51
  end
52
+ # rubocop:enable Metrics/AbcSize
33
53
  end
34
54
  end
@@ -1,4 +1,7 @@
1
- module IceCubeCron
1
+ module IceCubeCron # :nodoc: all
2
+ ##
3
+ # :category: Utilities
4
+ #
2
5
  module Util
3
6
  def self.sanitize_date_param(date)
4
7
  date = date.to_time(:utc) if date.is_a?(::Date) && !date.is_a?(::DateTime)
@@ -1,3 +1,3 @@
1
- module IceCubeCron
2
- VERSION = '0.0.2'
1
+ module IceCubeCron # :nodoc: all
2
+ VERSION = '0.0.3'
3
3
  end
data/lib/ice_cube_cron.rb CHANGED
@@ -1,11 +1,17 @@
1
1
  require 'ice_cube'
2
2
 
3
+ # core extensions
3
4
  require 'active_support/core_ext/date'
4
5
  require 'active_support/core_ext/integer'
5
6
 
7
+ # ice_cube extentions
8
+ require 'ice_cube_ext/validations/year.rb'
9
+ require 'ice_cube_ext/validated_rule.rb'
10
+
6
11
  require 'ice_cube_cron/version'
7
12
  require 'ice_cube_cron/util'
8
13
  require 'ice_cube_cron/rule_builder'
14
+ require 'ice_cube_cron/parser_attribute'
9
15
  require 'ice_cube_cron/expression_parser'
10
16
  require 'ice_cube_cron/extension'
11
17
  require 'ice_cube_cron/base'
@@ -0,0 +1,5 @@
1
+ module IceCube
2
+ class ValidatedRule < Rule # :nodoc:
3
+ include Validations::Year
4
+ end
5
+ end
@@ -0,0 +1,55 @@
1
+ module IceCube
2
+ module Validations
3
+ ##
4
+ # Allows addition of a year (non-standard cron) to the repeat rules.
5
+ #
6
+ module Year
7
+ ##
8
+ # Call to filter occurrences to one or more years.
9
+ #
10
+ def year(*years)
11
+ years.flatten.each do |year|
12
+ unless year.is_a?(Fixnum)
13
+ raise ArgumentError, "expecting Fixnum value for year, got #{year.inspect}"
14
+ end
15
+ validations_for(:year) << Validation.new(year)
16
+ end
17
+ ::IceCube::Validations::FixedValue::INTERVALS[:year] = 100
18
+ self
19
+ end
20
+
21
+ class Validation < Validations::FixedValue # :nodoc:
22
+ attr_reader :year
23
+ alias_method :value, :year
24
+
25
+ def initialize(year)
26
+ @year = year
27
+ end
28
+
29
+ def type
30
+ :year
31
+ end
32
+
33
+ def dst_adjust?
34
+ true
35
+ end
36
+
37
+ def build_s(builder)
38
+ builder.piece(:year) << StringBuilder.nice_number(year)
39
+ end
40
+
41
+ def build_hash(builder)
42
+ builder.validations_array(:year) << year
43
+ end
44
+
45
+ def build_ical(builder)
46
+ builder['BYYEAR'] << year
47
+ end
48
+
49
+ StringBuilder.register_formatter(:year) do |entries|
50
+ "in #{StringBuilder.sentence(entries)} "
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -214,6 +214,67 @@ describe ::IceCubeCron do
214
214
  expect(ice_cube_model.occurrences_between(::Date.new(2016, 2, 1), ::Date.new(2016, 4, 30))).to eq([::Date.new(2016, 2, 8), ::Date.new(2016, 3, 14), ::Date.new(2016, 4, 11)])
215
215
  end
216
216
  end
217
+
218
+ context 'every month until date' do
219
+ let(:schedule) do
220
+ ::IceCube::Schedule.from_cron(
221
+ ::Date.new(2015, 1, 1),
222
+ :repeat_day => '1',
223
+ :repeat_until => ::Date.new(2015, 3, 1)
224
+ )
225
+ end
226
+
227
+ it 'ends on specified end date' do
228
+ expect(schedule.occurrences_between(::Date.new(2015, 1, 1), ::Date.new(2015, 6, 30))).to eq([::Date.new(2015, 1, 1), ::Date.new(2015, 2, 1), ::Date.new(2015, 3, 1)])
229
+ end
230
+ end
231
+
232
+ context 'year expression support (non-standard)' do
233
+ context 'in month of a year' do
234
+ let(:ice_cube_model) do
235
+ ::IceCube::Schedule.from_cron(
236
+ ::Date.new(2015, 1, 1),
237
+ :year => '2015',
238
+ :month => '6',
239
+ :day => '10'
240
+ )
241
+ end
242
+
243
+ it '#occurrences_between' do
244
+ expect(ice_cube_model.occurrences_between(::Date.new(2013, 1, 1), ::Date.new(2017, 12, 31))).to eq([::Date.new(2015, 6, 10)])
245
+ end
246
+ end
247
+
248
+ context 'in a month of multiple years' do
249
+ let(:ice_cube_model) do
250
+ ::IceCube::Schedule.from_cron(
251
+ ::Date.new(2015, 1, 1),
252
+ :year => '2015,2017',
253
+ :month => '6',
254
+ :day => '10'
255
+ )
256
+ end
257
+
258
+ it '#occurrences_between' do
259
+ expect(ice_cube_model.occurrences_between(::Date.new(2013, 1, 1), ::Date.new(2018, 12, 31))).to eq([::Date.new(2015, 6, 10), ::Date.new(2017, 6, 10)])
260
+ end
261
+ end
262
+
263
+ context 'multiple days in month of a year' do
264
+ let(:ice_cube_model) do
265
+ ::IceCube::Schedule.from_cron(
266
+ ::Date.new(2015, 1, 1),
267
+ :year => '2015',
268
+ :month => '6',
269
+ :day => '10,15'
270
+ )
271
+ end
272
+
273
+ it '#occurrences_between' do
274
+ expect(ice_cube_model.occurrences_between(::Date.new(2013, 1, 1), ::Date.new(2017, 12, 31))).to eq([::Date.new(2015, 6, 10), ::Date.new(2015, 6, 15)])
275
+ end
276
+ end
277
+ end
217
278
  end
218
279
 
219
280
  context 'input types' do
@@ -223,7 +284,7 @@ describe ::IceCubeCron do
223
284
  :repeat_day => '1',
224
285
  :repeat_interval => '2'
225
286
  )
226
- expect(ice_cube_model.occurrences_between(::DateTime.new(2015, 7, 1), ::DateTime.new(2015, 7, 31))).to eq([::DateTime.new(2015, 7, 1)])
287
+ expect(ice_cube_model.occurrences_between(::Date.new(2015, 7, 1), ::Date.new(2015, 7, 31))).to eq([::Date.new(2015, 7, 1)])
227
288
  end
228
289
 
229
290
  it 'handles integer (epoch) as input' do
@@ -232,7 +293,7 @@ describe ::IceCubeCron do
232
293
  :repeat_day => '1',
233
294
  :repeat_interval => '2'
234
295
  )
235
- expect(ice_cube_model.occurrences_between(::DateTime.new(2015, 7, 1), ::DateTime.new(2015, 7, 31))).to eq([::DateTime.new(2015, 7, 1)])
296
+ expect(ice_cube_model.occurrences_between(::Date.new(2015, 7, 1), ::Date.new(2015, 7, 31))).to eq([::Date.new(2015, 7, 1)])
236
297
  end
237
298
  end
238
299
  end
@@ -1,8 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe ::IceCubeCron::ExpressionParser do
4
- let(:expression_parser) do
4
+ let(:expression) do
5
5
  described_class.new(
6
+ nil,
6
7
  :repeat_interval => 2,
7
8
  :repeat_day => 3,
8
9
  :repeat_month => 4,
@@ -11,144 +12,196 @@ describe ::IceCubeCron::ExpressionParser do
11
12
  )
12
13
  end
13
14
 
15
+ describe 'parses ::String expression' do
16
+ # rubocop:disable Style/SymbolProc
17
+ let(:expression_str) { |example| example.description }
18
+ # rubocop:enable Style/SymbolProc
19
+ let(:expression) { described_class.new(expression_str) }
20
+
21
+ it '* * 2 * *' do
22
+ expect(expression.expression_hash[:repeat_interval]).to eq(1)
23
+ expect(expression.expression_hash[:repeat_year]).to eq(nil)
24
+ expect(expression.expression_hash[:repeat_month]).to eq(nil)
25
+ expect(expression.expression_hash[:repeat_day]).to eq([2])
26
+ expect(expression.expression_hash[:repeat_weekday]).to eq(nil)
27
+ end
28
+
29
+ it '* * 1,15 * *' do
30
+ expect(expression.expression_hash[:repeat_interval]).to eq(1)
31
+ expect(expression.expression_hash[:repeat_year]).to eq(nil)
32
+ expect(expression.expression_hash[:repeat_month]).to eq(nil)
33
+ expect(expression.expression_hash[:repeat_day]).to eq([1, 15])
34
+ expect(expression.expression_hash[:repeat_weekday]).to eq(nil)
35
+ end
36
+
37
+ it '* * 1 6,12 *' do
38
+ expect(expression.expression_hash[:repeat_interval]).to eq(1)
39
+ expect(expression.expression_hash[:repeat_year]).to eq(nil)
40
+ expect(expression.expression_hash[:repeat_month]).to eq([6, 12])
41
+ expect(expression.expression_hash[:repeat_day]).to eq([1])
42
+ expect(expression.expression_hash[:repeat_weekday]).to eq(nil)
43
+ end
44
+
45
+ it '* * 1 6 * 2015' do
46
+ expect(expression.expression_hash[:repeat_interval]).to eq(1)
47
+ expect(expression.expression_hash[:repeat_year]).to eq([2015])
48
+ expect(expression.expression_hash[:repeat_month]).to eq([6])
49
+ expect(expression.expression_hash[:repeat_day]).to eq([1])
50
+ expect(expression.expression_hash[:repeat_weekday]).to eq(nil)
51
+ end
52
+
53
+ it '* * * 6 1L *' do
54
+ expect(expression.expression_hash[:repeat_interval]).to eq(1)
55
+ expect(expression.expression_hash[:repeat_year]).to eq(nil)
56
+ expect(expression.expression_hash[:repeat_month]).to eq([6])
57
+ expect(expression.expression_hash[:repeat_day]).to eq(nil)
58
+ expect(expression.expression_hash[:repeat_weekday]).to eq([1 => [-1]])
59
+ end
60
+ end
61
+
14
62
  describe 'parses ::Hash expression' do
15
63
  describe 'repeat_interval' do
16
64
  it '[]' do
17
- expect(expression_parser[:repeat_interval]).to eq(2)
65
+ expect(expression.interval).to eq(2)
18
66
  end
19
67
 
20
68
  it '[]=' do
21
- expression_parser[:repeat_interval] = 13
22
- expect(expression_parser[:repeat_interval]).to eq(13)
69
+ expression.interval = 13
70
+ expect(expression.interval).to eq(13)
23
71
  end
24
72
 
25
73
  it 'sanitizes' do
26
- expression_parser[:repeat_interval] = 2
27
- expect(expression_parser[:repeat_interval]).to eq(2)
74
+ expression.interval = 2
75
+ expect(expression.interval).to eq(2)
28
76
 
29
- expression_parser[:repeat_interval] = nil
30
- expect(expression_parser[:repeat_interval]).to eq(1)
77
+ expression.interval = nil
78
+ expect(expression.interval).to eq(1)
31
79
 
32
- expression_parser[:repeat_interval] = '3'
33
- expect(expression_parser[:repeat_interval]).to eq(3)
80
+ expression.interval = '3'
81
+ expect(expression.interval).to eq(3)
34
82
  end
35
83
  end
36
84
 
37
85
  describe 'repeat_day' do
38
86
  it '[]' do
39
- expect(expression_parser[:repeat_day]).to eq([3])
87
+ expect(expression.day).to eq([3])
40
88
  end
41
89
 
42
90
  it '[]=' do
43
- expression_parser[:repeat_day] = 6
44
- expect(expression_parser[:repeat_day]).to eq([6])
91
+ expression.day = 6
92
+ expect(expression.day).to eq([6])
45
93
  end
46
94
 
47
95
  it 'sanitizes' do
48
- expression_parser[:repeat_day] = 2
49
- expect(expression_parser[:repeat_day]).to eq([2])
96
+ expression.day = 2
97
+ expect(expression.day).to eq([2])
50
98
 
51
- expression_parser[:repeat_day] = nil
52
- expect(expression_parser[:repeat_day]).to eq(nil)
99
+ expression.day = nil
100
+ expect(expression.day).to eq(nil)
53
101
 
54
- expression_parser[:repeat_day] = '3'
55
- expect(expression_parser[:repeat_day]).to eq([3])
102
+ expression.day = '3'
103
+ expect(expression.day).to eq([3])
56
104
  end
57
105
 
58
106
  it 'should accept single day expression' do
59
- expression_parser.repeat_day = '1'
60
- expect(expression_parser.repeat_day).to eq([1])
107
+ expression.day = '1'
108
+ expect(expression.day).to eq([1])
61
109
  end
62
110
 
63
111
  it 'should accept series expression' do
64
- expression_parser.repeat_day = '1,3'
65
- expect(expression_parser.repeat_day).to eq([1, 3])
112
+ expression.day = '1,3'
113
+ expect(expression.day).to eq([1, 3])
114
+ end
115
+
116
+ it 'should accept range expression' do
117
+ expression.day = '1-3'
118
+ expect(expression.day).to eq([1, 2, 3])
66
119
  end
67
120
 
68
121
  it 'should accept last day expression' do
69
- expression_parser.repeat_day = 'L'
70
- expect(expression_parser.repeat_day).to eq([-1])
122
+ expression.day = 'L'
123
+ expect(expression.day).to eq([-1])
71
124
  end
72
125
  end
73
126
 
74
127
  describe 'repeat_month' do
75
128
  it '[]' do
76
- expect(expression_parser[:repeat_month]).to eq([4])
129
+ expect(expression.month).to eq([4])
77
130
  end
78
131
 
79
132
  it '[]=' do
80
- expression_parser[:repeat_month] = 11
81
- expect(expression_parser[:repeat_month]).to eq([11])
133
+ expression.month = 11
134
+ expect(expression.month).to eq([11])
82
135
  end
83
136
 
84
137
  it 'sanitizes' do
85
- expression_parser[:repeat_month] = 2
86
- expect(expression_parser[:repeat_month]).to eq([2])
138
+ expression.month = 2
139
+ expect(expression.month).to eq([2])
87
140
 
88
- expression_parser[:repeat_month] = nil
89
- expect(expression_parser[:repeat_month]).to eq(nil)
141
+ expression.month = nil
142
+ expect(expression.month).to eq(nil)
90
143
 
91
- expression_parser[:repeat_month] = '3'
92
- expect(expression_parser[:repeat_month]).to eq([3])
144
+ expression.month = '3'
145
+ expect(expression.month).to eq([3])
93
146
  end
94
147
  end
95
148
 
96
149
  describe 'repeat_year' do
97
150
  it '[]' do
98
- expect(expression_parser[:repeat_year]).to eq([1990])
151
+ expect(expression.year).to eq([1990])
99
152
  end
100
153
 
101
154
  it '[]=' do
102
- expression_parser[:repeat_year] = 1994
103
- expect(expression_parser[:repeat_year]).to eq([1994])
155
+ expression.year = 1994
156
+ expect(expression.year).to eq([1994])
104
157
  end
105
158
 
106
159
  it 'sanitizes' do
107
- expression_parser[:repeat_year] = 1992
108
- expect(expression_parser[:repeat_year]).to eq([1992])
160
+ expression.year = 1992
161
+ expect(expression.year).to eq([1992])
109
162
 
110
- expression_parser[:repeat_year] = nil
111
- expect(expression_parser[:repeat_year]).to eq(nil)
163
+ expression.year = nil
164
+ expect(expression.year).to eq(nil)
112
165
 
113
- expression_parser[:repeat_year] = '2001'
114
- expect(expression_parser[:repeat_year]).to eq([2001])
166
+ expression.year = '2001'
167
+ expect(expression.year).to eq([2001])
115
168
  end
116
169
  end
117
170
 
118
171
  describe 'repeat_weekday' do
119
172
  it '[]' do
120
- expect(expression_parser[:repeat_weekday]).to eq([0])
173
+ expect(expression.weekday).to eq([0])
121
174
  end
122
175
 
123
176
  it '[]=' do
124
- expression_parser[:repeat_weekday] = 4
125
- expect(expression_parser[:repeat_weekday]).to eq([4])
177
+ expression.weekday = 4
178
+ expect(expression.weekday).to eq([4])
126
179
  end
127
180
 
128
181
  it 'sanitizes' do
129
- expression_parser[:repeat_weekday] = 2
130
- expect(expression_parser[:repeat_weekday]).to eq([2])
182
+ expression.weekday = 2
183
+ expect(expression.weekday).to eq([2])
131
184
 
132
- expression_parser[:repeat_weekday] = nil
133
- expect(expression_parser[:repeat_weekday]).to eq(nil)
185
+ expression.weekday = nil
186
+ expect(expression.weekday).to eq(nil)
134
187
 
135
- expression_parser[:repeat_weekday] = '3'
136
- expect(expression_parser[:repeat_weekday]).to eq([3])
188
+ expression.weekday = '3'
189
+ expect(expression.weekday).to eq([3])
137
190
  end
138
191
 
139
192
  it 'should accept non-nth weekday expression' do
140
- expression_parser.weekday = '1'
141
- expect(expression_parser.weekday).to eq([1])
193
+ expression.weekday = '1'
194
+ expect(expression.weekday).to eq([1])
142
195
  end
143
196
 
144
197
  it 'should accept nth weekday expression' do
145
- expression_parser.weekday = '1#3'
146
- expect(expression_parser.weekday).to eq([{ 1 => [3] }])
198
+ expression.weekday = '1#3'
199
+ expect(expression.weekday).to eq([{ 1 => [3] }])
147
200
  end
148
201
 
149
202
  it 'should accept last weekday expression' do
150
- expression_parser.weekday = '1L'
151
- expect(expression_parser.weekday).to eq([{ 1 => [-1] }])
203
+ expression.weekday = '1L'
204
+ expect(expression.weekday).to eq([{ 1 => [-1] }])
152
205
  end
153
206
  end
154
207
  end
@@ -18,6 +18,11 @@ describe ::IceCubeCron::RuleBuilder do
18
18
  expect(rule_builder.build_rule(expression)).to be_a(IceCube::WeeklyRule)
19
19
  end
20
20
 
21
+ it 'monthly (last week day of month)' do
22
+ expression.weekday = '2L'
23
+ expect(rule_builder.build_rule(expression)).to be_a(IceCube::MonthlyRule)
24
+ end
25
+
21
26
  it 'yearly' do
22
27
  expression.month = 2
23
28
  expect(rule_builder.build_rule(expression)).to be_a(IceCube::YearlyRule)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ice_cube_cron
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Nichols
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-15 00:00:00.000000000 Z
11
+ date: 2015-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ice_cube
@@ -103,6 +103,7 @@ extensions: []
103
103
  extra_rdoc_files: []
104
104
  files:
105
105
  - ".gitignore"
106
+ - ".rdoc_options"
106
107
  - ".rspec"
107
108
  - ".rubocop.yml"
108
109
  - Gemfile
@@ -115,9 +116,12 @@ files:
115
116
  - lib/ice_cube_cron/base.rb
116
117
  - lib/ice_cube_cron/expression_parser.rb
117
118
  - lib/ice_cube_cron/extension.rb
119
+ - lib/ice_cube_cron/parser_attribute.rb
118
120
  - lib/ice_cube_cron/rule_builder.rb
119
121
  - lib/ice_cube_cron/util.rb
120
122
  - lib/ice_cube_cron/version.rb
123
+ - lib/ice_cube_ext/validated_rule.rb
124
+ - lib/ice_cube_ext/validations/year.rb
121
125
  - spec/lib/ice_cube_cron/base_spec.rb
122
126
  - spec/lib/ice_cube_cron/expression_parser_spec.rb
123
127
  - spec/lib/ice_cube_cron/rule_builder_spec.rb