recurrent 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -0,0 +1,136 @@
1
+ Recurrent
2
+ ====================
3
+ Schedule and execute your recurring tasks
4
+
5
+ Installation
6
+ ------------
7
+ gem install recurrent
8
+
9
+ Usage Examples
10
+ --------------
11
+ ###Command Line
12
+ ####Execute a command in the terminal
13
+ recurrent --every 5.seconds --system "say 'Recurrent says Hello'"
14
+ ####Run Ruby code
15
+ recurrent --every 1.hour --ruby "HourlyReport.run"
16
+ ####View command line options
17
+ recurrent --help
18
+
19
+ ###In your Rails app
20
+ Add `gem "recurrent"` to your Gemfile.
21
+
22
+ When `recurrent` is used from the root of your Rails application it will execute its tasks in the context of your environment and have access to your application's classes and methods.
23
+
24
+ Loading from a file
25
+ -------------------
26
+ You can define tasks and configuration info in a file.
27
+
28
+ recurrent --file config/recurrences.rb
29
+
30
+ ###The `every` method
31
+ Use the every method to create tasks.
32
+
33
+ every 30.seconds, :collect_usage_stats, :save => true do
34
+ Statistic.collect
35
+ end
36
+
37
+ ####Frequency
38
+ The first argument is the frequency, it can be an integer in seconds, including ActiveSupport style 2.hours, 3.weeks etc, or an [IceCube::Rule](http://seejohncode.com/ice_cube/)
39
+ ####Name
40
+ The second argument is the name of the task
41
+ ####Options
42
+ The third (optional) argument is options. The options are:
43
+ #####:save
44
+ Whether or not you want to save the return value of the task. Recurrent is data store agnostic, you must configure a method by which to save the return value (see below).
45
+ #####:start_time
46
+ The time your task should start, can be in the future or the past. If not provided it will be set based on the frequency your task is executed.
47
+ ####Block
48
+ The code you want to execute whenever the task runs.
49
+
50
+ ##Configuration
51
+ Configuration options can be set via the `configure` method in a file passed to `recurrent --file` or elsewhere in your app, such as a Rails initializer, via `Recurrent::Configuration`.
52
+
53
+ ###Logging
54
+ ####logger
55
+ Recurrent logs via puts, additionally you may use the logger of your choice by configuring `Recurrent::Configuration.logger` with two arguments and a block. The first argument is the message to be logged. The second is the log level, which will be one of `:info`, `:debug`, or `:warn`. An example using the Rails default logger:
56
+
57
+ configure.logger do |message, log_level|
58
+ RAILS_DEFAULT_LOGGER.send(log_level, message)
59
+ end
60
+
61
+ ###Keeping task execution times consistent
62
+ If you have a task that runs once an hour and you reboot your Recurrent worker 10 minutes before it was scheduled to execute you probably still want it to go off at the time it was scheduled. Letting Recurrent set your task start times partially solves this problem. For example when you create a task that runs every hour its start time is set to the beginning of the current day, and thus runs every hour on the hour. If your task runs every 5 seconds its start time will be set to the beginning of the current minute, and will execute at :00, :05, :10 etc.
63
+
64
+ Task frequencies that don't line up nicely like this will get out of sync. For example a task that is set to run every 23 hours with a start time of midnight will run at 11pm the first day, 10 pm the second day, 9pm the third day etc, but if your Recurrent worker is restarted it will start that process over. To avoid this you need to save your schedules so they can persist between reboots. Recurrent is datastore agnostic so you will need to configure the method by which you will save and load your tasks. If the following methods are configured Recurrent will keep your task execution times consistent.
65
+
66
+ ####save\_task\_schedule
67
+ This method is passed the name of the task being saved, and the schedule which is an instance of [IceCube::Schedule](http://seejohncode.com/ice_cube/). Note that the schedule has a `to_yaml` method. In the block you'll put the code that saves it to your datastore of choice, to later be retrieved by `load_task_schedule`.
68
+
69
+ configure.save_task_schedule do |name, schedule|
70
+ # Code to save the schedule
71
+ end
72
+
73
+ ####load\_task\_schedule
74
+ This method is passed the name of the task to be loaded and must return an instance of [IceCube::Schedule](http://seejohncode.com/ice_cube/). If you saved the schedule as yaml then `IceCube::Schedule.from_yaml(schedule)` may come in handy.
75
+
76
+ configure.load_task_schedule do |name|
77
+ # Code to load the schedule
78
+ end
79
+
80
+ ###Capturing the return value of your task
81
+ If you'd like to capture the return value of your task you'll need to configure the following method.
82
+
83
+ ####save\_task\_schedule
84
+ This method is passed an options hash.
85
+
86
+ configure.save_task_return_value do |options|
87
+ # Code to save the return value
88
+ end
89
+
90
+ ####options
91
+ #####:name
92
+ The name of the task.
93
+ #####:return\_value
94
+ The value returned by your block when the task was executed.
95
+ #####:executed\_at
96
+ The time at which the task was executed.
97
+ #####:executed\_by
98
+ Information about the Recurrent worker that performed the task.
99
+
100
+ ###Handling a slow task
101
+ When a task is scheduled to occur Recurrent first checks to see if the task is still running from a previously scheduled execution. If it is still running then Recurrent does not initiate a new run. If you'd like to anything else, for example send yourself a notification that a task which is supposed to run every 30 seconds is taking longer than 30 seconds to execute, you can configure `Recurrent::Configuration.handle_slow_task`.
102
+
103
+ ####handle\_slow\_task
104
+
105
+ configure.handle_slow_task do |name, current_time, still_running_time|
106
+ # notification code etc here
107
+ end
108
+
109
+ ####name
110
+ The name of the task.
111
+ ####current\_time
112
+ The time the task is scheduled to run at.
113
+ ####still\_running\_time
114
+ The time of the scheduled run that is still executing.
115
+
116
+ ###Dealing with running tasks when exiting the Recurrent worker
117
+ By default when attempting to exit the worker it will wait for all running tasks to finish before exiting.
118
+
119
+ ####wait\_for\_running\_tasks\_on\_exit\_for
120
+ How long to wait before killing tasks that are still running.
121
+
122
+ configure.wait_for_running_tasks_on_exit_for = 10.seconds
123
+
124
+ Submitting an Issue
125
+ -------------------
126
+ We use the [GitHub issue tracker](http://github.com/zencoder/recurrent/issues) to track bugs and
127
+ features. Before submitting a bug report or feature request, check to make sure it hasn't already
128
+ been submitted. You can indicate support for an existing issuse by voting it up. When submitting a
129
+ bug report, please include a [Gist](http://gist.github.com/) that includes a stack trace and any
130
+ details that may be necessary to reproduce the bug, including your gem version, Ruby version, and
131
+ operating system. Ideally, a bug report should include a pull request with failing specs.
132
+
133
+ Copyright
134
+ ---------
135
+ Copyright (c) 2011 [Zencoder](http://zencoder.com)
136
+ See [LICENSE](https://github.com/zencoder/recurrent/blob/master/LICENSE.mkd) for details.
data/bin/recurrent CHANGED
@@ -1,14 +1,20 @@
1
+ #!/usr/bin/env ruby
1
2
  $: << File.expand_path("#{File.dirname(__FILE__)}/../lib")
2
3
  require 'rubygems'
3
4
  require 'trollop'
5
+
6
+ opts = Trollop::options do
7
+ opt :file, "File containing task definitions and configuration", :type => :string
8
+ opt :every, "Frequency to perform a task", :type => :string
9
+ opt :ruby, "Ruby code to execute", :type => :string
10
+ opt :system, "Command to send to the command line", :type => :string
11
+ opt :name, "Name of the task", :default => 'command_line'
12
+ end
13
+
4
14
  begin
5
15
  require 'config/environment'
6
16
  rescue LoadError
7
17
  require 'recurrent'
8
18
  end
9
19
 
10
- opts = Trollop::options do
11
- opt :tasks_file, "File containing task definitions and configuration", :default => 'config/recurrences.rb'
12
- end
13
-
14
- Recurrent::Worker.new(opts[:tasks_file]).start
20
+ Recurrent::Worker.new(opts).start
@@ -1,3 +1,3 @@
1
1
  module Recurrent
2
- VERSION = '0.0.1'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -3,8 +3,21 @@ module Recurrent
3
3
 
4
4
  attr_accessor :scheduler, :logger
5
5
 
6
- def initialize(task_file=nil)
7
- @scheduler = Scheduler.new(task_file)
6
+ def initialize(options={})
7
+ file = options[:file]
8
+ @scheduler = Scheduler.new(file)
9
+ if options[:every]
10
+ every = eval(options[:every]).to_i
11
+ if options[:ruby]
12
+ @scheduler.every(every, options[:name]) do
13
+ eval(options[:ruby])
14
+ end
15
+ elsif options[:system]
16
+ @scheduler.every(every, options[:name]) do
17
+ system(options[:system])
18
+ end
19
+ end
20
+ end
8
21
  @logger = scheduler.logger
9
22
  end
10
23
 
@@ -48,7 +61,7 @@ module Recurrent
48
61
  lock_established = nil
49
62
  until lock_established
50
63
  break if $exit
51
- lock_established = Configuration.process_locking.call('recurrent') do
64
+ lock_established = Configuration.process_locking.call do
52
65
  execute
53
66
  end
54
67
  break if $exit
@@ -93,7 +106,8 @@ module Recurrent
93
106
 
94
107
  def wait_until(time)
95
108
  until time.past?
96
- sleep(0.5) unless $exit
109
+ break if $exit
110
+ sleep(0.5)
97
111
  end
98
112
  end
99
113
 
data/spec/worker_spec.rb CHANGED
@@ -2,6 +2,34 @@ require 'spec_helper'
2
2
 
3
3
  module Recurrent
4
4
  describe Worker do
5
+ describe "#wait_for_running_tasks_indefinitely" do
6
+ context "A worker with no running tasks" do
7
+ before :each do
8
+ @worker = Worker.new
9
+ end
10
+
11
+ it "logs that all tasks are finished and returns true" do
12
+ @worker.logger.should_receive(:info).with("All tasks finished, exiting...")
13
+ @worker.wait_for_running_tasks_indefinitely.should be_true
14
+ end
15
+ end
16
+
17
+ context "A worker with running tasks" do
18
+ before :each do
19
+ @worker = Worker.new
20
+ @task = Task.new :name => 'worker_test'
21
+ @task.thread = Thread.new { sleep 0.1 }
22
+ @worker.scheduler.tasks << @task
23
+ end
24
+
25
+ it "waits for the task to finish and returns true" do
26
+ @worker.logger.should_receive(:info).with("Waiting for worker_test to finish.")
27
+ @worker.logger.should_receive(:info).with("All tasks finished, exiting...")
28
+ @worker.wait_for_running_tasks_indefinitely.should be_true
29
+ end
30
+ end
31
+ end
32
+
5
33
  describe "#wait_until" do
6
34
  it "waits until a specified time" do
7
35
  Timecop.freeze(Time.local(2011, 7, 26, 11, 35, 00))
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recurrent
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 0
9
8
  - 1
10
- version: 0.0.1
9
+ - 0
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Adam Kittelson
@@ -15,8 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-08-05 00:00:00 -07:00
19
- default_executable:
18
+ date: 2011-08-09 00:00:00 Z
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
21
  name: ice_cube
@@ -179,7 +178,6 @@ files:
179
178
  - spec/spec_helper.rb
180
179
  - spec/task_spec.rb
181
180
  - spec/worker_spec.rb
182
- has_rdoc: true
183
181
  homepage: http://github.com/zencoder/recurrent
184
182
  licenses: []
185
183
 
@@ -209,7 +207,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
207
  requirements: []
210
208
 
211
209
  rubyforge_project:
212
- rubygems_version: 1.3.9.2
210
+ rubygems_version: 1.8.6
213
211
  signing_key:
214
212
  specification_version: 3
215
213
  summary: Task scheduler that doesn't need to bootstrap your Rails environment every time it executes a task the way running a rake task via cron does.