zhong 0.1.4 → 0.1.5

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: 56d5bb0e028b58158fb6078391782b96a4dab9d3
4
- data.tar.gz: 1b9dcabc60857d70547c091ddb526d706f258a1d
3
+ metadata.gz: 4eafb64d51589613dfcfeca62326319eb839962f
4
+ data.tar.gz: 66b4fd921dd9ed1795ffa407afbcc5f33b867607
5
5
  SHA512:
6
- metadata.gz: b1e9ecee03c7c42c66440daff973f0273558c5ba3b2925479062a07d35bd340f94315608035ef2e44401efb82b0807a3dab72c91e277bda47d81d83818938c67
7
- data.tar.gz: e03cb2a4b7f285021ac254bdd019c2017b0a085a2b3ae51d88a19bbd2af6e9b5330b4ee75e894511c236fed27908c7a96013bed47d1210d7e21b4a304a1658ee
6
+ metadata.gz: 267619b1f313f958d2e2e7071bb3fbdcb22efcd9018e64d1cdcf5f07d86d055e5e4e4e87f8b1144550956ded9370c10584212fec1c6c7890bd6ace880af2348b
7
+ data.tar.gz: e4a9329e22811d4cda8f01882ed7e8755388b30723eb0a424f5b01ff05d526f512be60fae222d77b359326e16f60208cc8452b0ce837b9ad2ea492e9d17bf8a6
@@ -0,0 +1,23 @@
1
+ ---
2
+ engines:
3
+ duplication:
4
+ enabled: true
5
+ config:
6
+ languages:
7
+ - ruby
8
+ - javascript
9
+ eslint:
10
+ enabled: true
11
+ fixme:
12
+ enabled: true
13
+ rubocop:
14
+ enabled: true
15
+ ratings:
16
+ paths:
17
+ - "**.js"
18
+ - "**.jsx"
19
+ - "**.module"
20
+ - "**.rb"
21
+ exclude_paths:
22
+ - test/
23
+ - "**/*{.,-}min.js"
@@ -0,0 +1 @@
1
+ **/*{.,-}min.js
@@ -0,0 +1,213 @@
1
+ ecmaFeatures:
2
+ modules: true
3
+ jsx: true
4
+
5
+ env:
6
+ amd: true
7
+ browser: true
8
+ es6: true
9
+ jquery: true
10
+ node: true
11
+
12
+ # http://eslint.org/docs/rules/
13
+ rules:
14
+ # Possible Errors
15
+ comma-dangle: [2, never]
16
+ no-cond-assign: 2
17
+ no-console: 0
18
+ no-constant-condition: 2
19
+ no-control-regex: 2
20
+ no-debugger: 2
21
+ no-dupe-args: 2
22
+ no-dupe-keys: 2
23
+ no-duplicate-case: 2
24
+ no-empty: 2
25
+ no-empty-character-class: 2
26
+ no-ex-assign: 2
27
+ no-extra-boolean-cast: 2
28
+ no-extra-parens: 0
29
+ no-extra-semi: 2
30
+ no-func-assign: 2
31
+ no-inner-declarations: [2, functions]
32
+ no-invalid-regexp: 2
33
+ no-irregular-whitespace: 2
34
+ no-negated-in-lhs: 2
35
+ no-obj-calls: 2
36
+ no-regex-spaces: 2
37
+ no-sparse-arrays: 2
38
+ no-unexpected-multiline: 2
39
+ no-unreachable: 2
40
+ use-isnan: 2
41
+ valid-jsdoc: 0
42
+ valid-typeof: 2
43
+
44
+ # Best Practices
45
+ accessor-pairs: 2
46
+ block-scoped-var: 0
47
+ complexity: [2, 6]
48
+ consistent-return: 0
49
+ curly: 0
50
+ default-case: 0
51
+ dot-location: 0
52
+ dot-notation: 0
53
+ eqeqeq: 2
54
+ guard-for-in: 2
55
+ no-alert: 2
56
+ no-caller: 2
57
+ no-case-declarations: 2
58
+ no-div-regex: 2
59
+ no-else-return: 0
60
+ no-empty-label: 2
61
+ no-empty-pattern: 2
62
+ no-eq-null: 2
63
+ no-eval: 2
64
+ no-extend-native: 2
65
+ no-extra-bind: 2
66
+ no-fallthrough: 2
67
+ no-floating-decimal: 0
68
+ no-implicit-coercion: 0
69
+ no-implied-eval: 2
70
+ no-invalid-this: 0
71
+ no-iterator: 2
72
+ no-labels: 0
73
+ no-lone-blocks: 2
74
+ no-loop-func: 2
75
+ no-magic-number: 0
76
+ no-multi-spaces: 0
77
+ no-multi-str: 0
78
+ no-native-reassign: 2
79
+ no-new-func: 2
80
+ no-new-wrappers: 2
81
+ no-new: 2
82
+ no-octal-escape: 2
83
+ no-octal: 2
84
+ no-proto: 2
85
+ no-redeclare: 2
86
+ no-return-assign: 2
87
+ no-script-url: 2
88
+ no-self-compare: 2
89
+ no-sequences: 0
90
+ no-throw-literal: 0
91
+ no-unused-expressions: 2
92
+ no-useless-call: 2
93
+ no-useless-concat: 2
94
+ no-void: 2
95
+ no-warning-comments: 0
96
+ no-with: 2
97
+ radix: 2
98
+ vars-on-top: 0
99
+ wrap-iife: 2
100
+ yoda: 0
101
+
102
+ # Strict
103
+ strict: 0
104
+
105
+ # Variables
106
+ init-declarations: 0
107
+ no-catch-shadow: 2
108
+ no-delete-var: 2
109
+ no-label-var: 2
110
+ no-shadow-restricted-names: 2
111
+ no-shadow: 0
112
+ no-undef-init: 2
113
+ no-undef: 0
114
+ no-undefined: 0
115
+ no-unused-vars: 0
116
+ no-use-before-define: 0
117
+
118
+ # Node.js and CommonJS
119
+ callback-return: 2
120
+ global-require: 2
121
+ handle-callback-err: 2
122
+ no-mixed-requires: 0
123
+ no-new-require: 0
124
+ no-path-concat: 2
125
+ no-process-exit: 2
126
+ no-restricted-modules: 0
127
+ no-sync: 0
128
+
129
+ # Stylistic Issues
130
+ array-bracket-spacing: 0
131
+ block-spacing: 0
132
+ brace-style: 0
133
+ camelcase: 0
134
+ comma-spacing: 0
135
+ comma-style: 0
136
+ computed-property-spacing: 0
137
+ consistent-this: 0
138
+ eol-last: 0
139
+ func-names: 0
140
+ func-style: 0
141
+ id-length: 0
142
+ id-match: 0
143
+ indent: 0
144
+ jsx-quotes: 0
145
+ key-spacing: 0
146
+ linebreak-style: 0
147
+ lines-around-comment: 0
148
+ max-depth: 0
149
+ max-len: 0
150
+ max-nested-callbacks: 0
151
+ max-params: 0
152
+ max-statements: [2, 30]
153
+ new-cap: 0
154
+ new-parens: 0
155
+ newline-after-var: 0
156
+ no-array-constructor: 0
157
+ no-bitwise: 0
158
+ no-continue: 0
159
+ no-inline-comments: 0
160
+ no-lonely-if: 0
161
+ no-mixed-spaces-and-tabs: 0
162
+ no-multiple-empty-lines: 0
163
+ no-negated-condition: 0
164
+ no-nested-ternary: 0
165
+ no-new-object: 0
166
+ no-plusplus: 0
167
+ no-restricted-syntax: 0
168
+ no-spaced-func: 0
169
+ no-ternary: 0
170
+ no-trailing-spaces: 0
171
+ no-underscore-dangle: 0
172
+ no-unneeded-ternary: 0
173
+ object-curly-spacing: 0
174
+ one-var: 0
175
+ operator-assignment: 0
176
+ operator-linebreak: 0
177
+ padded-blocks: 0
178
+ quote-props: 0
179
+ quotes: 0
180
+ require-jsdoc: 0
181
+ semi-spacing: 0
182
+ semi: 0
183
+ sort-vars: 0
184
+ space-after-keywords: 0
185
+ space-before-blocks: 0
186
+ space-before-function-paren: 0
187
+ space-before-keywords: 0
188
+ space-in-parens: 0
189
+ space-infix-ops: 0
190
+ space-return-throw-case: 0
191
+ space-unary-ops: 0
192
+ spaced-comment: 0
193
+ wrap-regex: 0
194
+
195
+ # ECMAScript 6
196
+ arrow-body-style: 0
197
+ arrow-parens: 0
198
+ arrow-spacing: 0
199
+ constructor-super: 0
200
+ generator-star-spacing: 0
201
+ no-arrow-condition: 0
202
+ no-class-assign: 0
203
+ no-const-assign: 0
204
+ no-dupe-class-members: 0
205
+ no-this-before-super: 0
206
+ no-var: 0
207
+ object-shorthand: 0
208
+ prefer-arrow-callback: 0
209
+ prefer-const: 0
210
+ prefer-reflect: 0
211
+ prefer-spread: 0
212
+ prefer-template: 0
213
+ require-yield: 0
@@ -1,3 +1,8 @@
1
+ ## 0.1.5
2
+
3
+ - Improve the API.
4
+ - Add scheduler test.
5
+
1
6
  ## 0.1.4
2
7
 
3
8
  - Fix several bugs related to time parsing.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Zhong
1
+ # Zhong [![Build Status](https://travis-ci.org/nickelser/zhong.svg?branch=master)](https://travis-ci.org/nickelser/zhong) [![Code Climate](https://codeclimate.com/github/nickelser/zhong/badges/gpa.svg)](https://codeclimate.com/github/nickelser/zhong) [![Test Coverage](https://codeclimate.com/github/nickelser/zhong/badges/coverage.svg)](https://codeclimate.com/github/nickelser/zhong) [![Gem Version](https://badge.fury.io/rb/zhong.svg)](http://badge.fury.io/rb/zhong)
2
2
 
3
3
  Useful, reliable distributed cron. Tired of your cron-like scheduler running key jobs twice? Would you like to be able to run your cron server on multiple machines and have it "just work"? Have we got the gem for you.
4
4
 
@@ -15,34 +15,37 @@ gem 'zhong'
15
15
  ## Usage
16
16
 
17
17
  ```ruby
18
- r = Redis.new
19
-
20
- Zhong.schedule(redis: r) do |s|
21
- s.category "stuff" do
22
- s.every(5.seconds, "foo") { puts "foo" }
23
- s.every(1.minute, "biz", at: ["**:26", "**:27"]) { puts "biz" }
24
- s.every(1.week, "baz", at: ["mon 22:45", "wed 23:13"]) { puts "baz" }
25
- s.every(10.seconds, "boom") { raise "fail" }
18
+ Zhong.redis = Redis.new(url: ENV["ZHONG_REDIS_URL"])
19
+
20
+ Zhong.schedule do
21
+ category "stuff" do
22
+ every 5.seconds, "foo" do
23
+ puts "foo"
24
+ end
25
+
26
+ every(1.minute, "biz", at: ["**:26", "**:27"]) { puts "biz" }
27
+ every(1.week, "baz", at: ["mon 22:45", "wed 23:13"]) { puts "baz" }
28
+ every(10.seconds, "boom") { raise "fail" }
26
29
  end
27
30
 
28
- s.category "clutter" do
29
- s.every(1.second, "compute", if: -> (t) { t.wday == 3 && rand < 0.5 }) do
31
+ category "clutter" do
32
+ every(1.second, "compute", if: -> (t) { t.wday == 3 && rand < 0.5 }) do
30
33
  puts "something happened on wednesday, maybe"
31
34
  end
32
35
  end
33
36
 
34
37
  # note: callbacks that return nil or false will cause event to not run
35
- s.on(:before_tick) do
38
+ on(:before_tick) do
36
39
  puts "ding"
37
40
  true
38
41
  end
39
42
 
40
- s.on(:after_tick) do
43
+ on(:after_tick) do
41
44
  puts "dong"
42
45
  true
43
46
  end
44
47
 
45
- s.error_handler do |e, job|
48
+ error_handler do |e, job|
46
49
  puts "damn, #{job} messed up: #{e}"
47
50
  end
48
51
  end
data/bin/zhong CHANGED
@@ -2,14 +2,18 @@
2
2
 
3
3
  STDERR.sync = STDOUT.sync = true
4
4
 
5
- require "bundler/setup"
6
5
  require "zhong"
7
6
 
8
- usage = "zhong <zhong.rb>"
9
- file = ARGV.shift || abort(usage)
7
+ file = ARGV.shift or abort "zhong <path to zhong.rb>"
10
8
 
11
9
  file = "./#{file}" unless file.match(/^[\/.]/)
12
10
 
13
11
  require file
14
12
 
15
- Zhong.start
13
+ begin
14
+ Zhong.start
15
+ rescue => boom
16
+ STDERR.puts boom.message
17
+ STDERR.puts boom.backtrace.join("\n")
18
+ exit 1
19
+ end
@@ -17,6 +17,20 @@ module Zhong
17
17
 
18
18
  attr_accessor :minute, :hour, :wday
19
19
 
20
+ def self.parse(at, grace: 0.seconds)
21
+ if at.respond_to?(:each)
22
+ MultiAt.new(at.map { |a| parse_at(a, grace) })
23
+ else
24
+ parse_at(at, grace)
25
+ end
26
+ rescue ArgumentError
27
+ fail FailedToParse, at
28
+ end
29
+
30
+ def self.deserialize(at)
31
+ parse_serialized(MessagePack.unpack(at))
32
+ end
33
+
20
34
  def initialize(minute: nil, hour: nil, wday: nil, grace: 0.seconds)
21
35
  @minute = minute
22
36
  @hour = hour
@@ -27,25 +41,13 @@ module Zhong
27
41
  end
28
42
 
29
43
  def next_at(time = Time.now)
30
- at_time = @wday.nil? ? time.dup : (time + (@wday - time.wday).days)
31
-
32
- at_time = if !@minute.nil? && !@hour.nil?
33
- at_time.change(hour: @hour, min: @minute)
34
- elsif !@minute.nil?
35
- at_time.change(min: @minute)
36
- elsif !@hour.nil? && @hour != time.hour
37
- at_time.change(hour: @hour)
38
- else
39
- at_time.change(sec: 0)
40
- end
44
+ at_time = at_time_day_hour_minute_adjusted(time)
41
45
 
42
- if at_time < (time.change(sec: 0) - @grace)
46
+ grace_cutoff = time.change(sec: 0) - @grace
47
+
48
+ if at_time < grace_cutoff
43
49
  if @wday.nil?
44
- if @hour.nil?
45
- at_time += 1.hour
46
- else
47
- at_time += 1.day
48
- end
50
+ at_time += @hour.nil? ? 1.hour : 1.day
49
51
  else
50
52
  at_time += 1.week
51
53
  end
@@ -54,26 +56,51 @@ module Zhong
54
56
  end
55
57
  end
56
58
 
57
- private def valid?
58
- (@minute.nil? || (0..59).cover?(@minute)) &&
59
- (@hour.nil? || (0..23).cover?(@hour)) &&
60
- (@wday.nil? || (0..6).cover?(@wday))
59
+ def to_s
60
+ str = "#{formatted_time(@hour)}:#{formatted_time(@minute)}"
61
+
62
+ if @wday
63
+ str += " on #{WDAYS.invert[@wday].capitalize}"
64
+ end
65
+
66
+ str
61
67
  end
62
68
 
63
- def self.parse(at, grace: 0.seconds)
64
- return unless at
69
+ def as_json
70
+ {m: @minute, h: @hour, w: @wday, g: @grace}
71
+ end
65
72
 
66
- # TODO: refactor this mess
67
- if at.respond_to?(:each)
68
- return MultiAt.new(at.map { |a| parse(a, grace: grace) })
73
+ def serialize
74
+ MessagePack.pack(as_json)
75
+ end
76
+
77
+ protected
78
+
79
+ def formatted_time(t)
80
+ if t.nil?
81
+ "**"
82
+ else
83
+ t.to_s.rjust(2, "0")
69
84
  end
85
+ end
86
+
87
+ def ==(o)
88
+ o.class == self.class && o.state == state
89
+ end
70
90
 
91
+ def state
92
+ [@minute, @hour, @wday]
93
+ end
94
+
95
+ private
96
+
97
+ def self.parse_at(at, grace)
71
98
  case at
72
99
  when /\A([[:alpha:]]+)\s+(.*)\z/
73
100
  wday = WDAYS[$1.downcase]
74
101
 
75
102
  if wday
76
- parsed_time = parse($2, grace: grace)
103
+ parsed_time = parse_at($2, grace)
77
104
  parsed_time.wday = wday
78
105
  parsed_time
79
106
  else
@@ -88,8 +115,36 @@ module Zhong
88
115
  else
89
116
  fail FailedToParse, at
90
117
  end
91
- rescue ArgumentError
92
- fail FailedToParse, at
118
+ end
119
+
120
+ def self.parse_serialized(at)
121
+ if at.is_a?(Array)
122
+ MultiAt.new(at.map { |a| parse_serialized(a) })
123
+ else
124
+ new(minute: at["m"], hour: at["h"], wday: at["w"], grace: at["g"])
125
+ end
126
+ end
127
+
128
+ def at_time_hour_minute_adjusted(time)
129
+ if @minute && @hour
130
+ time.change(hour: @hour, min: @minute)
131
+ elsif @minute
132
+ time.change(min: @minute)
133
+ elsif @hour && @hour != time.hour
134
+ time.change(hour: @hour)
135
+ else
136
+ time.change(sec: 0)
137
+ end
138
+ end
139
+
140
+ def at_time_day_hour_minute_adjusted(time)
141
+ at_time_hour_minute_adjusted(time) + (@wday ? (@wday - time.wday) : 0).days
142
+ end
143
+
144
+ def valid?
145
+ (@minute.nil? || (0..59).cover?(@minute)) &&
146
+ (@hour.nil? || (0..23).cover?(@hour)) &&
147
+ (@wday.nil? || (0..6).cover?(@wday))
93
148
  end
94
149
  end
95
150
 
@@ -100,8 +155,24 @@ module Zhong
100
155
  @ats = ats
101
156
  end
102
157
 
158
+ def ==(o)
159
+ o.class == self.class && @ats == o.ats
160
+ end
161
+
103
162
  def next_at(time = Time.now)
104
163
  ats.map { |at| at.next_at(time) }.min
105
164
  end
165
+
166
+ def to_s
167
+ ats.map(&:to_s).join(", ")
168
+ end
169
+
170
+ def as_json
171
+ ats.map(&:as_json)
172
+ end
173
+
174
+ def serialize
175
+ MessagePack.pack(as_json)
176
+ end
106
177
  end
107
178
  end