02 Configuration - kjellhex/diode GitHub Wiki
Diode supports two types of configuration: application-wide properties and settings that are specific to an instance of a service. That means you may deploy the same service multiple times on different paths, each with different settings. All services have access to the application-level settings.
Code
Service
Let's define a service that can be configured to behave differently based on some settings:
# time-service.rb
class TimeService
def initialize(timezone, city)
@tz = timezone
@city = city
end
def serve(request)
h = { "name": "Time Service", "location": @city } # use the servlet-specific property @city
h["time"] = DateTime.now.new_offset(@tz).to_time.rfc2822
h["stage"] = request[:environment] # use an application-wide property
body = JSON.dump(h)
Diode::Response.new(200, body)
end
end
Server
Now let's use that service multiple times, each with different configuration settings:
# time-server.rb
require 'diode/server'
port=3999
routing = [
[%r{^/cet}, "TimeService", "CET", "Stockholm"],
[%r{^/kst}, "TimeService", "KST", "Seoul"],
[%r{^/}, "TimeService", "UTC", "London"] # catch any other path
]
load 'time-service.rb'
# configure some application-wide properties that are available to all services
settings = {
"environment": "development",
"dbconnect": "postgresql://localhost:5432/work"
}
diode = Diode::Server.new(port, routing, settings)
puts("[+] listening on port #{port}")
diode.start()
puts("\r[.] stopped")
Running the sample
Start the server:
$ ruby time-server.rb
[+] listening on port 3999
And visit http://localhost:3999/cet and http://localhost:3999/kst and http://localhost:3999/ to see the difference.
How does it work?
Extra routing arguments
This server runs the same service with three different configurations. The routing rules match /cet
with an instance of TimeService
which is configured with the third and subsequent items in the routing rule (the inner list).
routing = [
[%r{^/cet}, "TimeService", "CET", "Stockholm"],
[%r{^/kst}, "TimeService", "KST", "Seoul"],
[%r{^/}, "TimeService", "UTC", "London"] # catch any other path
]
The TimeService
is mounted again for Korean Standard Time for Seoul, and finally a catch-all routing rule matches any other path and is configured for Universal Time. So for the first routing rule, the values ["CET","Stockholm"]
are passed as arguments to initialize
method of the TimeService
class which expects a timezone or Offset String and a city name.
def initialize(timezone, city)
@tz = timezone
@city = city
end
Using service settings
The serve
method demonstrates how service-specific settings are used. The instance variables @tz
and @city
were defined when the service was instantiated so they are available for use:
h = { "name": "Simple Example", "location": @city }
h["time"] = DateTime.now.new_offset(@tz).to_time.rfc2822
Using application settings
Of course, application settings could be defined as global variables but you might find it better to define them in a hash and pass the hash to the server. Application-wide settings
are passed as the third argument when creating the Diode::Server
:
settings = {
"environment": "development",
"dbconnect": "postgresql://localhost:5432/work"
}
diode = Diode::Server.new(port, routing, settings)
They are then automatically made available to every service by attaching a copy to each request as the request.env
hash. There is also a convenient shorthand syntax for accessing the settings:
h["stage"] = request.env[:environment]
h["stage"] = request[:environment] # shorthand
Starting and stopping
While we can create the server and start it right away, there are often reasons to split those two steps. In this example, we want to remind the user which port is listening, and report when the server has stopped. You may also want to modify the server before starting it, as we will see later when adding a filter.
puts("[+] listening on port #{port}")
diode.start()
puts("\r[.] stopped")
Next
The next tutorial 03 Rest shows how to handle GET and POST, and how to access request parameters.