djinn 0.1.0 → 0.1.1
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.
- data/README.rdoc +153 -112
- data/README.rdoc.old +251 -0
- data/VERSION +1 -1
- data/djinn.gemspec +9 -7
- data/lib/djinn.rb +3 -2
- data/lib/djinn/base.rb +28 -15
- data/lib/djinn/base/dsl.rb +191 -0
- data/lib/djinn/base/logging.rb +35 -0
- data/lib/djinn/base/pid_file.rb +41 -0
- data/lib/djinn/base/tonic.rb +51 -0
- data/lib/djinn/rails.rb +5 -4
- data/lib/djinn/rails/handlers.rb +5 -1
- metadata +10 -8
- data/lib/djinn/dsl.rb +0 -40
- data/lib/djinn/logging.rb +0 -33
- data/lib/djinn/pid_file.rb +0 -31
- data/lib/djinn/tonic.rb +0 -49
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= Djinn
|
2
2
|
|
3
|
-
Djinn is a
|
3
|
+
Djinn is a helper for building simple daemons.
|
4
4
|
|
5
5
|
In Arabian mythology a Djinn is a supernatural creature which occupies a
|
6
6
|
parallel world to that of mankind.
|
@@ -12,107 +12,183 @@ http://rdoc.info/projects/craigp/djinn
|
|
12
12
|
== Installation
|
13
13
|
|
14
14
|
gem install djinn
|
15
|
+
|
16
|
+
== Quickstart Example
|
15
17
|
|
16
|
-
|
18
|
+
Because you might not want to read the wordy version of the documentation, let's
|
19
|
+
dive right in and start by writing a simple Djinn and saving it in a file
|
20
|
+
called _file_djinn.rb_
|
17
21
|
|
18
22
|
#!/usr/bin/env ruby
|
19
23
|
|
20
24
|
require 'rubygems'
|
21
25
|
require 'djinn'
|
22
26
|
|
23
|
-
class
|
24
|
-
|
27
|
+
class FileDjinn
|
28
|
+
|
25
29
|
include Djinn
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
30
|
+
|
31
|
+
djinn do
|
32
|
+
|
33
|
+
configure do
|
34
|
+
add :output_file do
|
35
|
+
short_switch "-o"
|
36
|
+
long_switch "--output-file"
|
37
|
+
description "File to output stuff to"
|
38
|
+
required true
|
39
|
+
end
|
40
|
+
|
41
|
+
add_flag :create_file do
|
42
|
+
short_switch "-c"
|
43
|
+
long_switch "--create-file"
|
44
|
+
description "Create output file if it doesn't exist"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
start do
|
49
|
+
@file = unless File.exists?(config[:output_file])
|
50
|
+
unless config[:create_file]
|
51
|
+
log "File not found: #{config[:output_file]}"
|
52
|
+
nil
|
53
|
+
else
|
54
|
+
File.open(config[:output_file], 'a')
|
55
|
+
end
|
56
|
+
else
|
57
|
+
File.open(config[:output_file], 'a')
|
58
|
+
end
|
59
|
+
if @file
|
60
|
+
log "Opening output file: #{File.expand_path(@file.path)}"
|
61
|
+
loop do
|
62
|
+
@file.puts "Writing to the file at #{Time.now}"
|
63
|
+
@file.flush
|
64
|
+
sleep(5)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
exit do
|
70
|
+
if @file
|
71
|
+
log "Closing output file.."
|
72
|
+
@file.close
|
73
|
+
end
|
74
|
+
end
|
42
75
|
end
|
43
|
-
|
76
|
+
|
44
77
|
end
|
45
78
|
|
46
|
-
|
79
|
+
FileDjinn.djinnify
|
47
80
|
|
48
|
-
|
49
|
-
djinn.run
|
50
|
-
|
51
|
-
But running it in the background is sort of the point. A bit contrived, but
|
52
|
-
this is the general idea:
|
81
|
+
Now we can try and start it using:
|
53
82
|
|
54
|
-
|
55
|
-
sleep(10)
|
56
|
-
djinn.stop
|
83
|
+
ruby file_djinn.rb
|
57
84
|
|
58
|
-
|
59
|
-
detach and run in the background until it dies or gets killed. You can wrap
|
60
|
-
argument parsing around that if you want, or do it in any other way. By default
|
61
|
-
the daemon will look for a config YAML file in same directory as you executed it
|
62
|
-
from, named the same as the Djinn class, so in this case *basic.yml*. It will by
|
63
|
-
default create the pid and log files in the same way. You can change this by
|
64
|
-
putting it in the config file or supplying an options hash:
|
85
|
+
Oops, no luck, but you should see something like:
|
65
86
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
87
|
+
Missing argument: output_file
|
88
|
+
|
89
|
+
Usage: djinn_file [OPTIONS]
|
90
|
+
--no-daemon Don't run in the background
|
91
|
+
--stop Stop the Djinn, if possible
|
92
|
+
-o, --output-file OUTPUT_FILE File to output stuff to
|
93
|
+
-c, --create-file Create output file if it doesn't exist
|
94
|
+
-h, --help Prints this message
|
95
|
+
|
96
|
+
Right, now that we know more about this Djinn, let's try again..
|
97
|
+
|
98
|
+
ruby file_djinn.rb -co test.log --no-daemon
|
72
99
|
|
73
|
-
|
74
|
-
|
100
|
+
Our shiny new Djinn is running in the foreground! But lets make it run as a daemon:
|
101
|
+
|
102
|
+
ruby file_djinn.rb -co test.log
|
75
103
|
|
76
|
-
|
77
|
-
The Rails daemon helpers are an implementation on top of this illustrating how it
|
78
|
-
can be tailored to include some option parsing and so forth, and so do a little
|
79
|
-
more for you.
|
104
|
+
Awesomesauce. Now, I wonder if we can stop it:
|
80
105
|
|
81
|
-
|
106
|
+
ruby file_djinn.rb --stop
|
82
107
|
|
83
|
-
|
108
|
+
== In More Detail
|
84
109
|
|
85
|
-
|
110
|
+
Okay, so lets pull that example apart and look at things in a little more detail..
|
86
111
|
|
87
|
-
|
88
|
-
|
112
|
+
=== Djinn Definition
|
113
|
+
|
114
|
+
Transmogrifying an otherwise workaday class into a Djinn is all about the
|
115
|
+
Djinn definition DSL. So we'll start with the basics, including the module and
|
116
|
+
starting a definition block.
|
89
117
|
|
90
|
-
class
|
118
|
+
class MyDjinn
|
119
|
+
|
120
|
+
include Djinn
|
121
|
+
|
122
|
+
djinn do
|
123
|
+
|
124
|
+
end
|
91
125
|
|
126
|
+
end
|
127
|
+
|
128
|
+
Right, now we need to tell it _what_ to do, and _when_. There are three events
|
129
|
+
we can hook up to in the lifecycle of a Djinn: *start*, *stop* and *exit*. Doing
|
130
|
+
something when the Djinn starts is the most important, since otherwise .. well, it
|
131
|
+
just won't do anything. So lets use a simple loop for now:
|
132
|
+
|
133
|
+
class MyDjinn
|
134
|
+
|
92
135
|
include Djinn
|
93
136
|
|
94
137
|
djinn do
|
95
|
-
|
96
|
-
|
97
|
-
|
138
|
+
|
139
|
+
start do
|
140
|
+
loop do
|
141
|
+
log "Doing something.."
|
98
142
|
sleep(5)
|
99
143
|
end
|
100
144
|
end
|
101
|
-
|
102
|
-
on :exit do
|
103
|
-
log "Handling a nice graceful exit.."
|
104
|
-
end
|
145
|
+
|
105
146
|
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
We've used another helper method provided to us there, namely *log*. This will
|
151
|
+
write a timestamped message for us, either to the log if we're running in the
|
152
|
+
background, or to stdout if we're running in the foreground.
|
153
|
+
|
154
|
+
Just like that we have a working Djinn, well except for one line, at the bottom
|
155
|
+
of the file:
|
156
|
+
|
157
|
+
MyDjinn.djinnify
|
106
158
|
|
159
|
+
That will take care of parsing any arguments and running the Djinn. We now have
|
160
|
+
working Djinn that will log some entirely useless text to a log file!
|
161
|
+
|
162
|
+
=== Configuration
|
163
|
+
|
164
|
+
There are a few options available to us if we want to pass configuration information
|
165
|
+
to our Djinn.
|
166
|
+
|
167
|
+
First, we can do it using command line switches, which we can define as part of our
|
168
|
+
Djinn definition block.
|
169
|
+
|
170
|
+
djinn do
|
171
|
+
|
172
|
+
configure do
|
173
|
+
|
174
|
+
end
|
175
|
+
|
107
176
|
end
|
177
|
+
|
178
|
+
|
179
|
+
= OMG I LOST INTEREST IN WRITING THIS
|
108
180
|
|
109
|
-
|
110
|
-
|
181
|
+
(Everything past this point is old shit, will update soon)
|
182
|
+
|
111
183
|
|
112
|
-
|
184
|
+
options = {
|
185
|
+
'pid_file_path' => 'path/to/pid/file',
|
186
|
+
'log_file_path' => 'path/to/log/file'
|
187
|
+
}
|
188
|
+
|
189
|
+
djinn.start(options)
|
113
190
|
|
114
|
-
|
115
|
-
djinn.run
|
191
|
+
Available actions to the *on* method are :start, :stop and :exit
|
116
192
|
|
117
193
|
The actions are executed in the context of the Djinn itself, so you can
|
118
194
|
get at the config without having to pass it around:
|
@@ -122,9 +198,9 @@ get at the config without having to pass it around:
|
|
122
198
|
my_setting = config[:omghax]
|
123
199
|
end
|
124
200
|
end
|
125
|
-
|
201
|
+
|
126
202
|
...
|
127
|
-
|
203
|
+
|
128
204
|
djinn.run { :omghax => "Groovy, baby" }
|
129
205
|
|
130
206
|
You can also give it a block to work with:
|
@@ -139,50 +215,15 @@ If you need to man-handle the internals and stuff, it yields itself:
|
|
139
215
|
djinn.config[:omghax] = "Groovy, baby"
|
140
216
|
end
|
141
217
|
|
142
|
-
Starting in the background is the same as before, call *start* instead of *run*:
|
143
|
-
|
144
|
-
djinn.start
|
145
|
-
|
146
|
-
The Rails Djinns can be built in exactly the same way as this.
|
147
|
-
|
148
218
|
== Rails Example
|
149
219
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
Create a file in RAILS_ROOT/lib or somewhere similar:
|
157
|
-
|
158
|
-
require 'djinn/rails'
|
159
|
-
require 'eventmachine'
|
160
|
-
|
161
|
-
class BookDjinn
|
162
|
-
|
163
|
-
BOOK_WORKER_INTERVAL = 5
|
164
|
-
|
165
|
-
include Djinn::Rails
|
166
|
-
|
167
|
-
def perform config
|
168
|
-
EM.run do
|
169
|
-
log "Workers will run every #{BOOK_WORKER_INTERVAL} secs"
|
170
|
-
EM::PeriodicTimer.new(BOOK_WORKER_INTERVAL) do
|
171
|
-
log "There are #{Book.count} book(s) in the database"
|
172
|
-
log "Updating read counts for all books.."
|
173
|
-
Book.all.each &:read!
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
220
|
+
The original intention of the gem was to build daemons for my Rails apps, and
|
221
|
+
there's a simple example Rails app with a daemon in the example directory if
|
222
|
+
you check the code out of git, but here's the gist of it. Assumes a scenario
|
223
|
+
where you have a Book model that keeps a count of how many times a book has
|
224
|
+
been read..
|
177
225
|
|
178
|
-
|
179
|
-
EM.stop
|
180
|
-
super
|
181
|
-
end
|
182
|
-
|
183
|
-
end
|
184
|
-
|
185
|
-
(And, the new more awesome way:)
|
226
|
+
Create a file in RAILS_ROOT/lib or somewhere similar in your load path:
|
186
227
|
|
187
228
|
require 'djinn/rails'
|
188
229
|
require 'eventmachine'
|
@@ -241,10 +282,10 @@ Yay, we have a daemon running in the background! To stop it:
|
|
241
282
|
|
242
283
|
That gives you more-or-less everything you need to build something basic
|
243
284
|
and monitor it with god or a similar process monitor.
|
244
|
-
|
285
|
+
|
245
286
|
== TODO
|
246
287
|
|
247
|
-
|
288
|
+
Update this documentation.
|
248
289
|
|
249
290
|
== Copyright
|
250
291
|
|
data/README.rdoc.old
ADDED
@@ -0,0 +1,251 @@
|
|
1
|
+
= Djinn
|
2
|
+
|
3
|
+
Djinn is a very basic helper for building simple daemons.
|
4
|
+
|
5
|
+
In Arabian mythology a Djinn is a supernatural creature which occupies a
|
6
|
+
parallel world to that of mankind.
|
7
|
+
|
8
|
+
== Documentation
|
9
|
+
|
10
|
+
http://rdoc.info/projects/craigp/djinn
|
11
|
+
|
12
|
+
== Installation
|
13
|
+
|
14
|
+
gem install djinn
|
15
|
+
|
16
|
+
== Non-Rails Example (old non-DSL way, will be deprecated soon-ish)
|
17
|
+
|
18
|
+
#!/usr/bin/env ruby
|
19
|
+
|
20
|
+
require 'rubygems'
|
21
|
+
require 'djinn'
|
22
|
+
|
23
|
+
class Basic
|
24
|
+
|
25
|
+
include Djinn
|
26
|
+
|
27
|
+
# Not providing a "perform" method falls back to the base method
|
28
|
+
# in Djinn, which does nothing useful. Make sure your method accepts
|
29
|
+
# a config hash, even if it doesn't use it.
|
30
|
+
def perform options
|
31
|
+
while
|
32
|
+
log "ZOMG! A Djinn?"
|
33
|
+
sleep(5)
|
34
|
+
do
|
35
|
+
end
|
36
|
+
|
37
|
+
# Strictly optional, lets you do stuff when the Djinn daemon stops.
|
38
|
+
# The call to "super" is required, or your daemon will never die
|
39
|
+
def handle_exit
|
40
|
+
log "Handling a nice graceful exit.."
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
Run it in the foreground like this:
|
47
|
+
|
48
|
+
djinn = Basic.new
|
49
|
+
djinn.run
|
50
|
+
|
51
|
+
But running it in the background is sort of the point. A bit contrived, but
|
52
|
+
this is the general idea:
|
53
|
+
|
54
|
+
djinn.start
|
55
|
+
sleep(10)
|
56
|
+
djinn.stop
|
57
|
+
|
58
|
+
Assuming you didn't sleep there your script would end and the daemon would
|
59
|
+
detach and run in the background until it dies or gets killed. You can wrap
|
60
|
+
argument parsing around that if you want, or do it in any other way. By default
|
61
|
+
the daemon will look for a config YAML file in same directory as you executed it
|
62
|
+
from, named the same as the Djinn class, so in this case *basic.yml*. It will by
|
63
|
+
default create the pid and log files in the same way. You can change this by
|
64
|
+
putting it in the config file or supplying an options hash:
|
65
|
+
|
66
|
+
options = {
|
67
|
+
'pid_file_path' => 'path/to/pid/file',
|
68
|
+
'log_file_path' => 'path/to/log/file'
|
69
|
+
}
|
70
|
+
|
71
|
+
djinn.start(options)
|
72
|
+
|
73
|
+
These options will also be passed to your *perform* method, so you can include
|
74
|
+
anything you need in the hash as well, or in the YAML file for that matter.
|
75
|
+
|
76
|
+
It might seem ugly, but the solution is minimal, and so remains flexible I think.
|
77
|
+
The Rails daemon helpers are an implementation on top of this illustrating how it
|
78
|
+
can be tailored to include some option parsing and so forth, and so do a little
|
79
|
+
more for you.
|
80
|
+
|
81
|
+
== Non-Rails Example (new DSL awesomesauce)
|
82
|
+
|
83
|
+
Using the same silly example, you can do this:
|
84
|
+
|
85
|
+
#!/usr/bin/env ruby
|
86
|
+
|
87
|
+
require 'rubygems'
|
88
|
+
require 'djinn'
|
89
|
+
|
90
|
+
class Basic
|
91
|
+
|
92
|
+
include Djinn
|
93
|
+
|
94
|
+
djinn do
|
95
|
+
on :start do
|
96
|
+
while
|
97
|
+
log "ZOMG! A Djinn?"
|
98
|
+
sleep(5)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
on :exit do
|
103
|
+
log "Handling a nice graceful exit.."
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
Much cleaner and prettier, and no horrible *super* required. Available
|
110
|
+
actions to the *on* method are :start, :stop and :exit
|
111
|
+
|
112
|
+
Run it in the foreground in the same way:
|
113
|
+
|
114
|
+
djinn = Basic.new
|
115
|
+
djinn.run
|
116
|
+
|
117
|
+
The actions are executed in the context of the Djinn itself, so you can
|
118
|
+
get at the config without having to pass it around:
|
119
|
+
|
120
|
+
djinn do
|
121
|
+
on :start do
|
122
|
+
my_setting = config[:omghax]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
...
|
127
|
+
|
128
|
+
djinn.run { :omghax => "Groovy, baby" }
|
129
|
+
|
130
|
+
You can also give it a block to work with:
|
131
|
+
|
132
|
+
djinn.run do
|
133
|
+
puts "This will happen before calling the :start action"
|
134
|
+
end
|
135
|
+
|
136
|
+
If you need to man-handle the internals and stuff, it yields itself:
|
137
|
+
|
138
|
+
djinn.run do |djinn|
|
139
|
+
djinn.config[:omghax] = "Groovy, baby"
|
140
|
+
end
|
141
|
+
|
142
|
+
Starting in the background is the same as before, call *start* instead of *run*:
|
143
|
+
|
144
|
+
djinn.start
|
145
|
+
|
146
|
+
The Rails Djinns can be built in exactly the same way as this.
|
147
|
+
|
148
|
+
== Rails Example
|
149
|
+
|
150
|
+
There's a simple example in the example directory if you check the code out, but
|
151
|
+
here's the gist of it.
|
152
|
+
|
153
|
+
Assumes a scenario where you have a Book model that keeps a count of how many
|
154
|
+
times a book has been read.
|
155
|
+
|
156
|
+
Create a file in RAILS_ROOT/lib or somewhere similar:
|
157
|
+
|
158
|
+
require 'djinn/rails'
|
159
|
+
require 'eventmachine'
|
160
|
+
|
161
|
+
class BookDjinn
|
162
|
+
|
163
|
+
BOOK_WORKER_INTERVAL = 5
|
164
|
+
|
165
|
+
include Djinn::Rails
|
166
|
+
|
167
|
+
def perform config
|
168
|
+
EM.run do
|
169
|
+
log "Workers will run every #{BOOK_WORKER_INTERVAL} secs"
|
170
|
+
EM::PeriodicTimer.new(BOOK_WORKER_INTERVAL) do
|
171
|
+
log "There are #{Book.count} book(s) in the database"
|
172
|
+
log "Updating read counts for all books.."
|
173
|
+
Book.all.each &:read!
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def handle_exit
|
179
|
+
EM.stop
|
180
|
+
super
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
(And, the new more awesome way:)
|
186
|
+
|
187
|
+
require 'djinn/rails'
|
188
|
+
require 'eventmachine'
|
189
|
+
|
190
|
+
class BookDjinn
|
191
|
+
|
192
|
+
BOOK_WORKER_INTERVAL = 5
|
193
|
+
|
194
|
+
include Djinn::Rails
|
195
|
+
|
196
|
+
djinn do
|
197
|
+
|
198
|
+
on :start do
|
199
|
+
EM.run do
|
200
|
+
log "Workers will run every #{BOOK_WORKER_INTERVAL} secs"
|
201
|
+
EM::PeriodicTimer.new(BOOK_WORKER_INTERVAL) do
|
202
|
+
log "There are #{Book.count} book(s) in the database"
|
203
|
+
log "Updating read counts for all books.."
|
204
|
+
Book.all.each &:read!
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
on :exit do
|
210
|
+
EM.stop
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
Right, now you need to start it somehow. The easiest way is to create a file
|
218
|
+
in RAILS_ROOT/scripts and pop this in it:
|
219
|
+
|
220
|
+
#!/usr/bin/env ruby
|
221
|
+
require 'rubygems'
|
222
|
+
require File.join(File.dirname(__FILE__), '../lib/book_djinn')
|
223
|
+
BookDjinn.go ARGV
|
224
|
+
|
225
|
+
Righto, now start it from RAILS_ROOT:
|
226
|
+
|
227
|
+
ruby script/book_djinn
|
228
|
+
|
229
|
+
Okay, but that defaults to _run_, which starts it in the foreground and also
|
230
|
+
uses the rails development environment by default. Try this:
|
231
|
+
|
232
|
+
ruby script/book_djinn --help
|
233
|
+
|
234
|
+
That should give you a better idea of what's going on, then try this:
|
235
|
+
|
236
|
+
ruby script/book_djinn start -e production
|
237
|
+
|
238
|
+
Yay, we have a daemon running in the background! To stop it:
|
239
|
+
|
240
|
+
ruby script/book_djinn stop
|
241
|
+
|
242
|
+
That gives you more-or-less everything you need to build something basic
|
243
|
+
and monitor it with god or a similar process monitor.
|
244
|
+
|
245
|
+
== TODO
|
246
|
+
|
247
|
+
Lots.
|
248
|
+
|
249
|
+
== Copyright
|
250
|
+
|
251
|
+
Copyright (c) 2010 Craig Paterson. See LICENSE for details.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.1
|
data/djinn.gemspec
CHANGED
@@ -5,33 +5,35 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{djinn}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Craig Paterson"]
|
12
|
-
s.date = %q{2010-07-
|
12
|
+
s.date = %q{2010-07-30}
|
13
13
|
s.description = %q{Helper for creating simple custom daemons}
|
14
14
|
s.email = %q{darksavant@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
|
-
"README.rdoc"
|
17
|
+
"README.rdoc",
|
18
|
+
"README.rdoc.old"
|
18
19
|
]
|
19
20
|
s.files = [
|
20
21
|
".document",
|
21
22
|
".gitignore",
|
22
23
|
"LICENSE",
|
23
24
|
"README.rdoc",
|
25
|
+
"README.rdoc.old",
|
24
26
|
"Rakefile",
|
25
27
|
"VERSION",
|
26
28
|
"djinn.gemspec",
|
27
29
|
"lib/djinn.rb",
|
28
30
|
"lib/djinn/base.rb",
|
29
|
-
"lib/djinn/dsl.rb",
|
30
|
-
"lib/djinn/logging.rb",
|
31
|
-
"lib/djinn/pid_file.rb",
|
31
|
+
"lib/djinn/base/dsl.rb",
|
32
|
+
"lib/djinn/base/logging.rb",
|
33
|
+
"lib/djinn/base/pid_file.rb",
|
34
|
+
"lib/djinn/base/tonic.rb",
|
32
35
|
"lib/djinn/rails.rb",
|
33
36
|
"lib/djinn/rails/handlers.rb",
|
34
|
-
"lib/djinn/tonic.rb",
|
35
37
|
"test/helper.rb",
|
36
38
|
"test/test_djinn.rb"
|
37
39
|
]
|
data/lib/djinn.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
|
1
|
+
dir = File.expand_path(File.dirname(__FILE__))
|
2
|
+
$:.unshift(dir) unless $:.include?(dir)
|
2
3
|
|
3
4
|
require 'yaml'
|
4
5
|
require 'djinn/base'
|
5
|
-
require 'djinn/dsl'
|
6
|
+
require 'djinn/base/dsl'
|
6
7
|
|
7
8
|
# In Arabian mythology a Djinn is a supernatural creature which occupies a
|
8
9
|
# parallel world to that of mankind
|
data/lib/djinn/base.rb
CHANGED
@@ -1,23 +1,26 @@
|
|
1
|
-
|
1
|
+
dir = File.expand_path(File.dirname(__FILE__))
|
2
|
+
$:.unshift(dir) unless $:.include?(dir)
|
2
3
|
|
3
|
-
require 'tonic'
|
4
|
-
require 'pid_file'
|
5
|
-
require 'logging'
|
4
|
+
require 'base/tonic'
|
5
|
+
require 'base/pid_file'
|
6
|
+
require 'base/logging'
|
6
7
|
|
7
8
|
module Djinn
|
8
9
|
# The base class from which all Djinn spring forth
|
9
10
|
module Base
|
10
11
|
|
11
|
-
include Djinn::Tonic
|
12
|
-
include Djinn::Logging
|
12
|
+
include Djinn::Base::Tonic
|
13
|
+
include Djinn::Base::Logging
|
13
14
|
|
14
15
|
attr_reader :config
|
15
16
|
|
17
|
+
def initialize
|
18
|
+
@config = { :__daemonize => true }
|
19
|
+
end
|
20
|
+
|
16
21
|
# Base implementation does nothing worthwhile, you should override this
|
17
|
-
# in your own implementation
|
22
|
+
# in your own implementation.
|
18
23
|
def perform config={}
|
19
|
-
trap('TERM') { handle_exit }
|
20
|
-
trap('INT') { handle_exit }
|
21
24
|
while
|
22
25
|
log("[#{name}] Djinn is running.. and doing nothing worthwhile.")
|
23
26
|
sleep(5)
|
@@ -25,15 +28,17 @@ module Djinn
|
|
25
28
|
end
|
26
29
|
|
27
30
|
# Override this with useful exit code if you need to, but remember to
|
28
|
-
# call *super* or call *exit* yourself, or your Djinn will be immortal
|
31
|
+
# call *super* or call *exit* yourself, or your Djinn will be immortal.
|
32
|
+
# This is truly terrible code, and will be removed soon.
|
29
33
|
def handle_exit
|
30
34
|
__exit! if respond_to?(:__exit!)
|
31
35
|
exit(0)
|
32
36
|
end
|
33
37
|
|
34
|
-
# Starts the Djinn in the background
|
38
|
+
# Starts the Djinn in the background.
|
35
39
|
def start config={}, &block
|
36
|
-
@config
|
40
|
+
@config.update(config).update(load_config)
|
41
|
+
#@config = (config.empty?) ? load_config : config
|
37
42
|
log "Starting #{name} in the background.."
|
38
43
|
logfile = get_logfile(config)
|
39
44
|
daemonize(logfile, get_pidfile(config)) do
|
@@ -41,18 +46,25 @@ module Djinn
|
|
41
46
|
trap('TERM') { handle_exit }
|
42
47
|
trap('INT') { handle_exit }
|
43
48
|
(respond_to?(:__start!)) ? __start! : perform(@config)
|
49
|
+
# If this process doesn't loop or otherwise breaks out of
|
50
|
+
# the loop we still want to clean up after ourselves
|
51
|
+
handle_exit
|
44
52
|
end
|
45
53
|
end
|
46
54
|
|
47
55
|
# Starts the Djinn in the foreground, which is often useful for
|
48
|
-
# testing or other noble pursuits
|
56
|
+
# testing or other noble pursuits.
|
49
57
|
def run config={}, &block
|
50
|
-
@config
|
58
|
+
@config.update(config).update(load_config)
|
59
|
+
# @config = (config.empty?) ? load_config : config
|
51
60
|
log "Starting #{name} in the foreground.."
|
52
61
|
trap('TERM') { handle_exit }
|
53
62
|
trap('INT') { handle_exit }
|
54
63
|
yield(self) if block_given?
|
55
64
|
(respond_to?(:__start!)) ? __start! : perform(@config)
|
65
|
+
# If this process doesn't loop or otherwise breaks out of
|
66
|
+
# the loop we still want to clean up after ourselves
|
67
|
+
handle_exit
|
56
68
|
end
|
57
69
|
|
58
70
|
# Convenience method, really just calls *stop* and then *start* for you :P
|
@@ -64,7 +76,8 @@ module Djinn
|
|
64
76
|
# Stops the Djinn, unless you change the location of the pid file, in
|
65
77
|
# which case its all about you and the *kill* command
|
66
78
|
def stop config={}
|
67
|
-
@config
|
79
|
+
@config.update(config).update(load_config)
|
80
|
+
# @config = (config.empty?) ? load_config : config
|
68
81
|
yield(self) if block_given?
|
69
82
|
__stop! if respond_to?(:__stop!)
|
70
83
|
pidfile = get_pidfile(@config)
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Djinn
|
4
|
+
module Base
|
5
|
+
# Defines the awesomesauce DSL defining your Djinn
|
6
|
+
module Dsl
|
7
|
+
|
8
|
+
# Start your Djinn definition
|
9
|
+
def djinn &block
|
10
|
+
@definition = DslDefinitionHelper.new(&block)
|
11
|
+
@definition.actions.each do |action, proc|
|
12
|
+
module_eval do
|
13
|
+
define_method "__#{action}!".intern do |*args|
|
14
|
+
instance_exec(&proc)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Create an instance of your Djinn, interpret any command line switches
|
21
|
+
# defined in your configuration block, and starts the Djinn in the
|
22
|
+
# background
|
23
|
+
def djinnify config={}
|
24
|
+
djinn = new
|
25
|
+
@definition.prepare(djinn.config.update(config))
|
26
|
+
unless djinn.config[:__stop]
|
27
|
+
if djinn.config[:__daemonize]
|
28
|
+
djinn.start
|
29
|
+
else
|
30
|
+
djinn.run
|
31
|
+
end
|
32
|
+
else
|
33
|
+
djinn.stop
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Used internally to read configuration blocks
|
38
|
+
class ConfigHelper
|
39
|
+
|
40
|
+
attr_accessor :config
|
41
|
+
|
42
|
+
def initialize &block
|
43
|
+
@config_items = []
|
44
|
+
instance_eval(&block)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Add a posix-style switch to your Djinn
|
48
|
+
def add key, &block
|
49
|
+
@config_items << ConfigItem.new(:posix, key, &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Add a simple flag switch to your daemon
|
53
|
+
def add_flag key, &block
|
54
|
+
@config_items << ConfigItem.new(:flag, key, &block)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Parses the configuration items created by executing the
|
58
|
+
# configuration block
|
59
|
+
def parse_config!(config)
|
60
|
+
opts = OptionParser.new do |opts|
|
61
|
+
opts.banner = "Usage: djinn_file [OPTIONS]"
|
62
|
+
opts.on("--no-daemon", "Don't run in the background") do
|
63
|
+
config[:__daemonize] = false
|
64
|
+
end
|
65
|
+
opts.on("--stop", "Stop the Djinn, if possible") do
|
66
|
+
config[:__stop] = true
|
67
|
+
end
|
68
|
+
opts.on_tail("-h", "--help", "Prints this message") do
|
69
|
+
puts opts
|
70
|
+
exit(0)
|
71
|
+
end
|
72
|
+
@config_items.each { |c| c.parse!(opts, config) }
|
73
|
+
end
|
74
|
+
opts.parse!
|
75
|
+
@config_items.each do |ci|
|
76
|
+
if ci.required?
|
77
|
+
puts "Missing argument: #{ci.key}\n\n#{opts}"
|
78
|
+
exit(1)
|
79
|
+
end unless config.include?(ci.key) or config.include?(:__stop)
|
80
|
+
end
|
81
|
+
rescue OptionParser::InvalidOption => e
|
82
|
+
puts e.message
|
83
|
+
exit(1)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
# A helper class to hold individual configuration items
|
89
|
+
class ConfigItem
|
90
|
+
|
91
|
+
attr_reader :key
|
92
|
+
|
93
|
+
def initialize t, key, &block
|
94
|
+
@type, @key = t, key
|
95
|
+
instance_eval(&block)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Short configuration switch
|
99
|
+
def short_switch s
|
100
|
+
@short_switch = s
|
101
|
+
@short_switch = "-#{@short_switch}" unless @short_switch =~ /^-/
|
102
|
+
end
|
103
|
+
|
104
|
+
# Long configuration switch
|
105
|
+
def long_switch s
|
106
|
+
@long_switch = s
|
107
|
+
@long_switch = "--#{@long_switch}" unless @long_switch =~ /^--/
|
108
|
+
end
|
109
|
+
|
110
|
+
# Description of the switch
|
111
|
+
def description d
|
112
|
+
@description = d
|
113
|
+
end
|
114
|
+
|
115
|
+
# Sets whether the switch is required
|
116
|
+
def required r
|
117
|
+
@required = r
|
118
|
+
end
|
119
|
+
|
120
|
+
# Checks whether the switch is required
|
121
|
+
def required?
|
122
|
+
@required
|
123
|
+
end
|
124
|
+
|
125
|
+
# Parse the individual configuration option
|
126
|
+
def parse! opts, config
|
127
|
+
@long_switch = "#{@long_switch} #{@key.to_s.upcase}" if @type == :posix and \
|
128
|
+
defined?(@key) && @key
|
129
|
+
switches = []
|
130
|
+
switches << (defined?(@short_switch) && @short_switch) ? @short_switch : nil
|
131
|
+
switches << (defined?(@long_switch) && @long_switch) ? @long_switch : nil
|
132
|
+
opts.on(switches[0], switches[1], @description) do |o|
|
133
|
+
config[@key] = o
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
# A helper class for interpretting the definition block of a Djinn
|
140
|
+
class DslDefinitionHelper
|
141
|
+
|
142
|
+
attr_accessor :actions, :config_helper
|
143
|
+
|
144
|
+
def initialize &block
|
145
|
+
@actions = {}
|
146
|
+
instance_eval(&block)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Define configuration for a Djinn, adding ARGV switches that
|
150
|
+
# can be interpreted and acted on in your actions
|
151
|
+
def configure &block
|
152
|
+
@config_helper = ConfigHelper.new(&block)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Define an action that will be performed by a Djinn
|
156
|
+
def on action, &block
|
157
|
+
acceptable_actions = %w(start stop exit)
|
158
|
+
raise DjinnActionError.new("\"#{action}\" is unrecognized, please use one of: #{acceptable_actions.join(', ')}") \
|
159
|
+
unless acceptable_actions.include?(action.to_s)
|
160
|
+
@actions[action] = block
|
161
|
+
end
|
162
|
+
|
163
|
+
# Runs when the Djinn starts
|
164
|
+
def start &block
|
165
|
+
@actions[:start] = block
|
166
|
+
end
|
167
|
+
|
168
|
+
# Runs when Djinn exits
|
169
|
+
def exit &block
|
170
|
+
@actions[:exit] = block
|
171
|
+
end
|
172
|
+
|
173
|
+
# Runs when the Djinn stops
|
174
|
+
def stop &block
|
175
|
+
@actions[:stop] = block
|
176
|
+
end
|
177
|
+
|
178
|
+
# Preare the Djinn configuration based on informations passed in
|
179
|
+
# in the configuration block
|
180
|
+
def prepare(config)
|
181
|
+
@config_helper.parse_config!(config)
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
# This error means you screwed something up in your action definition
|
187
|
+
class DjinnActionError < Exception; end
|
188
|
+
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Djinn
|
2
|
+
module Base
|
3
|
+
# Logging Helper Class of Awesomeness
|
4
|
+
module Logging
|
5
|
+
|
6
|
+
# Log something to STDOUT, or wherever it's been redirected to
|
7
|
+
def log msg
|
8
|
+
puts "#{Time.now.strftime("%m/%d/%Y %H:%M:%S")}: #{msg}"
|
9
|
+
STDOUT.flush
|
10
|
+
end
|
11
|
+
|
12
|
+
# Make some text *green*
|
13
|
+
def green text
|
14
|
+
colorize 32, text
|
15
|
+
end
|
16
|
+
|
17
|
+
# Make some text *red*
|
18
|
+
def red text
|
19
|
+
colorize 31, text
|
20
|
+
end
|
21
|
+
|
22
|
+
# Make some text *cyan*
|
23
|
+
def cyan text
|
24
|
+
colorize 36, text
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def colorize color, text
|
30
|
+
"\033[#{color}m#{text}\033[0m"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Djinn
|
2
|
+
module Base
|
3
|
+
# pid files are what bind your Djinn to the material plane
|
4
|
+
class PidFile
|
5
|
+
|
6
|
+
attr_reader :file
|
7
|
+
|
8
|
+
def initialize(file)
|
9
|
+
@file = file
|
10
|
+
end
|
11
|
+
|
12
|
+
def pid
|
13
|
+
File.exists?(@file) and IO.read(@file).to_i
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove
|
17
|
+
File.unlink(@file) if pid
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
File.open(@file, "w") { |f| f.write($$) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def ensure_empty
|
25
|
+
_pid = self.pid
|
26
|
+
if _pid
|
27
|
+
$stdout.puts <<-MSG
|
28
|
+
It looks like this Djinn is already running, not starting.
|
29
|
+
Alternatively, you could just have an orphaned pid file,
|
30
|
+
try running this command to check:
|
31
|
+
|
32
|
+
ps aux | grep #{_pid}
|
33
|
+
MSG
|
34
|
+
exit 1
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Djinn
|
2
|
+
module Base
|
3
|
+
# This play on words kills me everytime. I'm that lame.
|
4
|
+
module Tonic
|
5
|
+
|
6
|
+
# Send your Djinn off to frolic in the ether
|
7
|
+
def daemonize(logfile, pidfile, &block)
|
8
|
+
pidfile.ensure_empty
|
9
|
+
puts "Djinn is leaving process #{$$}"
|
10
|
+
|
11
|
+
srand # split rand streams between spawning and daemonized process
|
12
|
+
|
13
|
+
fork do
|
14
|
+
puts "Daemonizing on process #{$$}"
|
15
|
+
# puts system("ps aux | grep #{$$}")
|
16
|
+
|
17
|
+
#Dir.chdir "/" # release old working directory
|
18
|
+
File.umask 0000 # ensure sensible umask
|
19
|
+
|
20
|
+
# Making sure all file descriptors are closed
|
21
|
+
ObjectSpace.each_object(IO) do |io|
|
22
|
+
unless [STDIN, STDOUT, STDERR].include?(io)
|
23
|
+
begin
|
24
|
+
io.close unless io.closed?
|
25
|
+
rescue ::Exception
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
pidfile.create # write PID file
|
31
|
+
|
32
|
+
# detach from controlling terminal
|
33
|
+
unless sess_id = Process.setsid
|
34
|
+
raise 'Cannot detach from controlling terminal'
|
35
|
+
end
|
36
|
+
|
37
|
+
# redirect IO
|
38
|
+
STDIN.reopen('/dev/null')
|
39
|
+
STDOUT.reopen(logfile, 'a')
|
40
|
+
STDERR.reopen(STDOUT)
|
41
|
+
|
42
|
+
yield
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
data/lib/djinn/rails.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
|
1
|
+
dir = File.expand_path(File.dirname(__FILE__))
|
2
|
+
$:.unshift(dir) unless $:.include?(dir)
|
2
3
|
|
3
4
|
require 'yaml'
|
4
5
|
|
5
6
|
require 'base'
|
6
7
|
require 'rails/handlers'
|
7
|
-
require 'dsl'
|
8
|
+
require 'base/dsl'
|
8
9
|
|
9
10
|
module Djinn
|
10
11
|
# The basis for all Djinn that want to soar among the Rails stars
|
@@ -14,9 +15,9 @@ module Djinn
|
|
14
15
|
|
15
16
|
def self.included(base)
|
16
17
|
base.__send__(:extend, Djinn::Rails::Handlers)
|
17
|
-
base.__send__(:extend, Djinn::Dsl)
|
18
|
+
base.__send__(:extend, Djinn::Base::Dsl)
|
18
19
|
end
|
19
|
-
|
20
|
+
|
20
21
|
RAILS_ROOT = File.expand_path(Dir.pwd) unless defined?(RAILS_ROOT)
|
21
22
|
|
22
23
|
private
|
data/lib/djinn/rails/handlers.rb
CHANGED
@@ -4,12 +4,16 @@ module Djinn
|
|
4
4
|
|
5
5
|
require 'optparse'
|
6
6
|
|
7
|
-
def
|
7
|
+
def djinnify_rails args=[]
|
8
8
|
action = parse_args(args)
|
9
9
|
self.new.__send__(action.intern) do
|
10
10
|
load_rails unless %w(stop restart).include?(action)
|
11
11
|
end
|
12
12
|
end
|
13
|
+
|
14
|
+
def go args=[]
|
15
|
+
djinnify_rails(args)
|
16
|
+
end
|
13
17
|
|
14
18
|
private
|
15
19
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: djinn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Craig Paterson
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-07-
|
18
|
+
date: 2010-07-30 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -43,22 +43,24 @@ extensions: []
|
|
43
43
|
extra_rdoc_files:
|
44
44
|
- LICENSE
|
45
45
|
- README.rdoc
|
46
|
+
- README.rdoc.old
|
46
47
|
files:
|
47
48
|
- .document
|
48
49
|
- .gitignore
|
49
50
|
- LICENSE
|
50
51
|
- README.rdoc
|
52
|
+
- README.rdoc.old
|
51
53
|
- Rakefile
|
52
54
|
- VERSION
|
53
55
|
- djinn.gemspec
|
54
56
|
- lib/djinn.rb
|
55
57
|
- lib/djinn/base.rb
|
56
|
-
- lib/djinn/dsl.rb
|
57
|
-
- lib/djinn/logging.rb
|
58
|
-
- lib/djinn/pid_file.rb
|
58
|
+
- lib/djinn/base/dsl.rb
|
59
|
+
- lib/djinn/base/logging.rb
|
60
|
+
- lib/djinn/base/pid_file.rb
|
61
|
+
- lib/djinn/base/tonic.rb
|
59
62
|
- lib/djinn/rails.rb
|
60
63
|
- lib/djinn/rails/handlers.rb
|
61
|
-
- lib/djinn/tonic.rb
|
62
64
|
- test/helper.rb
|
63
65
|
- test/test_djinn.rb
|
64
66
|
has_rdoc: true
|
data/lib/djinn/dsl.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
module Djinn
|
2
|
-
# Defines the awesomesauce DSL for a whole new generation of Djinn
|
3
|
-
module Dsl
|
4
|
-
|
5
|
-
# Start your Djinn definition
|
6
|
-
def djinn &block
|
7
|
-
@dsl_helper = DslHelper.new(&block)
|
8
|
-
@dsl_helper.actions.each do |action, proc|
|
9
|
-
module_eval do
|
10
|
-
define_method "__#{action}!".intern do |*args|
|
11
|
-
instance_exec(&proc)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
class DslHelper
|
18
|
-
|
19
|
-
attr_accessor :actions
|
20
|
-
|
21
|
-
def initialize &block
|
22
|
-
@actions = {}
|
23
|
-
instance_eval(&block)
|
24
|
-
end
|
25
|
-
|
26
|
-
# Define an action that will be performed by a Djinn
|
27
|
-
def on action, &block
|
28
|
-
acceptable_actions = %w(start stop exit)
|
29
|
-
raise DslActionError.new("\"#{action}\" is unrecognized, please use one of: #{acceptable_actions.join(', ')}") \
|
30
|
-
unless acceptable_actions.include?(action.to_s)
|
31
|
-
@actions[action] = block
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
# This error means you screwed something up in your action definition
|
37
|
-
class DslActionError < Exception; end
|
38
|
-
|
39
|
-
end
|
40
|
-
end
|
data/lib/djinn/logging.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
module Djinn
|
2
|
-
# Logging Helper Class of Awesomeness
|
3
|
-
module Logging
|
4
|
-
|
5
|
-
# Log something to STDOUT, or wherever it's been redirected to
|
6
|
-
def log msg
|
7
|
-
puts "#{Time.now.strftime("%m/%d/%Y %H:%M:%S")}: #{msg}"
|
8
|
-
STDOUT.flush
|
9
|
-
end
|
10
|
-
|
11
|
-
# Make some text *green*
|
12
|
-
def green text
|
13
|
-
colorize 32, text
|
14
|
-
end
|
15
|
-
|
16
|
-
# Make some text *red*
|
17
|
-
def red text
|
18
|
-
colorize 31, text
|
19
|
-
end
|
20
|
-
|
21
|
-
# Make some text *cyan*
|
22
|
-
def cyan text
|
23
|
-
colorize 36, text
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def colorize color, text
|
29
|
-
"\033[#{color}m#{text}\033[0m"
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
end
|
data/lib/djinn/pid_file.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
module Djinn
|
2
|
-
# pid files are what bind your Djinn to the material plane
|
3
|
-
class PidFile
|
4
|
-
|
5
|
-
attr_reader :file
|
6
|
-
|
7
|
-
def initialize(file)
|
8
|
-
@file = file
|
9
|
-
end
|
10
|
-
|
11
|
-
def pid
|
12
|
-
File.exists?(@file) and IO.read(@file).to_i
|
13
|
-
end
|
14
|
-
|
15
|
-
def remove
|
16
|
-
File.unlink(@file) if pid
|
17
|
-
end
|
18
|
-
|
19
|
-
def create
|
20
|
-
File.open(@file, "w") { |f| f.write($$) }
|
21
|
-
end
|
22
|
-
|
23
|
-
def ensure_empty(msg = nil)
|
24
|
-
if self.pid
|
25
|
-
$stdout.puts msg if msg
|
26
|
-
exit 1
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
end
|
data/lib/djinn/tonic.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
module Djinn
|
2
|
-
# This play on words kills me everytime. I'm that lame.
|
3
|
-
module Tonic
|
4
|
-
|
5
|
-
# Send your Djinn off to frolic in the ether
|
6
|
-
def daemonize(logfile, pidfile, &block)
|
7
|
-
pidfile.ensure_empty("It looks like I'm already running. Not starting.")
|
8
|
-
puts "Djinn is leaving process #{$$}"
|
9
|
-
|
10
|
-
srand # split rand streams between spawning and daemonized process
|
11
|
-
|
12
|
-
fork do
|
13
|
-
puts "Daemonizing on process #{$$}"
|
14
|
-
# puts system("ps aux | grep #{$$}")
|
15
|
-
|
16
|
-
#Dir.chdir "/" # release old working directory
|
17
|
-
File.umask 0000 # ensure sensible umask
|
18
|
-
|
19
|
-
puts 'Making sure all file descriptors are closed'
|
20
|
-
ObjectSpace.each_object(IO) do |io|
|
21
|
-
unless [STDIN, STDOUT, STDERR].include?(io)
|
22
|
-
begin
|
23
|
-
io.close unless io.closed?
|
24
|
-
rescue ::Exception
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
pidfile.create # write PID file
|
30
|
-
|
31
|
-
# detach from controlling terminal
|
32
|
-
unless sess_id = Process.setsid
|
33
|
-
raise 'Cannot detach from controlling terminal'
|
34
|
-
end
|
35
|
-
|
36
|
-
# redirect IO
|
37
|
-
STDIN.reopen('/dev/null')
|
38
|
-
STDOUT.reopen(logfile, 'a')
|
39
|
-
STDERR.reopen(STDOUT)
|
40
|
-
|
41
|
-
yield
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
46
|
-
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|