jstp 0.5.7 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +26 -3
- data/jstp.gemspec +2 -1
- data/lib/jstp.rb +23 -11
- data/lib/jstp/configuration.rb +68 -0
- data/lib/jstp/controller.rb +49 -11
- data/lib/jstp/dispatch.rb +106 -0
- data/lib/jstp/engine.rb +77 -13
- data/lib/jstp/version.rb +1 -1
- data/lib/reader/jstp/dispatch.rb +36 -0
- data/lib/reader/jstp/engine.rb +66 -0
- data/lib/writer/jstp/dispatch.rb +95 -0
- data/samples/api-1.5/modular_style.rb +22 -0
- data/samples/api-1.5/references.rb +12 -0
- data/samples/api-1.5/sinatra_style.rb +18 -0
- data/samples/api-2.0/a_la_rack.rb +22 -0
- data/samples/api-2.0/clearer_sample.rb +12 -0
- data/samples/api-2.0/middleware.rb +23 -0
- data/samples/api-2.0/references.rb +11 -0
- data/samples/diff.rb +12 -0
- data/samples/hooks.rb +9 -0
- data/samples/micro.rb +6 -0
- data/samples/new_api.rb +62 -0
- data/samples/websocket.rb +11 -0
- data/spec/jstp/engine_spec.rb +39 -0
- data/spec/spec_helper.rb +0 -1
- metadata +40 -32
- data/features/map_a_la_rest.feature +0 -148
- data/features/step_definitions/map_a_la_rest_steps.rb +0 -19
- data/features/support/env.rb +0 -11
- data/lib/jstp/api.rb +0 -25
- data/lib/jstp/base.rb +0 -7
- data/lib/jstp/connector.rb +0 -17
- data/lib/jstp/tcp.rb +0 -12
- data/lib/jstp/web_socket.rb +0 -45
- data/lib/reader/jstp/connector.rb +0 -31
- data/lib/writer/jstp/connector.rb +0 -46
- data/spec/jstp/api_spec.rb +0 -79
- data/spec/jstp/base_spec.rb +0 -5
- data/spec/jstp/connector_spec.rb +0 -16
- data/spec/jstp/tcp_spec.rb +0 -7
- data/spec/jstp/web_socket_spec.rb +0 -78
- data/spec/reader/jstp/connector_spec.rb +0 -32
- data/spec/writer/jstp/connector_spec.rb +0 -71
data/README.md
CHANGED
@@ -49,9 +49,32 @@ Gateways
|
|
49
49
|
|
50
50
|
Every JSTP server knows its own hostname, which should match the first string in the resource array of the message. If the host as received in the message does not corresponds to this server, it should look up for the right server and dispatch it.
|
51
51
|
|
52
|
-
|
52
|
+
API
|
53
|
+
---
|
54
|
+
|
55
|
+
The API of the JSTP Ruby Gem remained undocumented, mainly because right now it is sketchy. Here I write some guidelines:
|
56
|
+
|
57
|
+
- It should be possible to pass a Logger as argument to JSTP so that ingoing and outgoing dispatches and exceptions are logged.
|
58
|
+
- Now that JSTP is an engine, lets drop the DSL in the main object and use the standard Ruby configuration strategy:
|
59
|
+
|
60
|
+
JSTP.config do |config|
|
61
|
+
config.strategy outgoing: :tcp, ingoing: :websocket
|
62
|
+
config.port outgoing: 80, ingoing: 65
|
63
|
+
config.hostname "session.manager"
|
64
|
+
config.logger Logger.new
|
65
|
+
end
|
66
|
+
|
67
|
+
- A block as a middleware should also be configurable in the dispatch, similar to the former implementation.
|
68
|
+
- The engine should be configured with a hostname that will override the linux hostname found automatically by JSTP. This hostname will be used to detect dispatches as aimed to this node.
|
69
|
+
- The engine should automatically forward ingoing dispatches that carry a different hostname from the one of this node to the corresponding node. In this way, each JSTP Node is automatically a gateway.
|
70
|
+
- There should be a DSL in the Controller class for creating aahnd sending a JSTP Dispatch. The library should be able to recognize when an outgoing dispatch is actually aimed at self, so it gets mapped correctly without the need to generate network activity. This DSL should provide also an easy way to switch outgoing strategies.
|
71
|
+
- The message as passed to the Controller's initializer should be made into a Private Class Data. In this context, I should explore a little the idea of mapping the ingoing Dispatch into a class.
|
72
|
+
- Test implementation with Oj JSON parser/dumper.
|
73
|
+
- The Dispatch class should have a Writer an Reader that make it possible to log and parse dispatches from the abbreviated, similar-to-HTTP syntax.
|
74
|
+
- [distant future] Support for SSL/TLS.
|
53
75
|
|
54
|
-
|
76
|
+
## Installation
|
77
|
+
Add this line to your application's Gemfile:
|
55
78
|
|
56
79
|
gem 'jstp'
|
57
80
|
|
@@ -61,4 +84,4 @@ And then execute:
|
|
61
84
|
|
62
85
|
Or install it yourself as:
|
63
86
|
|
64
|
-
$ gem install jstp
|
87
|
+
$ gem install jstp
|
data/jstp.gemspec
CHANGED
@@ -13,8 +13,9 @@ Gem::Specification.new do |gem|
|
|
13
13
|
gem.add_dependency 'discoverer'
|
14
14
|
gem.add_dependency 'symbolmatrix'
|
15
15
|
gem.add_dependency 'uuid'
|
16
|
+
gem.add_dependency 'oj'
|
16
17
|
|
17
|
-
gem.add_development_dependency 'rspec'
|
18
|
+
gem.add_development_dependency 'rspec', '>= 2.1.0'
|
18
19
|
gem.add_development_dependency 'cucumber'
|
19
20
|
|
20
21
|
gem.files = `git ls-files`.split($\)
|
data/lib/jstp.rb
CHANGED
@@ -6,20 +6,32 @@ require 'json'
|
|
6
6
|
require 'discoverer'
|
7
7
|
require 'symbolmatrix'
|
8
8
|
require 'uuid'
|
9
|
-
|
10
|
-
require '
|
11
|
-
require 'jstp/tcp'
|
12
|
-
|
13
|
-
require 'jstp/api'
|
14
|
-
require 'jstp/base'
|
15
|
-
require 'jstp/connector'
|
9
|
+
require 'oj'
|
10
|
+
require 'logger'
|
16
11
|
|
17
12
|
require 'jstp/engine'
|
18
13
|
require 'jstp/controller'
|
14
|
+
require 'jstp/dispatch'
|
15
|
+
require 'jstp/configuration'
|
16
|
+
require 'jstp/version'
|
17
|
+
|
18
|
+
require 'reader/jstp/engine'
|
19
|
+
require 'reader/jstp/dispatch'
|
20
|
+
require 'writer/jstp/dispatch'
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
+
# Node for the JSTP protocol. Reference implementation in Ruby
|
23
|
+
module JSTP
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
+
# Configure the JSTP node. Usage:
|
26
|
+
#
|
27
|
+
# JSTP.config do |config|
|
28
|
+
# config.port :inbound => 33333, :outbound => 33333
|
29
|
+
# config.strategy :inbound => :tcp, :outbound => :websocket
|
30
|
+
# config.logger Logger.new $stdout
|
31
|
+
# config.hostname `hostname`
|
32
|
+
# config.gateway true
|
33
|
+
# end
|
34
|
+
def self.config &block
|
35
|
+
block.call Configuration.instance
|
36
|
+
end
|
25
37
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module JSTP
|
2
|
+
class Configuration
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@port = SymbolMatrix :inbound => 33333, :outbound => 33333
|
7
|
+
@strategy = SymbolMatrix :inbound => :tcp, :outbound => :tcp
|
8
|
+
@logger = Logger.new $stdout
|
9
|
+
@hostname = (`hostname`)[0..-2]
|
10
|
+
@environment = :development
|
11
|
+
@gateway = true
|
12
|
+
end
|
13
|
+
|
14
|
+
def port argument = nil
|
15
|
+
@port = SymbolMatrix argument unless argument.nil?
|
16
|
+
@port
|
17
|
+
end
|
18
|
+
|
19
|
+
def port= argument
|
20
|
+
port argument
|
21
|
+
end
|
22
|
+
|
23
|
+
def strategy argument = nil
|
24
|
+
@strategy = SymbolMatrix argument unless argument.nil?
|
25
|
+
@strategy
|
26
|
+
end
|
27
|
+
|
28
|
+
def strategy= argument
|
29
|
+
strategy argument
|
30
|
+
end
|
31
|
+
|
32
|
+
def hostname argument = nil
|
33
|
+
@hostname = argument unless argument.nil?
|
34
|
+
@hostname
|
35
|
+
end
|
36
|
+
|
37
|
+
def hostname= argument
|
38
|
+
hostname argument
|
39
|
+
end
|
40
|
+
|
41
|
+
def logger argument = nil
|
42
|
+
@logger = argument unless argument.nil?
|
43
|
+
@logger
|
44
|
+
end
|
45
|
+
|
46
|
+
def logger= argument
|
47
|
+
logger argument
|
48
|
+
end
|
49
|
+
|
50
|
+
def gateway argument = nil
|
51
|
+
@gateway = argument unless argument.nil?
|
52
|
+
@gateway
|
53
|
+
end
|
54
|
+
|
55
|
+
def gateway= argument
|
56
|
+
gateway argument
|
57
|
+
end
|
58
|
+
|
59
|
+
def environment argument = nil
|
60
|
+
@environment = argument unless argument.nil?
|
61
|
+
@environment
|
62
|
+
end
|
63
|
+
|
64
|
+
def environment= argument
|
65
|
+
environment argument
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/jstp/controller.rb
CHANGED
@@ -1,17 +1,55 @@
|
|
1
1
|
module JSTP
|
2
2
|
class Controller
|
3
|
-
|
3
|
+
attr_reader :protocol, :method, :referer, :resource, :timestamp, :token, :original, :query, :body, :engine
|
4
4
|
|
5
|
-
def initialize
|
6
|
-
@
|
7
|
-
@
|
8
|
-
|
9
|
-
@
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
5
|
+
def initialize original, query, engine
|
6
|
+
@original = original
|
7
|
+
@engine = engine
|
8
|
+
|
9
|
+
@query = query
|
10
|
+
|
11
|
+
Configuration.instance.logger.info original.to.short
|
12
|
+
end
|
13
|
+
|
14
|
+
# Since it delegates Reader::Dispatch, the method is
|
15
|
+
# @param Method
|
16
|
+
# @param Resource
|
17
|
+
# @param Body
|
18
|
+
# @param Headers
|
19
|
+
def dispatch *args
|
20
|
+
@dispatch = Dispatch.new @original
|
21
|
+
@dispatch.referer = [Configuration.instance.hostname] + (self.class.to_s.split("::") - engine.class.to_s.split("::"))
|
22
|
+
@dispatch.from.array args
|
23
|
+
|
24
|
+
@dispatch
|
25
|
+
end
|
26
|
+
|
27
|
+
def protocol
|
28
|
+
original.protocol
|
29
|
+
end
|
30
|
+
|
31
|
+
def method
|
32
|
+
original.method
|
33
|
+
end
|
34
|
+
|
35
|
+
def referer
|
36
|
+
original.referer
|
37
|
+
end
|
38
|
+
|
39
|
+
def timestamp
|
40
|
+
original.timestamp
|
41
|
+
end
|
42
|
+
|
43
|
+
def token
|
44
|
+
original.token
|
45
|
+
end
|
46
|
+
|
47
|
+
def resource
|
48
|
+
original.resource
|
49
|
+
end
|
50
|
+
|
51
|
+
def body
|
52
|
+
original.body
|
15
53
|
end
|
16
54
|
end
|
17
55
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module JSTP
|
2
|
+
class Dispatch < Hash
|
3
|
+
include Discoverer::Writer
|
4
|
+
include Discoverer::Reader
|
5
|
+
|
6
|
+
def initialize *args
|
7
|
+
|
8
|
+
unless args.empty?
|
9
|
+
if args.length == 1
|
10
|
+
if args.first.is_a? Hash
|
11
|
+
from.hash args.first
|
12
|
+
elsif args.first.is_a? String
|
13
|
+
from.string args.first
|
14
|
+
elsif args.first.is_a? Symbol
|
15
|
+
self.method = args.first
|
16
|
+
end
|
17
|
+
else
|
18
|
+
from.array args
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
self["protocol"] = ["JSTP", "0.1"] unless self.has_key? "protocol"
|
23
|
+
self["timestamp"] = Time.now.to_i
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
to.string
|
28
|
+
end
|
29
|
+
|
30
|
+
def method
|
31
|
+
self["method"]
|
32
|
+
end
|
33
|
+
|
34
|
+
def method= the_method
|
35
|
+
self["method"] = the_method.to_s.upcase
|
36
|
+
end
|
37
|
+
|
38
|
+
def resource
|
39
|
+
self["resource"]
|
40
|
+
end
|
41
|
+
|
42
|
+
def resource= the_resource
|
43
|
+
if the_resource.is_a? Array
|
44
|
+
self["resource"] = the_resource
|
45
|
+
elsif the_resource.is_a? String
|
46
|
+
self["resource"] = the_resource.split "/"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def protocol
|
51
|
+
self["protocol"]
|
52
|
+
end
|
53
|
+
|
54
|
+
def protocol= the_protocol
|
55
|
+
if the_protocol.is_a? Array
|
56
|
+
self["protocol"] = the_protocol
|
57
|
+
elsif the_protocol.is_a? String
|
58
|
+
self["protocol"] = the_protocol.split "/"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def timestamp
|
63
|
+
self["timestamp"]
|
64
|
+
end
|
65
|
+
|
66
|
+
def timestamp= the_timestamp
|
67
|
+
if the_timestamp.is_a? Integer
|
68
|
+
self["timestamp"] = the_timestamp
|
69
|
+
elsif the_timestamp.is_a? Time
|
70
|
+
self["timestamp"] = the_timestamp.to_i
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def referer
|
75
|
+
self["referer"]
|
76
|
+
end
|
77
|
+
|
78
|
+
def referer= the_referer
|
79
|
+
if the_referer.is_a? Array
|
80
|
+
self["referer"] = the_referer
|
81
|
+
elsif the_referer.is_a? String
|
82
|
+
self["referer"] = the_referer.split "/"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def token
|
87
|
+
self["token"]
|
88
|
+
end
|
89
|
+
|
90
|
+
def token= the_token
|
91
|
+
if the_token.is_a? Array
|
92
|
+
self["token"] = the_token
|
93
|
+
elsif the_token.is_a? String
|
94
|
+
self["token"] = the_token.split "/"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def body
|
99
|
+
self["body"]
|
100
|
+
end
|
101
|
+
|
102
|
+
def body= the_body
|
103
|
+
self["body"] = the_body
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/lib/jstp/engine.rb
CHANGED
@@ -1,28 +1,36 @@
|
|
1
1
|
module JSTP
|
2
2
|
class Engine
|
3
|
-
include
|
3
|
+
include Discoverer::Reader
|
4
|
+
|
5
|
+
def initialize test = false
|
6
|
+
@config = Configuration.instance
|
7
|
+
from.send @config.strategy.inbound unless test
|
8
|
+
end
|
4
9
|
|
5
10
|
# Processes the dispatch in the new REST engine way
|
6
|
-
def dispatch message
|
11
|
+
def dispatch message, client
|
7
12
|
host, the_class, query = discover_resource message["resource"].clone
|
8
|
-
|
9
|
-
|
10
|
-
|
13
|
+
if @config.gateway && host != @config.hostname && host != 'localhost'
|
14
|
+
# May be we should gateway this message, if this wasn't aimed at us
|
15
|
+
message.to.send @config.strategy.outbound
|
11
16
|
else
|
12
|
-
|
13
|
-
message
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
if the_class.ancestors.include? JSTP::Controller
|
18
|
+
resource = the_class.new message, query, self
|
19
|
+
resource.send message["method"].downcase.to_sym
|
20
|
+
else
|
21
|
+
if @config.environment == :development
|
22
|
+
raise NotAControllerError, "The resource class #{the_class} for #{message["resource"].join("/")} was found, but is not a JSTP::Controller"
|
23
|
+
else
|
24
|
+
raise NotPermittedError, "The selected resource is forbidden for this type of request"
|
25
|
+
end
|
26
|
+
end
|
19
27
|
end
|
20
28
|
end
|
21
29
|
|
22
30
|
def discover_resource resource_stack
|
23
31
|
response = []
|
24
32
|
response << resource_stack.shift
|
25
|
-
class_stack = "::"
|
33
|
+
class_stack = "::#{self.class}::"
|
26
34
|
query = []
|
27
35
|
resource_stack.each do |item|
|
28
36
|
begin
|
@@ -37,5 +45,61 @@ module JSTP
|
|
37
45
|
response << query
|
38
46
|
return response
|
39
47
|
end
|
48
|
+
|
49
|
+
def clients
|
50
|
+
@clients ||= {}
|
51
|
+
end
|
52
|
+
|
53
|
+
class NotPermittedError < RuntimeError; end
|
54
|
+
class NotAControllerError < RuntimeError; end
|
55
|
+
|
56
|
+
# DSL for configuration
|
57
|
+
def self.port argument = nil
|
58
|
+
Configuration.instance.port argument
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.port= argument
|
62
|
+
Configuration.instance.port = argument
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.strategy argument = nil
|
66
|
+
Configuration.instance.strategy argument
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.strategy= argument
|
70
|
+
Configuration.instance.strategy = argument
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.logger argument = nil
|
74
|
+
Configuration.instance.logger argument
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.logger= argument
|
78
|
+
Configuration.instance.logger = argument
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.hostname argument = nil
|
82
|
+
Configuration.instance.hostname argument
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.hostname= argument
|
86
|
+
Configuration.instance.hostname = argument
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.environment argument = nil
|
90
|
+
Configuration.instance.environment argument
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.environment= argument
|
94
|
+
Configuration.instance.environment = argument
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.gateway argument = nil
|
98
|
+
Configuration.instance.gateway argument
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.gateway= argument
|
102
|
+
Configuration.instance.gateway = argument
|
103
|
+
end
|
40
104
|
end
|
41
105
|
end
|