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 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