jstp 0.5.7 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/README.md +26 -3
  2. data/jstp.gemspec +2 -1
  3. data/lib/jstp.rb +23 -11
  4. data/lib/jstp/configuration.rb +68 -0
  5. data/lib/jstp/controller.rb +49 -11
  6. data/lib/jstp/dispatch.rb +106 -0
  7. data/lib/jstp/engine.rb +77 -13
  8. data/lib/jstp/version.rb +1 -1
  9. data/lib/reader/jstp/dispatch.rb +36 -0
  10. data/lib/reader/jstp/engine.rb +66 -0
  11. data/lib/writer/jstp/dispatch.rb +95 -0
  12. data/samples/api-1.5/modular_style.rb +22 -0
  13. data/samples/api-1.5/references.rb +12 -0
  14. data/samples/api-1.5/sinatra_style.rb +18 -0
  15. data/samples/api-2.0/a_la_rack.rb +22 -0
  16. data/samples/api-2.0/clearer_sample.rb +12 -0
  17. data/samples/api-2.0/middleware.rb +23 -0
  18. data/samples/api-2.0/references.rb +11 -0
  19. data/samples/diff.rb +12 -0
  20. data/samples/hooks.rb +9 -0
  21. data/samples/micro.rb +6 -0
  22. data/samples/new_api.rb +62 -0
  23. data/samples/websocket.rb +11 -0
  24. data/spec/jstp/engine_spec.rb +39 -0
  25. data/spec/spec_helper.rb +0 -1
  26. metadata +40 -32
  27. data/features/map_a_la_rest.feature +0 -148
  28. data/features/step_definitions/map_a_la_rest_steps.rb +0 -19
  29. data/features/support/env.rb +0 -11
  30. data/lib/jstp/api.rb +0 -25
  31. data/lib/jstp/base.rb +0 -7
  32. data/lib/jstp/connector.rb +0 -17
  33. data/lib/jstp/tcp.rb +0 -12
  34. data/lib/jstp/web_socket.rb +0 -45
  35. data/lib/reader/jstp/connector.rb +0 -31
  36. data/lib/writer/jstp/connector.rb +0 -46
  37. data/spec/jstp/api_spec.rb +0 -79
  38. data/spec/jstp/base_spec.rb +0 -5
  39. data/spec/jstp/connector_spec.rb +0 -16
  40. data/spec/jstp/tcp_spec.rb +0 -7
  41. data/spec/jstp/web_socket_spec.rb +0 -78
  42. data/spec/reader/jstp/connector_spec.rb +0 -32
  43. 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
- ## Installation
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
- Add this line to your application's Gemfile:
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
@@ -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($\)
@@ -6,20 +6,32 @@ require 'json'
6
6
  require 'discoverer'
7
7
  require 'symbolmatrix'
8
8
  require 'uuid'
9
-
10
- require 'jstp/web_socket'
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
- require 'writer/jstp/connector'
21
- require 'reader/jstp/connector'
22
+ # Node for the JSTP protocol. Reference implementation in Ruby
23
+ module JSTP
22
24
 
23
- class << self
24
- include JSTP::API
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
@@ -1,17 +1,55 @@
1
1
  module JSTP
2
2
  class Controller
3
- include JSTP::API
3
+ attr_reader :protocol, :method, :referer, :resource, :timestamp, :token, :original, :query, :body, :engine
4
4
 
5
- def initialize message
6
- @protocol = message["protocol"]
7
- @method = message["method"]
8
- @referer = message["referer"]
9
- @timestamp = message["timestamp"]
10
- @token = message["token"]
11
- @resource = message["resource"]
12
- @message = message
13
- @response = message.clone
14
- @response["body"] = {}
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
@@ -1,28 +1,36 @@
1
1
  module JSTP
2
2
  class Engine
3
- include Singleton
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
- resource = the_class.new message
9
- if query.empty? && (message["body"].nil? || message["body"].empty?)
10
- resource.send message["method"].downcase.to_sym
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
- resource.send(
13
- message["method"].downcase.to_sym,
14
- {
15
- "body" => message["body"],
16
- "query" => query
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