servitude 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0875adaf0bae960908e33467f31e8a15dc79bf5e
4
- data.tar.gz: cbde282dc8d7eb9964168bfadcab4bc564d51ea2
3
+ metadata.gz: 11b7ea1e73f25eb1270966889eb4341dd269657a
4
+ data.tar.gz: ab47729667a4604e5fb23bd1976b160d96d7b6e8
5
5
  SHA512:
6
- metadata.gz: ed319f26554dc51dc9bcdbe5db9e03db4c67fce36cab1fe85de5f8924f7b5102167532803781d608b9e2bb4b8b2cd1c727eeda4a8b754bd8f6430c427a318899
7
- data.tar.gz: ed3c76b26403ed32f0eb10437c039b7e53b3625fbf2c8f57a99c0d0b4661e33c3c954151bfc8213d399ddf62e77a122d3c015699e7008946a12ac0bd99f79910
6
+ metadata.gz: c13a26dda70ac70166e95038910e07692809df93cd82ec8b348b863a5fff729acdc5c8df004ed922f7c7f234b93779b7b58aff0de6e750125e480519bd0b0589
7
+ data.tar.gz: f7cb8d3b33788691a07f726db705d15a18adb5699ce545e9803e72af6210131be950f579f4a586dd2faf1bbef7f776684e92368373354dbd8d098ea0132281cc
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/README.md CHANGED
@@ -95,33 +95,148 @@ In your CLI file (bin/awesome-server):
95
95
 
96
96
  ### Servitude::Configuration
97
97
 
98
- The Configuration module provides functionality for creating a configuration class. You must call the ::configurations method and provide configuration
99
- attributes to it. The Configuration module also provides a ::from_file method that allows a configuration to be read from a JSON config file.
98
+ All Servitude servers automatically have a configuration instantiated for them (although it may be empty). The default class for the configuration is
99
+ Servitude::Configuration. In order to define a custom configuration, define a custom configuraiton class (which may inherit from Servitude::Configuration)
100
+ and simply override the Servitude::Server#configuration method in your Server class. Be sure the custom configuration calss accepts the command line
101
+ options and passes them to the super class's initializer or configuration will be completely broken.
100
102
 
101
103
  module AwesomeServer
102
- class Configuration
103
- include Servitude::Configuration
104
+ class Configuration < Servitude::Configuration
105
+ def initialize( cli_options )
106
+ super( cli_options )
107
+ # possibly do something else ...
108
+ end
109
+ end
110
+
111
+ class Server
112
+ include Servitude::Server
104
113
 
105
- configurations :some_config, # attribute with no default
106
- [:another, 'some value'] # attribute with a default
114
+ def configuration_class
115
+ AwesomeServer::Configuration
116
+ end
107
117
  end
108
118
  end
109
119
 
110
- If you want to load your configuration from a JSON config file you may do so by registering a callback (most likely an after_initialize callback). If the
111
- configuration file does not exist an error will be raised by Servitude.
120
+ The Servitude::Configuration class delegates to a [Hashie::Mash](https://github.com/intridea/hashie#mash) backend, which gives it great flexibiltiy.
121
+ Any Hash or JSON like structure can be passed directly into the configuration and work. Thus, one does not have to explicitly define the configuration
122
+ attributes as the configuration will represent exactly what is in the JSON config file. In addition, the command line options are passed into the
123
+ configuration and merged to the configuration that came from a config file (if there is a config file). The merge results in the command line options
124
+ overriding any matching file configurations.
125
+
126
+ For example, given a config file:
127
+
128
+ {
129
+ "key1": "value1",
130
+ "log_level": "info",
131
+ "envs": {
132
+ "development": {
133
+ "key2": "value2",
134
+ },
135
+ "production": {
136
+ "key2": "value3",
137
+ }
138
+ }
139
+ }
140
+
141
+ And command line options of:
142
+
143
+ $ awesome-server start --interactive --log_level debug
144
+
145
+ The configuration result will be:
146
+
147
+ {
148
+ "key1": "value1",
149
+ "log_level": "debug",
150
+ "interactive": true,
151
+ "envs": {
152
+ "development": {
153
+ "key2": "value2",
154
+ },
155
+ "production": {
156
+ "key2": "value3",
157
+ }
158
+ }
159
+ }
160
+
161
+ Notice the log_level has been overridden to the command line option value instead of the file value. Because the command line options are an inherently
162
+ flass structure, any config file options that should be overridden should be at the first level of the JSON structure.
163
+
164
+ Because Hashie::Mash is the backend for the configuration values may be accessed using a hash notation or an object notation.
165
+
166
+ config['key1'] # => "value1"
167
+ config[:key1] # => "value1"
168
+ config.key1 # => "value1"
169
+
170
+ config['development']['key2'] # => "value2"
171
+ config[:development][:key2] # => "value2"
172
+ config.development.key2 # => "value2"
173
+
174
+ The startup banner for a Servitude server automatically outputs the ocnfiguration options in a dot notation format. Continuing our configuraiton example,
175
+ the smart banner would look like:
176
+
177
+ ***
178
+ * Awesome Server started
179
+ *
180
+ * v1.0.0 ©2014 Awesome Company
181
+ *
182
+ * Configuration
183
+ * config: /Users/cjharrelson/development/personal/gems/servitude/config/echo-server.conf
184
+ * log_level: debug
185
+ * log: STDOUT
186
+ * pid: /Users/cjharrelson/development/personal/gems/servitude/tmp/echo-server.pid
187
+ * threads: 1
188
+ * key1: value1
189
+ * envs.development.key2: value2
190
+ * envs.production.key2: value3
191
+ *
192
+ ***
193
+
194
+ You may notice the absence of the interactive value. This is due to filtering built into the start banner output. Several values are already in the
195
+ default_config_filters that are a result of the Trollop implementation of the command line option parsing. If you would like to add additional keys
196
+ to be filterd, override the config_filters method in your server class and provide an array of keys (in dot notation) to filter.
112
197
 
113
198
  module AwesomeServer
114
199
  class Server
115
- include Servitude::Server
116
-
117
- after_initialize do
118
- Configuration.from_file( options[:config] )
200
+ ...
201
+ def config_filters
202
+ %w(
203
+ key1
204
+ envs.development.key2
205
+ )
119
206
  end
207
+ ...
208
+ end
209
+ end
210
+
211
+ ### Servitude::EnvironmentConfiguration
120
212
 
121
- # ...
213
+ Building upon Servitude::Configuration, the EnvironmentConfiguration adds the concept of environments to configuration. In order to use
214
+ EnvironmentConfiguration override #configuration_class in your server class.
215
+
216
+ module AwesomeServer
217
+ class Server
218
+ include Servitude::Server
219
+
220
+ def configuration_class
221
+ Servitude::EnvironmentConfiguration
222
+ end
122
223
  end
123
224
  end
124
225
 
226
+ The command line can except and --environment (-e) switch, although it is not required. If using config file and environemnt, a best practice is to put
227
+ the default environment in your config file so there is a default environment.
228
+
229
+ {
230
+ "environment": "development",
231
+ "development": {
232
+ ...
233
+ },
234
+ "production": {
235
+ ...
236
+ }
237
+ }
238
+
239
+
125
240
  ### Servitude::Server
126
241
 
127
242
  The Server module provides the base functionality for implementing a server, such as configuring the loggers, setting up Unix signal handling, outputting a
@@ -143,6 +258,7 @@ The Server module provides callbacks to utilize in your server implementation:
143
258
 
144
259
  * __before_initialize__: executes just before the initilaization of the server
145
260
  * __after_initialize__: executes immediately after initilaization of the server
261
+ * __before_run: executes just before the run method is called
146
262
  * __before_sleep__: executes just before the main thread sleeps to avoid exiting
147
263
  * __finalize__: executes before server exits
148
264
 
data/config/4.conf ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "key1": "value1",
3
+ "key2": {
4
+ "key3": "value3"
5
+ }
6
+ }
data/config/5.conf ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "key1": "value1",
3
+ "environment": "production",
4
+ "development": {
5
+ "key2": "value2"
6
+ },
7
+ "production": {
8
+ "key2": "value3"
9
+ }
10
+ }
@@ -20,14 +20,17 @@ module SimpleServer
20
20
  APP_FOLDER = 'simple-server'
21
21
  VERSION = '1.0.0'
22
22
 
23
+ PROJECT_ROOT = File.expand_path( '../..', __FILE__ )
24
+
23
25
  boot host_namespace: SimpleServer,
24
26
  app_id: 'simple-server',
25
27
  app_name: 'Simple Server',
26
28
  company: 'LFE',
27
- default_config_path: "/usr/local/etc/#{APP_FOLDER}/#{APP_FOLDER}.conf",
28
- default_log_path: "/usr/local/var/log/#{APP_FOLDER}/#{APP_FOLDER}.log",
29
- default_pid_path: "/usr/local/var/run/#{APP_FOLDER}/#{APP_FOLDER}.pid",
30
- default_thread_count: 1,
29
+ use_config: false,
30
+ default_config_path: nil,
31
+ default_log_path: nil,
32
+ default_pid_path: nil,
33
+ default_thread_count: nil,
31
34
  version_copyright: "v#{VERSION} \u00A9#{Time.now.year} LFE"
32
35
 
33
36
  class Server
@@ -26,14 +26,17 @@ module EchoServer
26
26
  APP_FOLDER = 'echo-server'
27
27
  VERSION = '1.0.0'
28
28
 
29
+ PROJECT_ROOT = File.expand_path( '../..', __FILE__ )
30
+
29
31
  boot host_namespace: EchoServer,
30
32
  app_id: 'echo-server',
31
33
  app_name: 'Echo Server',
32
34
  company: 'LFE',
33
- default_config_path: "/usr/local/etc/#{APP_FOLDER}/#{APP_FOLDER}.conf",
34
- default_log_path: "/usr/local/var/log/#{APP_FOLDER}/#{APP_FOLDER}.log",
35
- default_pid_path: "/usr/local/var/run/#{APP_FOLDER}/#{APP_FOLDER}.pid",
36
- default_thread_count: 1,
35
+ use_config: false,
36
+ default_config_path: nil,
37
+ default_log_path: nil,
38
+ default_pid_path: nil,
39
+ default_thread_count: nil,
37
40
  version_copyright: "v#{VERSION} \u00A9#{Time.now.year} LFE"
38
41
 
39
42
  class Server
@@ -45,7 +48,6 @@ module EchoServer
45
48
  end
46
49
 
47
50
  finalize do
48
- binding.pry
49
51
  info 'Shutting down ...'
50
52
  end
51
53
 
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'servitude'
5
+ require 'socket'
6
+ require 'trollop'
7
+ require 'pry'
8
+
9
+ # The echo server accepts with CLI builds on the 2_echo_server example, addiing a command line interface.
10
+ #
11
+ # Note: Due to TcpServer#accept's implementation, the server is not currently gracefully shutting down as the trap of INT
12
+ # appears to never happen.
13
+ #
14
+ # The CLI has 4 sub-commands: start, stop, status and restart.
15
+ #
16
+ # Using the -i of --interactive switch on the start command will run the server attached in the terminal you execute the command
17
+ # in. Without the interactive switch, the server will run as a daemon. The start, stop, and status commands are used to control
18
+ # the daemonized process. If run in daemon mode, you can tail the log:
19
+ #
20
+ # $ tail -f tmp/echo-server.log
21
+ #
22
+ # Usage:
23
+ # Help:
24
+ # bundle exec examples/3_echo_server_with_cli_and_daemon -h
25
+ #
26
+ # Start (interactive):
27
+ # bundle exec examples/3_echo_server_with_cli_and_daemon start -i
28
+ #
29
+ # Then use telnet to exercise the server:
30
+ # $ telnet localhost 1234
31
+ # Hello World!
32
+ # You said: Hello World!
33
+ # Connection closed by foreign host.
34
+ #
35
+ module EchoServer
36
+
37
+ include Servitude::Base
38
+
39
+ APP_FOLDER = 'echo-server'
40
+ VERSION = '1.0.0'
41
+
42
+ PROJECT_ROOT = File.expand_path( '../..', __FILE__ )
43
+
44
+ boot host_namespace: EchoServer,
45
+ app_id: 'echo-server-with-cli',
46
+ app_name: 'Echo Server With CLI',
47
+ company: 'LFE',
48
+ use_config: false,
49
+ default_config_path: "#{PROJECT_ROOT}}/config/#{APP_FOLDER}.conf",
50
+ default_log_path: "#{PROJECT_ROOT}/tmp/#{APP_FOLDER}.log",
51
+ default_pid_path: "#{PROJECT_ROOT}/tmp/#{APP_FOLDER}.pid",
52
+ default_thread_count: nil,
53
+ version_copyright: "v#{VERSION} \u00A9#{Time.now.year} LFE"
54
+
55
+ class Cli
56
+
57
+ include Servitude::Cli
58
+
59
+ end
60
+
61
+ class Server
62
+
63
+ include Servitude::Server
64
+
65
+ after_initialize do
66
+ @tcp_server = TCPServer.open( 'localhost', '1234' )
67
+ end
68
+
69
+ finalize do
70
+ info 'Shutting down ...'
71
+ end
72
+
73
+ def run
74
+ while client = tcp_server.accept
75
+ line = client.gets
76
+ info "Received '#{line.strip}'"
77
+ response = "You said: #{line.strip}"
78
+ client.puts response
79
+ info "Responded with '#{response}'"
80
+ info "Closing connection"
81
+ client.close
82
+ end
83
+ end
84
+
85
+ protected
86
+
87
+ def config_filters
88
+ %w(threads)
89
+ end
90
+
91
+ private
92
+
93
+ attr_reader :tcp_server
94
+
95
+ end
96
+ end
97
+
98
+ EchoServer::Cli.new( ARGV ).run
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'servitude'
5
+ require 'socket'
6
+ require 'trollop'
7
+ require 'pry'
8
+
9
+ # The echo server accepts with CLI builds on the 2_echo_server example, addiing a command line interface.
10
+ #
11
+ # Note: Due to TcpServer#accept's implementation, the server is not currently gracefully shutting down as the trap of INT
12
+ # appears to never happen.
13
+ #
14
+ # The CLI has 4 sub-commands: start, stop, status and restart.
15
+ #
16
+ # Using the -i of --interactive switch on the start command will run the server attached in the terminal you execute the command
17
+ # in. Without the interactive switch, the server will run as a daemon. The start, stop, and status commands are used to control
18
+ # the daemonized process. If run in daemon mode, you can tail the log:
19
+ #
20
+ # $ tail -f tmp/echo-server.log
21
+ #
22
+ # Usage:
23
+ # Help:
24
+ # bundle exec examples/4_echo_server_with_cli_daemon_and_config -h
25
+ #
26
+ # Start (interactive):
27
+ # bundle exec examples/4_echo_server_with_cli_daemon_and_config start -i
28
+ #
29
+ # Then use telnet to exercise the server:
30
+ # $ telnet localhost 1234
31
+ # Hello World!
32
+ # You said: Hello World!
33
+ # Connection closed by foreign host.
34
+ #
35
+ module EchoServer
36
+
37
+ include Servitude::Base
38
+
39
+ APP_FOLDER = 'echo-server'
40
+ VERSION = '1.0.0'
41
+
42
+ PROJECT_ROOT = File.expand_path( '../..', __FILE__ )
43
+
44
+ boot host_namespace: EchoServer,
45
+ app_id: 'echo-server-with-cli-and-file-config',
46
+ app_name: 'Echo Server With CLI And File Config',
47
+ company: 'LFE',
48
+ use_config: true,
49
+ default_config_path: "#{PROJECT_ROOT}/config/4.conf",
50
+ default_log_path: "#{PROJECT_ROOT}/tmp/#{APP_FOLDER}.log",
51
+ default_pid_path: "#{PROJECT_ROOT}/tmp/#{APP_FOLDER}.pid",
52
+ default_thread_count: nil,
53
+ version_copyright: "v#{VERSION} \u00A9#{Time.now.year} LFE"
54
+
55
+ class Cli
56
+
57
+ include Servitude::Cli
58
+
59
+ end
60
+
61
+ class Server
62
+
63
+ include Servitude::Server
64
+
65
+ after_initialize do
66
+ @tcp_server = TCPServer.open( 'localhost', '1234' )
67
+ end
68
+
69
+ finalize do
70
+ info 'Shutting down ...'
71
+ end
72
+
73
+ def run
74
+ while client = tcp_server.accept
75
+ line = client.gets
76
+ info "Received '#{line.strip}'"
77
+ response = "You said: #{line.strip}"
78
+ client.puts response
79
+ info "Responded with '#{response}'"
80
+ info "Closing connection"
81
+ client.close
82
+ end
83
+ end
84
+
85
+ protected
86
+
87
+ def config_filters
88
+ %w(threads)
89
+ end
90
+
91
+ private
92
+
93
+ attr_reader :tcp_server
94
+
95
+ end
96
+ end
97
+
98
+ EchoServer::Cli.new( ARGV ).run