scheduler_daemon_robertcigan 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+ gemspec
3
+ gem 'eventmachine', '>= 0.12.8'
4
+ gem 'daemons', '>= 1.0.10'
5
+ gem 'rufus-scheduler', '>= 2.0.1'
6
+ gem 'chronic', '>= 0.2.0'
7
+ gem 'activesupport'
data/Gemfile.lock ADDED
@@ -0,0 +1,35 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ scheduler_daemon_robertcigan (1.1.3)
5
+ activesupport
6
+ chronic (>= 0.2.0)
7
+ daemons (>= 1.0.10)
8
+ eventmachine (>= 0.12.8)
9
+ rufus-scheduler (>= 2.0.1)
10
+
11
+ GEM
12
+ remote: http://rubygems.org/
13
+ specs:
14
+ activesupport (3.2.12)
15
+ i18n (~> 0.6)
16
+ multi_json (~> 1.0)
17
+ chronic (0.9.1)
18
+ daemons (1.1.9)
19
+ eventmachine (1.0.0)
20
+ i18n (0.6.2)
21
+ multi_json (1.6.1)
22
+ rufus-scheduler (2.0.17)
23
+ tzinfo (>= 0.3.23)
24
+ tzinfo (0.3.35)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ activesupport
31
+ chronic (>= 0.2.0)
32
+ daemons (>= 1.0.10)
33
+ eventmachine (>= 0.12.8)
34
+ rufus-scheduler (>= 2.0.1)
35
+ scheduler_daemon_robertcigan!
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Steven Soroka
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,146 @@
1
+ Scheduler Daemon
2
+ ================
3
+
4
+ Rails 3+ compatible scheduler daemon (see branches for older versions).
5
+
6
+ Replaces cron/rake pattern of periodically running rake tasks
7
+ to perform maintenance tasks in Rails apps. Scheduler Daemon is made specifically for your Rails app,
8
+ and only loads the environment once, no matter how many tasks run.
9
+
10
+ What's so great about it? Well, I'm glad you asked!
11
+
12
+ - Only loads your Rails environment once on daemon start, not every time a task is run
13
+ - Allows you to easily deploy the scheduled tasks with your Rails app instead of depending on an
14
+ administrator to update crontab
15
+ - Can be installed as a gem or a plugin (I suggest gem)
16
+ - It doesn't use rake or cron!
17
+ - Gets you up and running with your own daemon in under 2 minutes
18
+ - Specially designed to work with your rails app!
19
+
20
+ Setup
21
+ =====
22
+
23
+ Install as a gem or plugin.
24
+
25
+ As a gem, the old-fashioned way:
26
+
27
+ gem install scheduler_daemon
28
+
29
+ As a gem with bundler, add to your ./Gemfile:
30
+
31
+ gem 'scheduler_daemon'
32
+
33
+ I pretty much assume you chose this option below and prefix most commands with "bundle exec"
34
+
35
+ As a plugin: (might be awkward to call the binary to start up the daemon...)
36
+
37
+ script/plugin install git://github.com/ssoroka/scheduler_daemon.git
38
+ # Install required gems
39
+ gem install daemons rufus-scheduler eventmachine chronic -s http://gemcutter.org
40
+
41
+ Optionally generate the default scheduler daemon task for your rails app:
42
+
43
+ script/rails generate scheduler_task MyNewTask
44
+
45
+ which will create an task named:
46
+
47
+ scheduled_tasks/my_new_task.rb
48
+
49
+ Usage
50
+ =====
51
+
52
+ generate a new scheduled task:
53
+
54
+ script/rails generate scheduler_task MyTaskName
55
+
56
+ If you have problems with that, the template for new tasks is in the gem under:
57
+
58
+ lib/scheduler_daemon/rails/generators/scheduler_task/templates/scheduled_tasks/example_task.rb
59
+
60
+ you can always copy it and make modifications, or see "Manually create tasks" below.
61
+
62
+ Tasks support their own special DSL; commands are:
63
+
64
+ environments :production, :staging # run only in environments listed. (:all by default)
65
+ every '1d' # run every day
66
+ every '1d', :first_at => Chronic.parse("2 am") # run every day, starting at 2 am (see caveat below)
67
+ at Cronic.parse('5 pm') # run *once* at 5 pm today
68
+ # (relative to scheduler start/restart time )
69
+ # (happens every time scheduler starts/restarts)
70
+ # (see caveat below )
71
+ cron '* 4 * * *' # cron style (run every 4 am)
72
+ in '30s' # run once, 30 seconds from scheduler start/restart
73
+
74
+ fire up the daemon in console mode to test it out
75
+
76
+ bundle exec scheduler_daemon run
77
+
78
+ For production environments, add the daemon to the system start-up, and
79
+ capistrano deploy scripts, etc. Something like:
80
+
81
+ export RAILS_ENV=production
82
+ bundle exec scheduler_daemon start
83
+
84
+ Selectively run tasks like so:
85
+
86
+ bundle exec scheduler_daemon start -- --only=task_name1,task_name2 --except=not_me
87
+
88
+ Manually create tasks
89
+ =====================
90
+
91
+ If you don't want to use this gem with Rails, you can manually create tasks in a scheduled_tasks/ subdirectory and start the daemon with --skip-rails (though it'll figure it out anyway if there's no config/environment.rb file in the launch directory or --dir=/path)
92
+
93
+ Here's an example task file.
94
+
95
+ class CleanUpTask < Scheduler::SchedulerTask
96
+ every '2m'
97
+
98
+ def run
99
+ do_something
100
+ log("I've done things")
101
+ end
102
+ end
103
+
104
+ Specs
105
+ =====
106
+
107
+ See the spec for session cleaner for an idea on how to write specs for your tasks
108
+
109
+ To Do
110
+ =====
111
+
112
+ Looking for suggestions!
113
+
114
+ Send requests to ssoroka78@gmail.com or on twitter, @ssoroka
115
+
116
+ Bugs
117
+ ====
118
+
119
+ Submit bugs here http://github.com/ssoroka/scheduler_daemon/issues
120
+
121
+ Caveats
122
+ =======
123
+
124
+ When using the cronic gem to parse dates, be careful of how it interprets your date,
125
+ for example:
126
+
127
+ every '24h', :first_at => Chronic.parse('noon')
128
+
129
+ will be once a day at noon, but the first time the server starts up (or restarts), noon
130
+ is relative to the current time of day. Before lunch, and it's in the future. If the
131
+ daemon starts up after lunch, the date is in the past, *and the task is immediately run*
132
+ because it thinks it missed its last execution time. Depending on what your task is,
133
+ this may or may not be a problem. If you always want the date to resolve in the future
134
+ with terms like "noon", "3 am" and "midnight", prepend "next" to it. ie:
135
+
136
+ every '24h', :first_at => Chronic.parse('next noon')
137
+
138
+ Author
139
+ ======
140
+
141
+ Steven Soroka
142
+
143
+ * [@ssoroka](http://twitter.com/ssoroka)
144
+ * [My Github repo](http://github.com/ssoroka)
145
+ * [My blog](http://blog.stevensoroka.ca)
146
+
data/Rakefile ADDED
@@ -0,0 +1,71 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "scheduler_daemon"
8
+ gem.summary = %Q{Rails 3 compatible scheduler daemon. Replaces cron/rake pattern of periodically running rake tasks
9
+ to perform maintenance tasks in Rails apps. Scheduler Daemon is made specifically for your Rails app,
10
+ and only loads the environment once, no matter how many tasks run.
11
+
12
+ What's so great about it? Well, I'm glad you asked!
13
+
14
+ - Only loads your Rails environment once on daemon start, not every time a task is run
15
+ - Allows you to easily deploy the scheduled tasks with your Rails app instead of depending on an
16
+ administrator to update crontab
17
+ - It doesn't use rake or cron!
18
+ - Gets you up and running with your own daemon in under 2 minutes
19
+ }
20
+ gem.version = File.read('VERSION').chomp
21
+ gem.description = 'a Rails 2.3, Rails 3, and Ruby compatible scheduler daemon. Replaces cron/rake pattern of periodically running rake tasks to perform maintenance tasks, only loading the environment ONCE.'
22
+ gem.email = "ssoroka78@gmail.com"
23
+ gem.homepage = "http://github.com/ssoroka/scheduler_daemon"
24
+ gem.authors = ["Steven Soroka"]
25
+ gem.add_dependency('eventmachine', '>= 0.12.8')
26
+ gem.add_dependency('daemons', '>= 1.0.10')
27
+ gem.add_dependency('rufus-scheduler', '>= 2.0.1')
28
+ gem.add_dependency('chronic', '>= 0.2.0')
29
+
30
+ gem.executables = ['scheduler_daemon']
31
+
32
+ everything_from_dirs = %w(bin lib spec)
33
+ gem.files = everything_from_dirs.map{|d| Dir["#{d}/**/*"] }.flatten
34
+ gem.files += Dir['*'] - (everything_from_dirs + ['pkg'])
35
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
36
+ end
37
+
38
+ rescue LoadError
39
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
40
+ end
41
+
42
+ # require 'spec/rake/spectask'
43
+ # Spec::Rake::SpecTask.new(:spec) do |spec|
44
+ # spec.libs << 'lib' << 'spec'
45
+ # spec.spec_files = FileList['spec/**/*_spec.rb']
46
+ # end
47
+ #
48
+ # Spec::Rake::SpecTask.new(:rcov) do |spec|
49
+ # spec.libs << 'lib' << 'spec'
50
+ # spec.pattern = 'spec/**/*_spec.rb'
51
+ # spec.rcov = true
52
+ # end
53
+ #
54
+ #
55
+ # task :default => :spec
56
+ #
57
+ # require 'rake/rdoctask'
58
+ # Rake::RDocTask.new do |rdoc|
59
+ # if File.exist?('VERSION.yml')
60
+ # config = YAML.load(File.read('VERSION.yml'))
61
+ # version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
62
+ # else
63
+ # version = ""
64
+ # end
65
+ #
66
+ # rdoc.rdoc_dir = 'rdoc'
67
+ # rdoc.title = "scheduler_daemon #{version}"
68
+ # rdoc.rdoc_files.include('README*')
69
+ # rdoc.rdoc_files.include('lib/**/*.rb')
70
+ # end
71
+ #
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.2
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+ # This file launches the scheduler as a daemon.
3
+ # USAGE:
4
+ #
5
+ # scheduler_daemon run # start the daemon and stay on top
6
+ # scheduler_daemon start # start the daemon and stay on top
7
+ # scheduler_daemon stop # stop all instances of the application
8
+ # scheduler_daemon restart # stop all instances and restart them afterwards
9
+ #
10
+ # options can be passed to the scheduler like so:
11
+ #
12
+ # scheduler_daemon start -- --except=session_cleaner
13
+ #
14
+ # options can be passed to the daemon:
15
+ #
16
+ # scheduler_daemon start --dir=/my/rails/root/
17
+ #
18
+ # see README for more info
19
+ require 'rubygems'
20
+ require 'daemons'
21
+ require 'scheduler_daemon/command_line_args_to_hash'
22
+
23
+ # arguments to pass to the daemon launcher
24
+ def launch_args(options = {})
25
+ params = options.map{|k,v| "--#{k}=#{v}"}
26
+ if params.empty?
27
+ ARGV
28
+ else
29
+ args = ARGV.dup
30
+ args << '--' unless args.include?('--')
31
+ args + params
32
+ end
33
+ end
34
+
35
+ # app arguments
36
+ app_args = {}
37
+ # only take args after the '--' arg.
38
+ if separator_index = ARGV.index('--')
39
+ app_args = CommandLineArgsToHash.parse(ARGV[separator_index+1..-1])
40
+ end
41
+
42
+ app_args[:dir] ||= Dir.pwd
43
+ app_args[:pid_dir] ||= File.expand_path(File.join(app_args[:dir], 'log'))
44
+ scheduler = File.join(File.dirname(__FILE__), %w(.. lib loader scheduler_loader.rb))
45
+
46
+ raise "#{pid_dir} does not exist" unless File.exist?(app_args[:pid_dir])
47
+
48
+ app_options = {
49
+ :app_name => 'scheduler_daemon',
50
+ :ARGV => launch_args(app_args),
51
+ :dir_mode => :normal,
52
+ :dir => app_args[:pid_dir],
53
+ :multiple => false,
54
+ :backtrace => true,
55
+ :log_output => true,
56
+ :monitor => true
57
+ }
58
+
59
+ Daemons.run(scheduler, app_options)
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # This file loads the rails environment and starts the scheduler.
3
+ # do not use it directly unless you don't intend for the scheduler to run as a daemon.
4
+ require 'scheduler_daemon/command_line_args_to_hash'
5
+ args = CommandLineArgsToHash.parse(ARGV)
6
+ daemons_lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
7
+
8
+ Dir.chdir(args[:dir])
9
+
10
+ require File.join(daemons_lib_dir, 'scheduler_daemon', 'base')
11
+
12
+ Scheduler::Base.new(args)
@@ -0,0 +1,2 @@
1
+ require 'scheduler_daemon/rails/railtie' if defined?(Rails) && Rails.version =~ /^3\./
2
+ require 'scheduler_daemon/base'
@@ -0,0 +1,149 @@
1
+ require 'eventmachine'
2
+ require 'rufus/scheduler'
3
+ begin
4
+ require 'active_support/hash_with_indifferent_access'
5
+ rescue LoadError
6
+ begin
7
+ require 'active_support/core_ext/hash'
8
+ rescue LoadError
9
+ puts "can't load activesupport gem not loaded"
10
+ raise $!
11
+ end
12
+ end
13
+
14
+ require 'scheduler_daemon/scheduler_task'
15
+ require 'scheduler_daemon/exception_handler'
16
+ require 'scheduler_daemon/command_line_args_to_hash'
17
+
18
+ module Scheduler
19
+ class Base
20
+ attr_reader :tasks, :options, :env_name
21
+
22
+ # :root_dir is a required option, because the scheduler likely has no idea what the original
23
+ # root directory was after the process was daemonized (which changes the current directory)
24
+ # :silent
25
+ # doesn't output anything to STDOUT or logs.
26
+ # :root_dir
27
+ # root dir of the application.
28
+ # :only
29
+ # load only tasks in this list
30
+ # :except
31
+ # load all tasks except ones in this list
32
+ # :env_name
33
+ # if used without rails, you can manually set something in place of the environment name
34
+ def initialize(opts = {}, command_line_args = [])
35
+ @options = ::HashWithIndifferentAccess.new(opts)
36
+ @options.merge!(CommandLineArgsToHash.parse(command_line_args, :array_args => ['only', 'except']))
37
+ @options['only'] ||= []
38
+ @options['except'] ||= []
39
+ @env_name = @options['env_name'] || 'scheduler'
40
+ @rufus_scheduler = nil
41
+ @tasks = []
42
+
43
+ log("initialized with settings: #{@options.inspect}")
44
+
45
+ if !@options['skip_init']
46
+ load_rails_env
47
+ load_tasks
48
+ run_scheduler
49
+ end
50
+ end
51
+
52
+ # registers a task class with the scheduler
53
+ def register_task(task)
54
+
55
+ end
56
+
57
+ def load_rails_env
58
+ if File.exists?('config/environment.rb') && !@options['skip_rails']
59
+ log("loading rails environment")
60
+ require File.expand_path('./config/environment')
61
+ @env_name = ::Rails.env
62
+ end
63
+ rescue
64
+ log("Error loading rails environment; #{$!.class.name}: #{$!.message}")
65
+ raise $!
66
+ end
67
+
68
+ # time redefines itself with a faster implementation, since it gets called a lot.
69
+ def time
70
+ if Time.respond_to?(:zone) && Time.zone
71
+ self.class.send(:define_method, :time) { Time.zone.now.to_s }
72
+ else
73
+ self.class.send(:define_method, :time) { Time.now.to_s }
74
+ end
75
+ time
76
+ end
77
+
78
+ def log(*args)
79
+ return if @options[:silent]
80
+ Kernel::puts(%([#{time}] #{args.join("\n")}))
81
+ end
82
+ alias :puts :log
83
+
84
+ def run_scheduler
85
+ if defined?(::Rails)
86
+ log "Starting Scheduler in #{::Rails.env}"
87
+ else
88
+ log "Starting Scheduler"
89
+ end
90
+
91
+ $daemon_scheduler = self
92
+
93
+ EventMachine::run {
94
+ @rufus_scheduler = Rufus::Scheduler::EmScheduler.start_new
95
+
96
+ def @rufus_scheduler.handle_exception(job, exception)
97
+ msg = "[#{env_name}] scheduler job #{job.job_id} (#{job.tags * ' '}) caught exception #{exception.inspect}"
98
+ log msg
99
+ log exception.backtrace.join("\n")
100
+ Scheduler::ExceptionHandler.handle_exception(exception, job, message)
101
+ end
102
+
103
+ def @rufus_scheduler.daemon_scheduler
104
+ $daemon_scheduler
105
+ end
106
+
107
+ # This is where the magic happens. tasks in scheduled_tasks/*.rb are loaded up.
108
+ tasks.each do |task|
109
+ if task.should_run_in_current_environment?(env_name)
110
+ task.add_to(@rufus_scheduler)
111
+ else
112
+ log "[#{env_name}] #{task} not configured to run; skipping."
113
+ end
114
+ end
115
+ }
116
+ end
117
+
118
+ def load_tasks
119
+ tasks_to_run.each{|f|
120
+ begin
121
+ unless options[:only].any? && options[:only].all?{|m| f !~ Regexp.new(Regexp.escape(m)) }
122
+ require File.expand_path("./#{f}")
123
+ filename = f.split('/').last.split('.').first
124
+ log "Loading task #{filename}..."
125
+ @tasks << filename.camelcase.constantize # "path/newsfeed_task.rb" => NewsfeedTask
126
+ end
127
+ rescue Exception => e
128
+ msg = "Error loading task #{filename}: #{e.class.name}: #{e.message}"
129
+ log msg
130
+ log e.backtrace.join("\n")
131
+ Scheduler::ExceptionHandler.handle_exception(e, nil, msg)
132
+ end
133
+ }
134
+ end
135
+
136
+ def tasks_to_run
137
+ task_files = Dir[File.join(%w(scheduled_tasks *.rb))]# + File.join(%w(lib scheduled_tasks *.rb))
138
+
139
+ if options[:only].any?
140
+ task_files.reject!{|f| options[:only].all?{|m| f !~ Regexp.new(Regexp.escape(m))}}
141
+ end
142
+
143
+ if options[:except].any?
144
+ task_files.reject!{|f| options[:except].any?{|m| f =~ Regexp.new(Regexp.escape(m))}}
145
+ end
146
+ task_files
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,47 @@
1
+ begin
2
+ require 'active_support/hash_with_indifferent_access'
3
+ rescue LoadError
4
+ begin
5
+ require 'active_support/core_ext/hash'
6
+ rescue LoadError
7
+ puts "can't load activesupport gem not loaded"
8
+ raise $!
9
+ end
10
+ end
11
+
12
+ class CommandLineArgsToHash
13
+ def self.parse(args, options = {})
14
+ new(args, options).parse
15
+ end
16
+
17
+ def initialize(args, options = {})
18
+ @args = args
19
+ @options = options
20
+ end
21
+
22
+ def parse
23
+ hash = ::HashWithIndifferentAccess.new
24
+ @args.each{|arg|
25
+ k, v = read_argument(arg)
26
+ hash[k] = v
27
+ }
28
+ hash
29
+ end
30
+
31
+ def read_argument(arg)
32
+ k, v = arg.sub(/^\-\-/, '').split('=')
33
+ k = k.gsub(/-/, '_') # replace - to _ so that --skip-init becomes options[:skip_init]
34
+ v = true if v.nil? # default passed in args to true.
35
+ v = format_value(k, v)
36
+ [k, v]
37
+ end
38
+
39
+ def format_value(k, v)
40
+ if @options[:array_args] && @options[:array_args].include?(k)
41
+ v = v.split(/,\s*/) if v.respond_to?(:split)
42
+ end
43
+ v = false if v == 'false'
44
+ v = true if v == 'true'
45
+ v
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ # override this in your app to do something meaningful,
2
+ # like post to campfire or hoptoad.
3
+ #
4
+ # Example campfire code below, using 'tinder' gem:
5
+ module Scheduler
6
+ class ExceptionHandler
7
+ # @@campfire_subdomain = ''
8
+ # @@campfire_username = ''
9
+ # @@campfire_password = ''
10
+ # @@campfire_room_name = ''
11
+ # @@campfire_room = nil
12
+
13
+ def self.handle_exception(exception, job, message)
14
+ # If your team all hangs out in Campfire, you might want to try
15
+ # something like Tinder here to write these messages out to campfire,
16
+ # Such as:
17
+ #
18
+ # if Rails.env.production? || Rails.env.staging?
19
+ # msg = "#{message}, see log for backtrace"
20
+ #
21
+ # unless @@campfire_room
22
+ # campfire = Tinder::Campfire.new(@@campfire_subdomain, :ssl => true)
23
+ # campfire.login(@@campfire_username, @@campfire_password)
24
+ # @@campfire_room = campfire.find_room_by_name(@@campfire_room_name)
25
+ # end
26
+ # @@campfire_room.speak(msg)
27
+ # end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,6 @@
1
+ Description:
2
+ Creates the scheduler folder and basic scripts for it to run
3
+
4
+ Example:
5
+ `./script/generate scheduler`
6
+
@@ -0,0 +1,16 @@
1
+ class SchedulerGenerator < Rails::Generator::Base
2
+ def banner
3
+ "Usage: #{$0} #{spec.name}"
4
+ end
5
+
6
+ def manifest
7
+ record do |m|
8
+ m.directory File.join('lib', 'scheduled_tasks')
9
+
10
+ m.template 'lib/scheduled_tasks/session_cleaner_task.rb',
11
+ 'lib/scheduled_tasks/session_cleaner_task.rb'
12
+
13
+ m.readme('README')
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+
2
+ GREAT!
3
+
4
+ Now that you've generated the scheduler daemon and a default task,
5
+ you can take a look at the new task:
6
+
7
+ lib/scheduled_tasks/session_cleaner_task.rb
8
+
9
+ or make your own:
10
+
11
+ script/generate scheduler_task MyTaskName
@@ -0,0 +1,29 @@
1
+ class SessionCleanerTask < Scheduler::SchedulerTask
2
+ environments :all
3
+
4
+ every '1d', :first_at => Chronic.parse('next 2 am')
5
+
6
+ def run
7
+ remove_old_sessions
8
+ end
9
+
10
+ def remove_old_sessions
11
+ log "running the session cleaner"
12
+ if ActionController::Base.session_store == session_store_class
13
+ ActiveRecord::Base.connection.execute("DELETE FROM #{session_table_name} WHERE updated_at < '#{7.days.ago.to_s(:db)}'")
14
+ log "old sessions are gone!"
15
+ else
16
+ log "sessions are not stored in the database; nothing to do."
17
+ end
18
+ end
19
+
20
+ def session_store_class
21
+ return ActiveRecord::SessionStore if defined?(ActiveRecord::SessionStore)
22
+ # pre rails 2.3 support...
23
+ return CGI::Session::ActiveRecordStore if defined?(CGI::Session::ActiveRecordStore)
24
+ end
25
+
26
+ def session_table_name
27
+ ActiveRecord::Base.pluralize_table_names ? :sessions : :session
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ class SchedulerTaskGenerator < Rails::Generators::NamedBase
2
+ check_class_collision :suffix => 'Task'
3
+
4
+ def create_task
5
+ template File.join(source_dir, 'example_task.rb'), "scheduled_tasks/#{file_name}_task.rb"
6
+ readme(File.join(template_dir, 'README'))
7
+ end
8
+
9
+ def self.source_root
10
+ File.dirname(File.expand_path(__FILE__))
11
+ end
12
+
13
+ private
14
+ def source_dir
15
+ File.join(template_dir, 'scheduled_tasks')
16
+ end
17
+
18
+ def template_dir
19
+ File.join(File.dirname(__FILE__), 'templates')
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+
2
+ Awesome! Now that you've created a scheduler, you can test it out with:
3
+
4
+ bundle exec scheduler_daemon run
5
+
6
+ to run it in the background (a la system start-up):
7
+
8
+ bundle exec scheduler_daemon start
9
+
10
+
11
+ You'll probably want to add tests or specs for your new task.
12
+ You can find existing specs for scheduler_daemon in the scheduler_daemon/spec folder.
13
+
14
+ Be sure to copy them to your spec folder if you're using rspec.
@@ -0,0 +1,19 @@
1
+ class <%= class_name %>Task < Scheduler::SchedulerTask
2
+ environments :all
3
+ # environments :staging, :production
4
+
5
+ every '10s'
6
+ # other examples:
7
+ # every '24h', :first_at => Chronic.parse('next midnight')
8
+ # cron '* 4 * * *' # cron style
9
+ # in '30s' # run once, 30 seconds from scheduler start/restart
10
+
11
+
12
+ def run
13
+ # Your code here, eg:
14
+ # User.send_due_invoices!
15
+
16
+ # use log() for writing to scheduler daemon log
17
+ log("I've sent invoices!")
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ module Scheduler
2
+ module Rails
3
+ class Railtie < ::Rails::Railtie
4
+ generators do
5
+ require 'scheduler_daemon/rails/generators/scheduler_task/scheduler_task_generator'
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,78 @@
1
+ module Scheduler
2
+ class SchedulerTask
3
+ attr_accessor :daemon_scheduler, :rufus_scheduler
4
+ class <<self
5
+ def add_to(schedule)
6
+ %w(every in at cron).each{|time_cmd|
7
+ if args = self.instance_variable_get("@#{time_cmd}")
8
+ # add a default tag to the arguments so we know which task was running
9
+ options = args.extract_options!
10
+ options.merge!(:tags => [name])
11
+ args << options
12
+
13
+ schedule.send(time_cmd, *args) do
14
+ begin
15
+ a_task = new
16
+ a_task.daemon_scheduler = schedule.daemon_scheduler
17
+ a_task.rufus_scheduler = schedule
18
+ a_task.run
19
+ ensure
20
+ # Note: AR's ActiveRecord::Base.connection_pool.with_connection(&block) seems broken;
21
+ # it doesn't release the connection properly.
22
+ ActiveRecord::Base.connection_pool.release_connection
23
+ end
24
+ end
25
+ end
26
+ }
27
+ end
28
+
29
+ # run the task every... '5m', etc. see rufus-scheduler docs
30
+ def every(*args)
31
+ @every = args
32
+ end
33
+
34
+ # run the task in '30s', etc. see rufus-scheduler docs
35
+ def in(*args)
36
+ @in = args
37
+ end
38
+
39
+ # run the task at... Cronic.parse('5 pm'), etc. see rufus-scheduler docs
40
+ def at(*args)
41
+ @at = args
42
+ end
43
+
44
+ # run the task cron '* 4 * * *', etc. see rufus-scheduler docs
45
+ def cron(*args)
46
+ @cron = args
47
+ end
48
+
49
+ # what environments should this task run in?
50
+ # accepts the usual :development, :production, as well as :all
51
+ #
52
+ # examples:
53
+ # environments :all
54
+ # environments :staging, :production
55
+ # environments :development
56
+ #
57
+ def environments(*args)
58
+ @environments = args.map{|arg| arg.to_sym }
59
+ end
60
+
61
+ def should_run_in_current_environment?(env)
62
+ @environments.nil? || @environments == [:all] || @environments.include?(env.to_sym)
63
+ end
64
+ end
65
+
66
+ # override me to do stuff
67
+ def run
68
+ nil
69
+ end
70
+
71
+ def log(*args)
72
+ daemon_scheduler.log(*args)
73
+ end
74
+ alias :puts :log
75
+ end
76
+ end
77
+ # alias this for backwards compatability
78
+ # SchedulerTask = Scheduler::SchedulerTask unless defined?(::SchedulerTask)
@@ -0,0 +1,82 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{scheduler_daemon_robertcigan}
8
+ s.version = "1.1.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Steven Soroka"]
12
+ s.date = %q{2013-02-26}
13
+ s.description = %q{a Rails 2.3, Rails 3, and Ruby compatible scheduler daemon. Replaces cron/rake pattern of periodically running rake tasks to perform maintenance tasks, only loading the environment ONCE.}
14
+ s.email = %q{ssoroka78@gmail.com}
15
+ s.executables = ["scheduler_daemon"]
16
+ s.extra_rdoc_files = [
17
+ "README.markdown"
18
+ ]
19
+ s.files = [
20
+ "Gemfile",
21
+ "Gemfile.lock",
22
+ "MIT-LICENSE",
23
+ "README.markdown",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "bin/scheduler_daemon",
27
+ "lib/loader/scheduler_loader.rb",
28
+ "lib/scheduler_daemon.rb",
29
+ "lib/scheduler_daemon/base.rb",
30
+ "lib/scheduler_daemon/command_line_args_to_hash.rb",
31
+ "lib/scheduler_daemon/exception_handler.rb",
32
+ "lib/scheduler_daemon/rails/generators/scheduler/USAGE",
33
+ "lib/scheduler_daemon/rails/generators/scheduler/scheduler_generator.rb",
34
+ "lib/scheduler_daemon/rails/generators/scheduler/templates/README",
35
+ "lib/scheduler_daemon/rails/generators/scheduler/templates/lib/scheduled_tasks/session_cleaner_task.rb",
36
+ "lib/scheduler_daemon/rails/generators/scheduler_task/scheduler_task_generator.rb",
37
+ "lib/scheduler_daemon/rails/generators/scheduler_task/templates/README",
38
+ "lib/scheduler_daemon/rails/generators/scheduler_task/templates/scheduled_tasks/example_task.rb",
39
+ "lib/scheduler_daemon/rails/railtie.rb",
40
+ "lib/scheduler_daemon/scheduler_task.rb",
41
+ "scheduler_daemon_robertcigan.gemspec",
42
+ "spec/command_line_args_to_hash_spec.rb",
43
+ "spec/scheduled_tasks/session_cleaner_task_spec.rb",
44
+ "spec/scheduler_spec.rb",
45
+ "spec/spec_helper.rb"
46
+ ]
47
+ s.homepage = %q{http://github.com/ssoroka/scheduler_daemon}
48
+ s.require_paths = ["lib"]
49
+ s.rubygems_version = %q{1.7.2}
50
+ s.summary = %q{Rails 3 compatible scheduler daemon. Replaces cron/rake pattern of periodically running rake tasks to perform maintenance tasks in Rails apps. Scheduler Daemon is made specifically for your Rails app, and only loads the environment once, no matter how many tasks run. What's so great about it? Well, I'm glad you asked! - Only loads your Rails environment once on daemon start, not every time a task is run - Allows you to easily deploy the scheduled tasks with your Rails app instead of depending on an administrator to update crontab - It doesn't use rake or cron! - Gets you up and running with your own daemon in under 2 minutes}
51
+ s.test_files = [
52
+ "spec/command_line_args_to_hash_spec.rb",
53
+ "spec/scheduled_tasks/session_cleaner_task_spec.rb",
54
+ "spec/scheduler_spec.rb",
55
+ "spec/spec_helper.rb"
56
+ ]
57
+
58
+ if s.respond_to? :specification_version then
59
+ s.specification_version = 3
60
+
61
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
62
+ s.add_runtime_dependency(%q<eventmachine>, [">= 0.12.8"])
63
+ s.add_runtime_dependency(%q<daemons>, [">= 1.0.10"])
64
+ s.add_runtime_dependency(%q<rufus-scheduler>, [">= 2.0.1"])
65
+ s.add_runtime_dependency(%q<chronic>, [">= 0.2.0"])
66
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
67
+ else
68
+ s.add_dependency(%q<eventmachine>, [">= 0.12.8"])
69
+ s.add_dependency(%q<daemons>, [">= 1.0.10"])
70
+ s.add_dependency(%q<rufus-scheduler>, [">= 2.0.1"])
71
+ s.add_dependency(%q<chronic>, [">= 0.2.0"])
72
+ s.add_dependency(%q<activesupport>, [">= 0"])
73
+ end
74
+ else
75
+ s.add_dependency(%q<eventmachine>, [">= 0.12.8"])
76
+ s.add_dependency(%q<daemons>, [">= 1.0.10"])
77
+ s.add_dependency(%q<rufus-scheduler>, [">= 2.0.1"])
78
+ s.add_dependency(%q<chronic>, [">= 0.2.0"])
79
+ s.add_dependency(%q<activesupport>, [">= 0"])
80
+ end
81
+ end
82
+
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+ require 'scheduler_daemon/command_line_args_to_hash'
3
+
4
+ describe CommandLineArgsToHash do
5
+ it 'should process arguments' do
6
+ h = CommandLineArgsToHash.parse('--hello')
7
+ h['hello'].should == true
8
+ end
9
+
10
+ it 'should change key names with - to _' do
11
+ h = CommandLineArgsToHash.parse(['--hi-there'])
12
+ h['hi_there'].should == true
13
+ end
14
+
15
+ it 'should play nice with array args' do
16
+ h = CommandLineArgsToHash.parse(['--only=one,two,three'], :array_args => 'only')
17
+ h['only'].should == %w(one two three)
18
+ end
19
+
20
+ it 'should handle multiple args ok' do
21
+ h = CommandLineArgsToHash.parse(['--one', '--two=three'])
22
+ h['one'].should == true
23
+ h['two'].should == 'three'
24
+ end
25
+
26
+ end
@@ -0,0 +1,35 @@
1
+ # copy this spec to your project after installing the plugin if you want to run the specs.
2
+ # suggested file name for this file: spec/scheduler_daemon/scheduled_tasks/session_cleaner_task_spec.rb
3
+
4
+ # require File.join(File.dirname(__FILE__), %w(.. spec_helper))
5
+ require 'spec_helper'
6
+ require 'scheduler_daemon/scheduler_task'
7
+ require 'scheduler_daemon/rails/generators/scheduler/templates/lib/scheduled_tasks/session_cleaner_task'
8
+
9
+ describe SessionCleanerTask do
10
+ before(:each) do
11
+ @task = SessionCleanerTask.new
12
+ @task.stub!(:log)
13
+ end
14
+
15
+ it "should remove old sessions" do
16
+ # this test only matters if we're using AR's session store.
17
+ if defined?(ActionController) && defined?(ActiveRecord) &&
18
+ ActionController::Base.session_store == ActiveRecord::SessionStore
19
+
20
+ # insert old session
21
+ ActiveRecord::Base.connection.execute(%(delete from #{@task.session_table_name} where session_id = 'abc123'))
22
+ ActiveRecord::Base.connection.execute(%(insert into #{@task.session_table_name} (session_id, updated_at) values ('abc123', '#{2.years.ago.to_s(:db)}')))
23
+
24
+ get_session_count = lambda {
25
+ ActiveRecord::Base.connection.select_one(%(select count(*) as count from #{@task.session_table_name}))['count'].to_i
26
+ }
27
+
28
+ lambda {
29
+ @task.run
30
+ }.should change(get_session_count, :call).by_at_most(-1)
31
+ else
32
+ pending 'skipping SessionCleanerTask test since it depends on rails and ActiveRecord::SessionStore; try copying this spec to your rails application.'
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'scheduler_daemon/base'
3
+
4
+ describe Scheduler::Base do
5
+ before(:each) do
6
+ @scheduler = Scheduler::Base.new(:skip_init => true, :silent => true)
7
+ @scheduler.stub!(:log)
8
+ end
9
+
10
+ describe 'decide_what_to_run' do
11
+ it "should support --only" do
12
+ @scheduler = Scheduler::Base.new({:skip_init => true, :silent => true}, ['--only=alphabets'])
13
+ @scheduler.options['only'].should == ['alphabets']
14
+ end
15
+
16
+ it "should support --except" do
17
+ @scheduler = Scheduler::Base.new({:skip_init => true, :silent => true}, ['--except=balloons,monkeys'])
18
+ @scheduler.options['except'].should == ['balloons', 'monkeys']
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ spec_runner = nil
3
+ begin
4
+ require 'rspec'
5
+ spec_runner = RSpec
6
+ rescue
7
+ require 'spec'
8
+ spec_runner = Spec::Runner
9
+ end
10
+ require 'chronic'
11
+ require 'active_support'
12
+
13
+ RAILS_ENV = 'test' unless defined?(RAILS_ENV) && RAILS_ENV == 'test'
14
+
15
+ spec_runner.configure do |config|
16
+ end
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scheduler_daemon_robertcigan
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Steven Soroka
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-26 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: eventmachine
16
+ requirement: &70144495925840 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.12.8
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70144495925840
25
+ - !ruby/object:Gem::Dependency
26
+ name: daemons
27
+ requirement: &70144495922360 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.10
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70144495922360
36
+ - !ruby/object:Gem::Dependency
37
+ name: rufus-scheduler
38
+ requirement: &70144495919080 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 2.0.1
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70144495919080
47
+ - !ruby/object:Gem::Dependency
48
+ name: chronic
49
+ requirement: &70144495918340 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.2.0
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70144495918340
58
+ - !ruby/object:Gem::Dependency
59
+ name: activesupport
60
+ requirement: &70144495917540 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *70144495917540
69
+ description: a Rails 2.3, Rails 3, and Ruby compatible scheduler daemon. Replaces
70
+ cron/rake pattern of periodically running rake tasks to perform maintenance tasks,
71
+ only loading the environment ONCE.
72
+ email: ssoroka78@gmail.com
73
+ executables:
74
+ - scheduler_daemon
75
+ extensions: []
76
+ extra_rdoc_files:
77
+ - README.markdown
78
+ files:
79
+ - Gemfile
80
+ - Gemfile.lock
81
+ - MIT-LICENSE
82
+ - README.markdown
83
+ - Rakefile
84
+ - VERSION
85
+ - bin/scheduler_daemon
86
+ - lib/loader/scheduler_loader.rb
87
+ - lib/scheduler_daemon.rb
88
+ - lib/scheduler_daemon/base.rb
89
+ - lib/scheduler_daemon/command_line_args_to_hash.rb
90
+ - lib/scheduler_daemon/exception_handler.rb
91
+ - lib/scheduler_daemon/rails/generators/scheduler/USAGE
92
+ - lib/scheduler_daemon/rails/generators/scheduler/scheduler_generator.rb
93
+ - lib/scheduler_daemon/rails/generators/scheduler/templates/README
94
+ - lib/scheduler_daemon/rails/generators/scheduler/templates/lib/scheduled_tasks/session_cleaner_task.rb
95
+ - lib/scheduler_daemon/rails/generators/scheduler_task/scheduler_task_generator.rb
96
+ - lib/scheduler_daemon/rails/generators/scheduler_task/templates/README
97
+ - lib/scheduler_daemon/rails/generators/scheduler_task/templates/scheduled_tasks/example_task.rb
98
+ - lib/scheduler_daemon/rails/railtie.rb
99
+ - lib/scheduler_daemon/scheduler_task.rb
100
+ - scheduler_daemon_robertcigan.gemspec
101
+ - spec/command_line_args_to_hash_spec.rb
102
+ - spec/scheduled_tasks/session_cleaner_task_spec.rb
103
+ - spec/scheduler_spec.rb
104
+ - spec/spec_helper.rb
105
+ homepage: http://github.com/ssoroka/scheduler_daemon
106
+ licenses: []
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ! '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 1.8.10
126
+ signing_key:
127
+ specification_version: 3
128
+ summary: Rails 3 compatible scheduler daemon. Replaces cron/rake pattern of periodically
129
+ running rake tasks to perform maintenance tasks in Rails apps. Scheduler Daemon
130
+ is made specifically for your Rails app, and only loads the environment once, no
131
+ matter how many tasks run. What's so great about it? Well, I'm glad you asked! -
132
+ Only loads your Rails environment once on daemon start, not every time a task is
133
+ run - Allows you to easily deploy the scheduled tasks with your Rails app instead
134
+ of depending on an administrator to update crontab - It doesn't use rake or cron!
135
+ - Gets you up and running with your own daemon in under 2 minutes
136
+ test_files:
137
+ - spec/command_line_args_to_hash_spec.rb
138
+ - spec/scheduled_tasks/session_cleaner_task_spec.rb
139
+ - spec/scheduler_spec.rb
140
+ - spec/spec_helper.rb