gregfitz23-chrono_trigger 0.0.3 → 0.0.4

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.
@@ -1,3 +1,7 @@
1
+ == 0.0.4 2009-04-09
2
+ * 1 enhancement
3
+ * Added chrono_trigger binary and supporting files
4
+
1
5
  == 0.0.2 2009-04-02
2
6
 
3
7
  * 1 minor enhancement:
@@ -3,8 +3,11 @@ Manifest.txt
3
3
  PostInstall.txt
4
4
  README.rdoc
5
5
  Rakefile
6
+ bin/chrono_trigger
6
7
  lib/chrono_trigger.rb
7
8
  lib/chrono_trigger/cron_entry.rb
9
+ lib/chrono_trigger/process.rb
10
+ lib/chrono_trigger/runner.rb
8
11
  lib/chrono_trigger/shell.rb
9
12
  lib/chrono_trigger/tasks.rb
10
13
  lib/chrono_trigger/trigger.rb
@@ -1,3 +1 @@
1
- # Remember to add chrono_trigger.rake to lib/tasks in your rails project containing:
2
- # require 'chrono_trigger/tasks'
3
1
 
@@ -10,8 +10,6 @@ A cron framework for defining cron tasks using a readable DSL.
10
10
 
11
11
  == SYNOPSIS:
12
12
 
13
- After installation, create lib/tasks/chrono_trigger.rake containing: require 'chrono_trigger/tasks'
14
-
15
13
  Create trigger files (by default in the lib/triggers) directory.
16
14
  Triggers should follow the pattern:
17
15
 
@@ -22,7 +20,7 @@ trigger "name" do
22
20
  at :hour=>9, :minute=>[30,50]
23
21
  end
24
22
 
25
- Execute rake RAILS_ENV={env} chrono_trigger:run
23
+ Run chrono_trigger -t{full path to trigger file}
26
24
 
27
25
  == REQUIREMENTS:
28
26
 
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 3
2
+ :patch: 4
3
3
  :major: 0
4
4
  :minor: 0
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ require 'chrono_trigger/runner'
5
+
6
+ ChronoTrigger::Runner.run
7
+
@@ -0,0 +1,22 @@
1
+ module ChronoTrigger
2
+
3
+ class Process
4
+
5
+ def run(options={})
6
+ @t = Thread.new do
7
+ shell = ChronoTrigger::Shell.new
8
+ shell.load_triggers(options[:trigger_file])
9
+ loop do
10
+ shell.execute_triggers
11
+ sleep 1.minute.to_i
12
+ end
13
+ end
14
+
15
+ @t.join
16
+ end
17
+
18
+ def stop
19
+ @t.exit
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,276 @@
1
+ require File.join(File.dirname(__FILE__), 'process')
2
+ require "chrono_trigger"
3
+ require 'optparse'
4
+ require 'yaml'
5
+
6
+ module ChronoTrigger
7
+ class Runner
8
+
9
+ attr_accessor :options
10
+ private :options, :options=
11
+
12
+ def self.run
13
+ new
14
+ end
15
+
16
+ def self.shutdown
17
+ @@instance.shutdown
18
+ end
19
+
20
+ def initialize
21
+ @@instance = self
22
+ parse_options
23
+
24
+ @process = ProcessHelper.new(options[:logger], options[:pid_file], options[:user], options[:group])
25
+
26
+ if options[:stop]
27
+ @process.kill
28
+ exit(1)
29
+ end
30
+
31
+ pid = @process.running?
32
+ if pid
33
+ if options[:force]
34
+ STDERR.puts "Shutting down existing ChronoTrigger."
35
+ @process.kill
36
+ @process = ProcessHelper.new(options[:logger], options[:pid_file], options[:user], options[:group])
37
+ else
38
+ STDERR.puts "There is already a ChronoTrigger process running (pid #{pid}), exiting."
39
+ exit(1)
40
+ end
41
+ elsif pid.nil?
42
+ STDERR.puts "Cleaning up stale pidfile at #{options[:pid_file]}."
43
+ end
44
+
45
+ start
46
+ end
47
+
48
+ def parse_options
49
+ self.options = {
50
+ :log_level => Logger::INFO,
51
+ :daemonize => false,
52
+ :pid_file => File.join('', 'var', 'run', 'chrono_trigger.pid')
53
+ }
54
+
55
+ OptionParser.new do |opts|
56
+ opts.summary_width = 25
57
+
58
+ opts.banner = "ChronoTrigger\n\n",
59
+ "usage: chrono_trigger [options...]\n",
60
+ " chrono_trigger --help\n",
61
+ " chrono_trigger --version\n"
62
+
63
+ opts.separator ""
64
+ opts.separator ""; opts.separator "ChronoTrigger Options:"
65
+
66
+ opts.on("-tTRIGGER_FILE", "--triggers TRIGGERS", "Path to file specifying triggers to be executed") do |trigger_file|
67
+ options[:trigger_file] = trigger_file
68
+ end
69
+
70
+ opts.on("-f", "--force", "Force restart of ChronoTrigger process.") do
71
+ options[:force] = true
72
+ end
73
+
74
+ opts.on("-s", "--stop", "Stop a currently running ChronoTrigger process.") do
75
+ options[:stop] = true
76
+ end
77
+
78
+ opts.separator ""
79
+ opts.separator ""; opts.separator "Process:"
80
+
81
+ opts.on("-PFILE", "--pid FILENAME", "save PID in FILENAME when using -d option.", "(default: #{options[:pid_file]})") do |pid_file|
82
+ options[:pid_file] = File.expand_path(pid_file)
83
+ end
84
+
85
+ opts.on("-u", "--user USER", "User to run as") do |user|
86
+ options[:user] = user.to_i == 0 ? Etc.getpwnam(user).uid : user.to_i
87
+ end
88
+
89
+ opts.on("-gGROUP", "--group GROUP", "Group to run as") do |group|
90
+ options[:group] = group.to_i == 0 ? Etc.getgrnam(group).gid : group.to_i
91
+ end
92
+
93
+ opts.separator ""; opts.separator "Logging:"
94
+
95
+ opts.on("-L", "--log [FILE]", "Path to print debugging information.") do |log_path|
96
+ options[:logger] = File.expand_path(log_path)
97
+ end
98
+
99
+ opts.on("-v", "Increase logging verbosity (may be used multiple times).") do
100
+ options[:log_level] -= 1
101
+ end
102
+
103
+ opts.on("-d", "Run as a daemon.") do
104
+ options[:daemonize] = true
105
+ end
106
+ end.parse!
107
+ end
108
+
109
+ def start
110
+ drop_privileges
111
+
112
+ @process.daemonize if options[:daemonize]
113
+
114
+ setup_signal_traps
115
+ @process.write_pid_file
116
+
117
+ STDOUT.puts "Starting ChronoTrigger."
118
+ @chrono_trigger_process = ChronoTrigger::Process.new
119
+ @chrono_trigger_process.run(options)
120
+
121
+ @process.remove_pid_file
122
+ end
123
+
124
+ def drop_privileges
125
+ ::Process.egid = options[:group] if options[:group]
126
+ ::Process.euid = options[:user] if options[:user]
127
+ end
128
+
129
+ def shutdown
130
+ begin
131
+ STDOUT.puts "Shutting down."
132
+ @chrono_trigger_process.stop
133
+ exit(1)
134
+ rescue Object => e
135
+ STDERR.puts "There was an error shutting down: #{e}"
136
+ exit(70)
137
+ end
138
+ end
139
+
140
+ def setup_signal_traps
141
+ Signal.trap("INT") { shutdown }
142
+ Signal.trap("TERM") { shutdown }
143
+ end
144
+ end
145
+
146
+ class ProcessHelper
147
+
148
+ def initialize(log_file = nil, pid_file = nil, user = nil, group = nil)
149
+ @log_file = log_file
150
+ @pid_file = pid_file
151
+ @user = user
152
+ @group = group
153
+ end
154
+
155
+ def safefork
156
+ begin
157
+ if pid = fork
158
+ return pid
159
+ end
160
+ rescue Errno::EWOULDBLOCK
161
+ sleep 5
162
+ retry
163
+ end
164
+ end
165
+
166
+ def daemonize
167
+ sess_id = detach_from_terminal
168
+ exit if pid = safefork
169
+
170
+ Dir.chdir("/")
171
+ File.umask 0000
172
+
173
+ close_io_handles
174
+ redirect_io
175
+
176
+ return sess_id
177
+ end
178
+
179
+ def detach_from_terminal
180
+ srand
181
+ safefork and exit
182
+
183
+ unless sess_id = ::Process.setsid
184
+ raise "Couldn't detach from controlling terminal."
185
+ end
186
+
187
+ trap 'SIGHUP', 'IGNORE'
188
+
189
+ sess_id
190
+ end
191
+
192
+ def close_io_handles
193
+ ObjectSpace.each_object(IO) do |io|
194
+ unless [STDIN, STDOUT, STDERR].include?(io)
195
+ begin
196
+ io.close unless io.closed?
197
+ rescue Exception
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ def redirect_io
204
+ begin; STDIN.reopen('/dev/null'); rescue Exception; end
205
+
206
+ if @log_file
207
+ begin
208
+ STDOUT.reopen(@log_file, "a")
209
+ STDOUT.sync = true
210
+ rescue Exception
211
+ begin; STDOUT.reopen('/dev/null'); rescue Exception; end
212
+ end
213
+ else
214
+ begin; STDOUT.reopen('/dev/null'); rescue Exception; end
215
+ end
216
+
217
+ begin; STDERR.reopen(STDOUT); rescue Exception; end
218
+ STDERR.sync = true
219
+ end
220
+
221
+ def rescue_exception
222
+ begin
223
+ yield
224
+ rescue Exception
225
+ end
226
+ end
227
+
228
+ def write_pid_file
229
+ return unless @pid_file
230
+ FileUtils.mkdir_p(File.dirname(@pid_file))
231
+ File.open(@pid_file, "w") { |f| f.write(::Process.pid) }
232
+ File.chmod(0644, @pid_file)
233
+ end
234
+
235
+ def remove_pid_file
236
+ return unless @pid_file
237
+ File.unlink(@pid_file) if File.exists?(@pid_file)
238
+ end
239
+
240
+ def running?
241
+ return false unless @pid_file
242
+
243
+ pid = File.read(@pid_file).chomp.to_i rescue nil
244
+ pid = nil if pid == 0
245
+ return false unless pid
246
+
247
+ begin
248
+ ::Process.kill(0, pid)
249
+ return pid
250
+ rescue Errno::ESRCH
251
+ return nil
252
+ rescue Errno::EPERM
253
+ return pid
254
+ end
255
+ end
256
+
257
+ def kill
258
+ return false unless @pid_file
259
+
260
+ pid = File.read(@pid_file).chomp.to_i rescue nil
261
+ pid = nil if pid == 0
262
+ return false unless pid
263
+
264
+ begin
265
+ ::Process.kill("TERM", pid)
266
+ remove_pid_file
267
+ return pid
268
+ rescue Errno::ESRCH
269
+ return nil
270
+ rescue Errno::EPERM
271
+ return pid
272
+ end
273
+
274
+ end
275
+ end
276
+ end
@@ -25,6 +25,7 @@ module ChronoTrigger
25
25
  #Run execute on any trigger who's cron entry matches the current time.
26
26
  def execute_triggers
27
27
  now = Time.now
28
+ STDOUT.puts "triggers: #{triggers}"
28
29
  triggers.map {|trigger| trigger.execute_on_match(now)}
29
30
  end
30
31
 
@@ -55,6 +55,7 @@ module ChronoTrigger
55
55
  end
56
56
 
57
57
  def execute
58
+ STDOUT.puts "executing #{@exec_block}"
58
59
  @exec_block.call
59
60
  end
60
61
 
@@ -1,5 +1,5 @@
1
1
  trigger "trigger1" do
2
- runs { puts "trigger 1 runs every 1 minutes; executed at #{Time.now}"}
2
+ runs { File.open("/tmp/test_#{Time.now}.txt", 'w') {|file| file.puts "trigger 1 runs every 1 minutes; executed at #{Time.now}"}}
3
3
  every :minutes=>1
4
4
  end
5
5
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gregfitz23-chrono_trigger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Fitzgerald
@@ -9,14 +9,14 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-06 00:00:00 -07:00
13
- default_executable:
12
+ date: 2009-04-09 00:00:00 -07:00
13
+ default_executable: chrono_trigger
14
14
  dependencies: []
15
15
 
16
16
  description: TODO
17
17
  email: greg_fitz@yahoo.com
18
- executables: []
19
-
18
+ executables:
19
+ - chrono_trigger
20
20
  extensions: []
21
21
 
22
22
  extra_rdoc_files:
@@ -27,8 +27,11 @@ files:
27
27
  - PostInstall.txt
28
28
  - README.rdoc
29
29
  - VERSION.yml
30
+ - bin/chrono_trigger
30
31
  - lib/chrono_trigger
31
32
  - lib/chrono_trigger/cron_entry.rb
33
+ - lib/chrono_trigger/process.rb
34
+ - lib/chrono_trigger/runner.rb
32
35
  - lib/chrono_trigger/shell.rb
33
36
  - lib/chrono_trigger/tasks.rb
34
37
  - lib/chrono_trigger/trigger.rb