ice_cube_cron 0.0.2 → 0.0.3

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: 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