climax 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown ADDED
@@ -0,0 +1,164 @@
1
+ Introduction
2
+ ============
3
+
4
+ Climax is a Ruby gem designed to speed-up the development of command line applications. It provides
5
+ a number of features that nearly all long-running cli application need such as logging, running as a
6
+ daemon, processing command line arguments, and something else unexpected. When your application
7
+ uses climax, a control DRb runs with your application, allowing you to manipulate your application
8
+ as it runs in the background. For instance, if your application is running, you can remotely debug
9
+ the application at any time. This is great for long-running processes in production. Something
10
+ strange going on that you'd like to investigate? Use the control drb to change the log level from
11
+ "info" to "debug" to get more info. Still not sure what's happening? Attach a debugger to the running process.
12
+
13
+ You get all of these features for free when your application uses climax.
14
+
15
+ Installation
16
+ ============
17
+
18
+ To install climax simply install the climax gem: `gem install climax` or alternatively place `gem
19
+ "climax"` in your application's Gemfile and run bundler.
20
+
21
+ Getting Started
22
+ ===============
23
+
24
+ It's easy to get started. Just include the `Climax::Application` module into your application
25
+ class:
26
+
27
+ require 'climax'
28
+
29
+ class MyApplication
30
+ include Climax::Application
31
+
32
+ def main
33
+ log.info "Hello World"
34
+ return 0
35
+ end
36
+ end
37
+
38
+ MyApplication.new(ARGV).run()
39
+
40
+ The above example is about as simple as you can get. Here we define an application and give it a
41
+ `main` method. The `main` method will be called REPEATEDLY until it returns a value other than nil.
42
+ In the example above `main` will only be called once because it returns `0`.
43
+
44
+ If you save the above example in a file and run it with `--help` you will get a list of default
45
+ command line options provided by climax:
46
+
47
+ Usage: my_application [options]
48
+ -d, --daemon Fork application and run in background
49
+ --log_level Set to debug, info, warn, error, or fatal. Default: info.
50
+ --log_file File to log output to. By default logs to stdout.
51
+ --control_port Override the port for the control DRb to listen on. Default is 7249
52
+ -h, --help Display this help message.
53
+
54
+ As you can see climax by default provides some options free of charge. If you would like to modify
55
+ how climax behaves, such as by adding more command line options or maybe removing the option to fork
56
+ your application, you can provide these configurations by defining a method in your application
57
+ class named `configure`. The `configure` method is called by climax before it parses command line
58
+ options, before it sets up logging, basically before it does anything. This means that you cannot
59
+ use climax facilities such as logging in this method because climax has not yet bootstrapped.
60
+
61
+ After the `configure` method is run climax bootstraps the environment for your application. It
62
+ parses the arguments passed from the command line, it sets up logging, forks your application if
63
+ asked to, and starts the control drb unless your application requests otherwise.
64
+
65
+ The climax framework then runs your application as follows:
66
+
67
+ * Calls the `pre_main` method of your application class if it exists. This is an excellent place
68
+ to put code that should run once before your `main` method.
69
+
70
+ * Calls the `main` method of your application class. If this method does not exist climax will
71
+ raise an exception. The `main` method will be called **repeatedly** until it returns a value
72
+ other than nil. If the `main` method returns an integer then your application will exit with
73
+ that integer as the status code. If the `main` method returns a string then your application
74
+ will abort with that string as the message.
75
+
76
+ * Calls the `post_main` method of your application class if it exists. This is a good place to put
77
+ code that should run once when your application is exiting.
78
+
79
+ Here is a slightly larger example that shows more of climax's features:
80
+
81
+ require 'climax'
82
+
83
+ class MyApplication
84
+ include Climax::Application
85
+
86
+ def configure
87
+ options do
88
+ on 'v', 'verbose', 'More verbose'
89
+ end
90
+ end
91
+
92
+ def pre_main
93
+ log.debug "App has initialized and is about to run."
94
+ end
95
+
96
+ def main
97
+ puts "Hello World!"
98
+ sleep 3
99
+ return nil
100
+ end
101
+
102
+ def post_main
103
+ log.debug "App has finished running and is about to exit."
104
+ end
105
+ end
106
+
107
+ The above example is a simple application that adds an extra command line option `--verbose` in the
108
+ `configure` method. The `pre_main` method is simple and just writes a debug log statement. As you
109
+ can see logging has been setup at this point. Then the `main` method is called. Because the `main`
110
+ method in this example always returns nil, this application will run forever until it is stopped
111
+ externally (`Ctrl-C`, `kill -9`, or using the Control DRb). Using `Ctrl-C` and `kill -9` will
112
+ immediately halt execution of the application and `post_main` will never be called. However if the
113
+ Control DRb is used then the application will exit in an orderly fashion and the `post_main` method
114
+ will be called. Notice that for free you can fork this application, change the log level with the
115
+ `--log-level` option, write the logs to a file using the `--log-file` option, and you can start a
116
+ debugger at any time while this application is running using the Control DRb. The Control DRb has a
117
+ lot of power. We'll talk about it more below.
118
+
119
+ Climax Event Queue
120
+ ==================
121
+
122
+ An important concept to understand is that climax handles events that come in from the Control DRb.
123
+ DRb's by necessity run in a separate thread. However, climax makes absolutely no assumptions about
124
+ the thread-safeness of your application. **You do not need to write thread safe applications.**
125
+ Climax is written in such a way that it simply places events into a thread safe queue. Every time
126
+ your `main` method completes, any existing events in the queue are processed. Your `main` method is
127
+ then called again. This process goes on until the `main` method returns a value other than nil or
128
+ an `:exit` or `:quit` event is placed in the event queue.
129
+
130
+ Your application can send events to the climax event queue with the `send_event` method. You must
131
+ pass `send_event` the event type (e.g., `:exit` or `:start_remote_debugger`) and you may also
132
+ optionally pass a payload (i.e., extra data) as a second parameter.
133
+
134
+ For example, if you wish for your application to exit in an orderly fashion **after** the current
135
+ iteration of `main` has had a chance to finish, you can accomplish this by placing an `:exit` event
136
+ onto the event queue and letting your `main` method finish its work. When your `main` method is
137
+ finished the events on the queue will be processed by climax. When climax reads the `:exit` event
138
+ off of the queue it will call your `post_main` method and perform other cleanup tasks and then exit.
139
+
140
+ require 'climax'
141
+
142
+ class MyApplication
143
+ include Climax::Application
144
+
145
+ def main
146
+ send_event(:exit) if about_to_meet_work_quota?
147
+ work = get_some_work
148
+ do_work(work)
149
+ return nil
150
+ end
151
+
152
+ # ...
153
+
154
+ end
155
+
156
+ A consequence of this is that when you issue commands to the Control DRb, your command will not be
157
+ processed until the current iteration of `main` has had a chance to complete. Therefore it is a
158
+ good idea to keep each iteration of `main` as short as possible, although this is certainly not a
159
+ requirement. In other words, if you wish to enter the remote debugger for a running process, the
160
+ debugger will not begin until the current iteration of `main` has completed.
161
+
162
+ This is excellent for stopping a long running process without interrupting its work. By sending an
163
+ :exit or :quit event, whether through the Control DRb or from your application itself, your
164
+ application will be allowed to finish processing its current unit of work before exiting.
data/climax.gemspec CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.version = Climax::VERSION
8
8
  s.authors = ["Alfred J. Fazio"]
9
9
  s.email = ["alfred.fazio@gmail.com"]
10
- s.homepage = "https://github.com/afazio/climax"
10
+ s.homepage = "https://github.com/appriss/climax"
11
11
  s.summary = %q{Ruby command line application framework}
12
12
  s.description = %q{Opinionated framework for Ruby CLI applications that provides logging, cli argument parsing, daemonizing, configuration, testing, and even remote control of long running processes}
13
13
 
@@ -1,3 +1,3 @@
1
1
  module Climax
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/climax.rb CHANGED
@@ -12,20 +12,35 @@ module Climax
12
12
  @args = args.dup
13
13
  options do
14
14
  on :d, :daemon, "Fork application and run in background"
15
- on :control_port=, "Override the port for the control DRb to listen on. Default is 7249", :as => :int, :default => 7249
16
15
  on :log_level=, "Set to debug, info, warn, error, or fatal. Default: info.", :default => "info"
17
16
  on :log_file=, "File to log output to. By default logs to stdout.", :default => nil
17
+ on :control_port=, "Override the port for the control DRb to listen on. Default is 7249", :as => :int, :default => 7249
18
18
  end
19
- post_initialize
20
- slop.parse
21
- @opts = slop
19
+ configure
20
+ _parse_options
21
+ end
22
+
23
+ def _pre_main
22
24
  if daemonize?
23
25
  exit 0 if !Process.fork.nil?
24
26
  log.debug "Running in background (#{$$})"
25
27
  end
28
+
26
29
  log.debug "Starting Control DRb on port #{control_port}"
27
30
  @control_drb = Climax::ControlDRb.new(self, control_port)
28
- run
31
+ end
32
+
33
+ def _post_main
34
+ log.debug "Stopping Control DRb"
35
+ @control_drb.stop_service
36
+ end
37
+
38
+ def _parse_options
39
+ slop.parse!
40
+ @opts = slop
41
+ exit 0 if opts.help?
42
+ rescue => e
43
+ abort("#{e.message}\n#{slop.to_s}")
29
44
  end
30
45
 
31
46
  def daemonize?
@@ -98,7 +113,7 @@ module Climax
98
113
 
99
114
  # Return instance of Slop
100
115
  def slop
101
- @slop ||= Slop.new
116
+ @slop ||= Slop.new(:strict => true, :help => true)
102
117
  end
103
118
 
104
119
  # Method for wrapping calls to on() and banner(). Simply for readability.
@@ -112,9 +127,18 @@ module Climax
112
127
 
113
128
  # Run the application
114
129
  def run
130
+ _pre_main
115
131
  pre_main
116
- _event_loop
132
+ @exit_status = _event_loop
133
+ _post_main
117
134
  post_main
135
+
136
+ exit exit_status if exit_status.is_a? Fixnum
137
+ abort(exit_status.to_s)
138
+ end
139
+
140
+ def exit_status
141
+ @exit_status
118
142
  end
119
143
 
120
144
  # Return current options. nil until ClimaxApplication::new has finished running
@@ -138,21 +162,22 @@ module Climax
138
162
 
139
163
  def _event_loop
140
164
  while true
141
- event = _next_event
142
165
 
143
- unless event.nil?
144
- case event.type
145
- when :set_log_level then log_level = event.payload
146
- when :start_remote_debugger then binding.remote_pry
147
- when :quit then exit
166
+ begin
167
+ event = _next_event
168
+
169
+ unless event.nil?
170
+ case event.type
171
+ when :set_log_level then log_level = event.payload
172
+ when :stop_control_drb then @control_drb && @control_drb.stop_service
173
+ when :start_remote_debugger then binding.remote_pry
174
+ when :quit, :exit then return 0
175
+ end
148
176
  end
149
- end
177
+ end while !event.nil?
150
178
 
151
179
  result = main
152
-
153
- exit result if result.is_a? Fixnum
154
- raise result if result.is_a? String
155
- raise "Unrecognized return value type: (#{result.class}: #{result}). Only recognize strings, ints, or nil" if !result.nil?
180
+ return result if !result.nil?
156
181
  end
157
182
  end
158
183
 
@@ -176,6 +201,18 @@ module Climax
176
201
  @events_mutex ||= Mutex.new
177
202
  end
178
203
 
204
+ def configure
205
+ end
206
+
207
+ def main
208
+ raise "Please implement a main() method for your application."
209
+ end
210
+
211
+ def pre_main
212
+ end
213
+
214
+ def post_main
215
+ end
179
216
 
180
217
  end
181
218
  end
@@ -14,6 +14,7 @@ module HelloWorld
14
14
  def main
15
15
  puts "Hello World!"
16
16
  sleep 3
17
+ return nil
17
18
  end
18
19
  end
19
20
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: climax
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-19 00:00:00.000000000 Z
12
+ date: 2013-02-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pry
@@ -102,6 +102,7 @@ extra_rdoc_files: []
102
102
  files:
103
103
  - .gitignore
104
104
  - Gemfile
105
+ - README.markdown
105
106
  - Rakefile
106
107
  - climax.gemspec
107
108
  - lib/climax.rb
@@ -109,7 +110,7 @@ files:
109
110
  - lib/climax/version.rb
110
111
  - test/bin/hello_world
111
112
  - test/lib/hello_world.rb
112
- homepage: https://github.com/afazio/climax
113
+ homepage: https://github.com/appriss/climax
113
114
  licenses: []
114
115
  post_install_message:
115
116
  rdoc_options: []