djinn 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = Djinn
2
2
 
3
- Djinn is a very basic helper for building simple daemons.
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
- == Non-Rails Example (old non-DSL way, will be deprecated soon-ish)
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 Basic
24
-
27
+ class FileDjinn
28
+
25
29
  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
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
- Run it in the foreground like this:
79
+ FileDjinn.djinnify
47
80
 
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:
81
+ Now we can try and start it using:
53
82
 
54
- djinn.start
55
- sleep(10)
56
- djinn.stop
83
+ ruby file_djinn.rb
57
84
 
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:
85
+ Oops, no luck, but you should see something like:
65
86
 
66
- options = {
67
- 'pid_file_path' => 'path/to/pid/file',
68
- 'log_file_path' => 'path/to/log/file'
69
- }
70
-
71
- djinn.start(options)
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
- 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.
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
- 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.
104
+ Awesomesauce. Now, I wonder if we can stop it:
80
105
 
81
- == Non-Rails Example (new DSL awesomesauce)
106
+ ruby file_djinn.rb --stop
82
107
 
83
- Using the same silly example, you can do this:
108
+ == In More Detail
84
109
 
85
- #!/usr/bin/env ruby
110
+ Okay, so lets pull that example apart and look at things in a little more detail..
86
111
 
87
- require 'rubygems'
88
- require 'djinn'
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 Basic
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
- on :start do
96
- while
97
- log "ZOMG! A Djinn?"
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
- Much cleaner and prettier, and no horrible *super* required. Available
110
- actions to the *on* method are :start, :stop and :exit
181
+ (Everything past this point is old shit, will update soon)
182
+
111
183
 
112
- Run it in the foreground in the same way:
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
- djinn = Basic.new
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
- 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
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
- def handle_exit
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
- Lots.
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.0
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.0"
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-28}
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
- $:.unshift(File.dirname(__FILE__))
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
- $:.unshift(File.dirname(__FILE__))
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 = (config.empty?) ? load_config : 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 = (config.empty?) ? load_config : 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 = (config.empty?) ? load_config : 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
- $:.unshift(File.join(File.dirname(__FILE__)))
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
@@ -4,12 +4,16 @@ module Djinn
4
4
 
5
5
  require 'optparse'
6
6
 
7
- def go args=[]
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: 27
4
+ hash: 25
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 0
10
- version: 0.1.0
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-28 00:00:00 +02:00
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
@@ -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
-