servitude 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/README.md +129 -13
- data/config/4.conf +6 -0
- data/config/5.conf +10 -0
- data/examples/1_simple_server +7 -4
- data/examples/2_echo_server +7 -5
- data/examples/3_echo_server_with_cli_and_daemon +98 -0
- data/examples/4_echo_server_with_cli_daemon_and_file_config +98 -0
- data/examples/5_echo_server_with_cli_daemon_and_env_config +114 -0
- data/lib/servitude.rb +13 -10
- data/lib/servitude/base.rb +12 -10
- data/lib/servitude/cli.rb +6 -7
- data/lib/servitude/config_helper.rb +11 -0
- data/lib/servitude/configuration.rb +21 -45
- data/lib/servitude/environment_configuration.rb +13 -0
- data/lib/servitude/pretty_print.rb +36 -0
- data/lib/servitude/server.rb +16 -4
- data/lib/servitude/server_logging.rb +44 -34
- data/lib/servitude/version.rb +1 -1
- data/servitude.gemspec +2 -0
- data/spec/servitude/pretty_print_spec.rb +71 -0
- data/spec/spec_helper.rb +89 -0
- metadata +45 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 11b7ea1e73f25eb1270966889eb4341dd269657a
|
4
|
+
data.tar.gz: ab47729667a4604e5fb23bd1976b160d96d7b6e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c13a26dda70ac70166e95038910e07692809df93cd82ec8b348b863a5fff729acdc5c8df004ed922f7c7f234b93779b7b58aff0de6e750125e480519bd0b0589
|
7
|
+
data.tar.gz: f7cb8d3b33788691a07f726db705d15a18adb5699ce545e9803e72af6210131be950f579f4a586dd2faf1bbef7f776684e92368373354dbd8d098ea0132281cc
|
data/.rspec
ADDED
data/README.md
CHANGED
@@ -95,33 +95,148 @@ In your CLI file (bin/awesome-server):
|
|
95
95
|
|
96
96
|
### Servitude::Configuration
|
97
97
|
|
98
|
-
|
99
|
-
|
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
|
-
|
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
|
-
|
106
|
-
|
114
|
+
def configuration_class
|
115
|
+
AwesomeServer::Configuration
|
116
|
+
end
|
107
117
|
end
|
108
118
|
end
|
109
119
|
|
110
|
-
|
111
|
-
configuration
|
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
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
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
data/config/5.conf
ADDED
data/examples/1_simple_server
CHANGED
@@ -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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
data/examples/2_echo_server
CHANGED
@@ -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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|