lita-task-scheduler 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 23f66b45e3100b728a6a53a23fa9f166552d7989
4
- data.tar.gz: 4b23ad6c9ed0638905152bb6ac960fa98978a47d
3
+ metadata.gz: c87964e095867fd79b29baed0c909bea11c627b8
4
+ data.tar.gz: c594c7eae944748bcd509e50214814edc629f2fe
5
5
  SHA512:
6
- metadata.gz: 468d8b5438c5d815e570d72d14c727b093f7d584104017c459b1c0a72d2ec6097fba77df538f2e02acfccb7e1615c3b7ab130419845363dbd23ffcc12aff929d
7
- data.tar.gz: 969dc2317064912351a8bdf4af531fd0e1095186d4100298587b298c0cf5c027339672e2311ad08481dad6757b178ec8f4df5d897e5dbd54ee54739f4acb6c6e
6
+ metadata.gz: 407c632bfb744874758e04fa94fdc2af7e69cdea4bbd9d13fdc2ec02c3fb1d25031ced5f2596b1f0e3e5c69b2831d4024247193b2062adb469bbe44147da4658
7
+ data.tar.gz: 8c25068482aadcee6efbbab58aa09085167a71d09758516f43efcb0537be13d218b8565e379e090ec591372fd3a174a7abd49274ee9384450373a4f9b612e186
@@ -1,29 +1,41 @@
1
- require 'pry'
2
- require 'json'
1
+ require 'lita/scheduler'
3
2
 
4
3
  module Lita
5
4
  module Handlers
6
5
  class TaskScheduler < Handler
7
6
 
7
+ # START:routes
8
8
  route(/^schedule\s+"(.+)"\s+in\s+(.+)$/i, :schedule_command, command: true)
9
9
  route(/^show schedule$/i, :show_schedule, command: true)
10
10
  route(/^empty schedule$/i, :empty_schedule, command: true)
11
+ # END:routes
11
12
 
12
- REDIS_TASKS_KEY = name.to_s
13
-
13
+ # START:handlers
14
14
  def show_schedule(payload)
15
- schedule = redis.hgetall(REDIS_TASKS_KEY)
16
-
17
- payload.reply schedule_report(schedule)
15
+ payload.reply schedule_report(scheduler.get_all)
18
16
  end
19
17
 
20
18
  def empty_schedule(payload)
21
- redis.del(REDIS_TASKS_KEY)
19
+ scheduler.clear
20
+ show_schedule payload
21
+ end
22
+
23
+ # START:schedule_command
24
+ def schedule_command(payload)
25
+ task, timing = payload.matches.last
26
+ run_at = parse_timing(timing)
27
+ serialized = command_to_hash(payload.message, new_body: task)
28
+
29
+ defer_task(serialized, run_at)
22
30
  show_schedule payload
23
31
  end
32
+ # END:schedule_command
33
+
34
+ def scheduler
35
+ @_schedule ||= Scheduler.new(redis: redis, logger: Lita.logger)
36
+ end
24
37
 
25
38
  def schedule_report(schedule)
26
- reply = 'Scheduled tasks: '
27
39
  descriptions = []
28
40
 
29
41
  schedule.keys.each do |timestamp|
@@ -36,75 +48,16 @@ module Lita
36
48
  end
37
49
  end
38
50
 
39
- reply + (descriptions.empty? ? 'None.' : descriptions.join)
40
- end
41
-
42
- def schedule_command(payload)
43
- task, timing = payload.matches.last
44
- run_at = parse_timing(timing)
45
- serialized = serialize_message(payload.message, new_body: task)
46
-
47
- defer_task(serialized, run_at)
48
- show_schedule(payload)
51
+ 'Scheduled tasks: ' + (descriptions.empty? ? 'None.' : descriptions.join)
49
52
  end
50
53
 
54
+ # START:defer_task
51
55
  def defer_task(serialized_task, run_at)
52
- key_time = run_at.to_i.to_s
53
-
54
- redis.watch(REDIS_TASKS_KEY)
55
-
56
- tasks = redis.hget(REDIS_TASKS_KEY, key_time) || []
57
-
58
- tasks = JSON.parse(tasks) unless tasks.empty?
59
- tasks << serialized_task
60
-
61
- redis.hset(REDIS_TASKS_KEY, key_time, tasks.to_json)
62
-
63
- redis.unwatch
64
-
65
- tasks
66
- end
67
-
68
- def execute_tasks(serialized_tasks)
69
- serialized_tasks.each do |serialized_task|
70
- Lita.logger.debug "Resending task #{serialized_task}"
71
- resend serialized_task
72
- end
73
- end
74
-
75
- def run_loop
76
- Thread.new do
77
- loop do
78
- tick
79
- sleep 1
80
- end
81
- end
82
- end
83
-
84
- def tick
85
- tasks = find_tasks_due
86
- tasks.each { |t| resend t }
87
- Lita.logger.debug "Task loop done for #{Time.now}"
88
- end
89
-
90
- def find_tasks_due
91
- results = []
92
- timestamps = redis.hkeys(REDIS_TASKS_KEY)
93
-
94
- timestamps.each do |t|
95
- key_time = Time.at(t.to_i)
96
- next unless key_time <= Time.now
97
-
98
- tasks_raw = redis.hget(REDIS_TASKS_KEY, t)
99
- tasks = JSON.parse(tasks_raw)
100
-
101
- results += tasks
102
- redis.hdel(REDIS_TASKS_KEY, t)
103
- end
104
-
105
- results
56
+ scheduler.add(serialized_task, run_at)
106
57
  end
58
+ # END:defer_task
107
59
 
60
+ # START:parse_timing
108
61
  def parse_timing(timing)
109
62
  count, unit = timing.split
110
63
  count = count.to_i
@@ -125,22 +78,14 @@ module Lita
125
78
 
126
79
  Time.now.utc + seconds
127
80
  end
81
+ # END:parse_timing
128
82
 
129
- def rebroadcast(payload)
130
- serialized = serialize_message(payload.message)
131
-
132
- key = "delay_#{rand(100..10_000)}"
133
- redis.set(key, serialized.to_json)
134
- reloaded = JSON.parse redis.get(key), symbolize_names: true
135
-
136
- resend(reloaded)
137
- end
138
-
139
- def resend(serialized)
140
- user = Lita::User.new(serialized.fetch('user_name'))
141
- room = Lita::Room.new(serialized.fetch('room_name'))
83
+ # START:resend_command
84
+ def resend_command(command_hash)
85
+ user = Lita::User.new(command_hash.fetch('user_name'))
86
+ room = Lita::Room.new(command_hash.fetch('room_name'))
142
87
  source = Lita::Source.new(user: user, room: room)
143
- body = "#{robot.name} #{serialized.fetch('body')}"
88
+ body = "#{robot.name} #{command_hash.fetch('body')}"
144
89
 
145
90
  newmsg = Lita::Message.new(
146
91
  robot,
@@ -150,20 +95,42 @@ module Lita
150
95
 
151
96
  robot.receive newmsg
152
97
  end
98
+ # END:resend_command
153
99
 
154
- def serialize_message(message, new_body: nil)
100
+ # START:serialize_message
101
+ def command_to_hash(command, new_body: nil)
155
102
  {
156
- user_name: message.user.name,
157
- room_name: message.source.room,
158
- body: new_body || message.body
103
+ user_name: command.user.name,
104
+ room_name: command.source.room,
105
+ body: new_body || command.body
159
106
  }
160
107
  end
108
+ # END:serialize_message
161
109
 
162
- Lita.register_handler(self)
110
+ def find_tasks_due
111
+ scheduler.find_tasks_due
112
+ end
113
+
114
+ # START:loop_ticks
115
+ def run_loop
116
+ Thread.new do
117
+ loop do
118
+ tick
119
+ sleep 1
120
+ end
121
+ end
122
+ end
163
123
 
164
- on :loaded do
165
- run_loop
124
+ def tick
125
+ tasks = find_tasks_due
126
+ tasks.each { |t| resend_command t }
127
+ Lita.logger.debug "Task loop done for #{Time.now}"
166
128
  end
129
+
130
+ on(:loaded) { run_loop }
131
+ # END:loop_ticks
132
+
133
+ Lita.register_handler(self)
167
134
  end
168
135
  end
169
136
  end
@@ -0,0 +1,62 @@
1
+ require 'json'
2
+
3
+ # START:preamble
4
+ module Lita
5
+ class Scheduler
6
+ REDIS_TASKS_KEY = name.to_s
7
+
8
+ def initialize(redis:, logger:)
9
+ @redis = redis
10
+ @logger = logger
11
+ end
12
+
13
+ attr_reader :redis, :logger
14
+
15
+ def get_all
16
+ redis.hgetall(REDIS_TASKS_KEY)
17
+ end
18
+ # END:preamble
19
+
20
+ # START:store_new
21
+ def add(payload, timestamp)
22
+ key_time = timestamp.to_i.to_s
23
+
24
+ redis.watch(REDIS_TASKS_KEY)
25
+
26
+ tasks = redis.hget(REDIS_TASKS_KEY, key_time) || []
27
+
28
+ tasks = JSON.parse(tasks) unless tasks.empty?
29
+ tasks << payload
30
+
31
+ redis.hset(REDIS_TASKS_KEY, key_time, tasks.to_json)
32
+
33
+ redis.unwatch
34
+ tasks
35
+ end
36
+ # END:store_new
37
+
38
+ def clear
39
+ redis.del(REDIS_TASKS_KEY)
40
+ end
41
+
42
+ # START:find_tasks_due
43
+ def find_tasks_due
44
+ results = []
45
+ timestamps = redis.hkeys(REDIS_TASKS_KEY)
46
+
47
+ timestamps.each do |t|
48
+ key_time = Time.at(t.to_i)
49
+ next unless key_time <= Time.now
50
+
51
+ tasks_raw = redis.hget(REDIS_TASKS_KEY, t)
52
+ tasks = JSON.parse(tasks_raw)
53
+
54
+ results += tasks
55
+ redis.hdel(REDIS_TASKS_KEY, t)
56
+ end
57
+
58
+ results
59
+ end
60
+ # END:find_tasks_due
61
+ end
62
+ end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "lita-task-scheduler"
3
- spec.version = "1.0.0"
3
+ spec.version = "1.1.0"
4
4
  spec.authors = ["Daniel J. Pritchett"]
5
5
  spec.email = ["dpritchett@gmail.com"]
6
6
  spec.description = "Schedule jobs in Lita"
@@ -2,13 +2,17 @@ require 'spec_helper'
2
2
 
3
3
  describe Lita::Handlers::TaskScheduler, lita_handler: true do
4
4
  let(:robot) { Lita::Robot.new(registry) }
5
+ before(:suite) { subject.scheduler.clear }
5
6
 
6
7
  subject { described_class.new(robot) }
7
8
 
9
+ # START:routes
8
10
  describe 'routing' do
9
- it { is_expected.to route('Lita schedule "show schedule" in 2 hours') }
11
+ it { is_expected.to route('Lita schedule "double 4" in 2 hours') }
10
12
  it { is_expected.to route('Lita show schedule') }
13
+ it { is_expected.to route('Lita empty schedule') }
11
14
  end
15
+ # END:routes
12
16
 
13
17
  describe 'functionality' do
14
18
 
@@ -27,6 +31,7 @@ describe Lita::Handlers::TaskScheduler, lita_handler: true do
27
31
  end
28
32
  end
29
33
 
34
+ # START: defer_task
30
35
  describe ':defer_task' do
31
36
  it 'defers any single task' do
32
37
  message = { canary_message: Time.now }
@@ -48,7 +53,9 @@ describe Lita::Handlers::TaskScheduler, lita_handler: true do
48
53
  expect(result).to eq([message] * 6)
49
54
  end
50
55
  end
56
+ # END: defer_task
51
57
 
58
+ # START:find_tasks_due
52
59
  describe ':find_tasks_due' do
53
60
  context 'two tasks are scheduled for five seconds ago' do
54
61
  before { 2.times { subject.defer_task('past_task', Time.now - 5) } }
@@ -69,28 +76,23 @@ describe Lita::Handlers::TaskScheduler, lita_handler: true do
69
76
  end
70
77
  end
71
78
  end
79
+ # END:find_tasks_due
72
80
  end
73
81
 
74
- describe 'execute_tasks' do
75
- it 'resends each task' do
76
- tasks = [{}, {}]
77
-
78
- expect(subject).to receive(:resend).exactly(2).times
79
- subject.execute_tasks(tasks)
80
- end
81
- end
82
-
82
+ # START:loop_ticks
83
83
  describe 'tick' do
84
84
  before { subject.stub(:find_tasks_due).and_return ['a_task'] }
85
85
 
86
86
  it 'should find tasks due and resend them' do
87
87
  expect(subject).to receive(:find_tasks_due)
88
- expect(subject).to receive(:resend).with('a_task')
88
+ expect(subject).to receive(:resend_command).with('a_task')
89
89
 
90
90
  subject.tick
91
91
  end
92
92
  end
93
+ # END:loop_ticks
93
94
 
95
+ # START:parse_timing
94
96
  describe ':parse_timing' do
95
97
  def time_drift(time, expected_seconds:)
96
98
  delta = (Time.now.utc - time).abs
@@ -112,4 +114,5 @@ describe Lita::Handlers::TaskScheduler, lita_handler: true do
112
114
  end
113
115
  end
114
116
  end
117
+ # END:parse_timing
115
118
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lita-task-scheduler
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel J. Pritchett
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-07 00:00:00.000000000 Z
11
+ date: 2018-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lita
@@ -121,6 +121,7 @@ files:
121
121
  - Rakefile
122
122
  - lib/lita-task-scheduler.rb
123
123
  - lib/lita/handlers/task_scheduler.rb
124
+ - lib/lita/scheduler.rb
124
125
  - lita-task-scheduler.gemspec
125
126
  - locales/en.yml
126
127
  - spec/lita/handlers/task_scheduler_spec.rb