zss 0.0.2 → 0.0.3

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,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- MjNkNWE0YzBjMWQyYzg4NDRmMmQ0MDJhNDZlOWY1OWY2OTY2NWVhNw==
5
- data.tar.gz: !binary |-
6
- NmFiOWM0N2UwNjU2Y2RhODY2OWE1ZDQyN2FhZjk2YTk1OWRmOTI2MA==
2
+ SHA1:
3
+ metadata.gz: f3fe063c07e36ab09e3f66202e1ec9b936d4124a
4
+ data.tar.gz: 8a03c4d06efc0dd89a84b5bdf311712ae6edb205
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- MDU4Y2IzMTc5NDcyOWMxN2UzZTYzMmY2YWU4ZjJiNzBmYzNjMzA3MGViNDRk
10
- NTAzYTU1NzI4NTVmZmU3NDk2NjQwZGY0NDIyZTJkMmNlYmUzMGE3Yzk5YTAz
11
- YTcxYzI2MzBlMzZlNzdkMGUzYTZkYzYyY2ZmZDc0M2U3NTQwNTU=
12
- data.tar.gz: !binary |-
13
- ZTJkNTk5NDg2NDE3OWU2MWVlMjY0ZWY5OGFiOTNmYmExY2NiYzFlZGY5N2Mz
14
- YjQwMmFlZGM2MDJiZGM4NGEwOWZhNGQxNDE2N2U2YTA0NmZiOWRlNDA1ZDE1
15
- YWRjOTA1NWFiODUxNWU0YTFhMmU5YTExY2Q0OTcyZGY2ZDc4Zjc=
6
+ metadata.gz: f50426fa1b1d3cc52f8c65798ff8943e48c9294e55d00f2e4b4d63def33a1a19a0fc55573873e877420f1c8bdd87e95f884e7586a8c4a7334aa2f12d464a1170
7
+ data.tar.gz: ac111e8fd24c86206b1e874d9f6f01ced6c16b4bcc32cf88fdbad62f1e01a70c14333842cf15cf63d54892d9bc1d2928a52bf1333063109b328d72bd15c80b52
data/.travis.yml CHANGED
@@ -20,8 +20,8 @@ addons:
20
20
  code_climate:
21
21
  repo_token:
22
22
  secure: U6d5emmLhWdFAqrVgtHtXDs1lR2f40is89mZfOp1HbnTQKnClGbmCuEGfeHL8HbaMZjpxFb7g9Ery26E3g1gCsE82sP8SkD0qY46LbWlufRctZWsD4d+TZZZttqo2eNNObKkW5JbzW2pGiRqQb7RCfsxsOnqqhXl3uXVOHK24x4=
23
- deploy:
24
- provider: rubygems
25
- api_key:
26
- secure: OsOTnuz+qiZe4RY2hOhYsv46LJE7eQf3jZAX5oBBxsSaC6V91SgyHgl1mexMM6r1CPvgOlG4b33AU2Um9NIVrO11fwl/psSeh6zrMf46l5tmn+QCtjOuoM1jtKpomYAPXioH0KkZWoRcqPyNZemZx14YJQW0/fLtrc5xUS20Re0=
27
- gemspec: zss.gemspec
23
+ # deploy:
24
+ # provider: rubygems
25
+ # api_key:
26
+ # secure: OsOTnuz+qiZe4RY2hOhYsv46LJE7eQf3jZAX5oBBxsSaC6V91SgyHgl1mexMM6r1CPvgOlG4b33AU2Um9NIVrO11fwl/psSeh6zrMf46l5tmn+QCtjOuoM1jtKpomYAPXioH0KkZWoRcqPyNZemZx14YJQW0/fLtrc5xUS20Re0=
27
+ # gemspec: zss.gemspec
data/Gemfile.lock CHANGED
@@ -1,8 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- zss (0.0.2)
4
+ zss (0.0.3)
5
5
  activesupport
6
+ daemons
7
+ em-zeromq
6
8
  ffi-rzmq
7
9
  hashie
8
10
  msgpack
@@ -20,18 +22,24 @@ GEM
20
22
  codeclimate-test-reporter (0.3.0)
21
23
  simplecov (>= 0.7.1, < 1.0.0)
22
24
  coderay (1.1.0)
25
+ daemons (1.1.9)
23
26
  diff-lcs (1.2.5)
24
27
  docile (1.1.5)
28
+ em-zeromq (0.5.0)
29
+ eventmachine (>= 1.0.0)
30
+ ffi (>= 1.0.0)
31
+ ffi-rzmq (~> 2.0.1)
32
+ eventmachine (1.0.3)
25
33
  ffi (1.9.3)
26
34
  ffi-rzmq (2.0.1)
27
35
  ffi-rzmq-core (>= 1.0.1)
28
36
  ffi-rzmq-core (1.0.3)
29
37
  ffi (~> 1.9)
30
- hashie (3.1.0)
31
- i18n (0.6.9)
38
+ hashie (3.2.0)
39
+ i18n (0.6.11)
32
40
  json (1.8.1)
33
41
  method_source (0.8.2)
34
- minitest (5.3.5)
42
+ minitest (5.4.0)
35
43
  msgpack (0.5.8)
36
44
  multi_json (1.10.1)
37
45
  pry (0.10.0)
data/README.md CHANGED
@@ -76,13 +76,22 @@ PongClient.call("ping/pong", "payload")
76
76
  4. Push to the branch (`git push origin my-new-feature`)
77
77
  5. Create new Pull Request
78
78
 
79
+ ## Deployment
80
+
81
+ Continuous Integration build on master branch is configured to:
82
+ 1. bump gem patch version
83
+ 2. tag version
84
+ 3. deploy to ruby gems
85
+
86
+ Major and Minor version are controlled manually, check next chapter.
87
+
79
88
  ## Bump versioning
80
89
 
81
90
  We use [bump gem](https://github.com/gregorym/bump) to control gem versioning.
82
91
 
83
92
  Bump Patch version
84
93
 
85
- $ bump minor
94
+ $ bump patch
86
95
 
87
96
  Bump Minor version
88
97
 
data/lib/zss/client.rb CHANGED
@@ -1,19 +1,19 @@
1
- require 'zss/socket'
1
+ require_relative 'socket'
2
2
 
3
3
  module ZSS
4
4
  class Client
5
5
 
6
6
  attr_reader :sid, :frontend, :identity, :timeout
7
7
 
8
- def initialize sid, config = nil
9
- @frontend = config.try(:frontend) || Configuration.default.frontend
8
+ def initialize sid, config = {}
9
+ @frontend = config[:frontend] || Configuration.default.frontend
10
10
  @sid = sid.to_s.upcase
11
- @identity = config.try(:identity) || "client"
12
- @timeout = config.try(:timeout) || 1000
11
+ @identity = config[:identity] || "client"
12
+ @timeout = config[:timeout] || 1000
13
13
  end
14
14
 
15
- def call verb, payload = nil, headers: {}, timeout: nil
16
- action = verb.to_s.upcase#.gsub('_', '/')
15
+ def call verb, payload, headers: {}, timeout: nil
16
+ action = verb.to_s.upcase
17
17
  address = Message::Address.new(sid: sid, verb: action)
18
18
 
19
19
  request = Message.new(
@@ -21,7 +21,7 @@ module ZSS
21
21
  headers: headers,
22
22
  payload: payload)
23
23
 
24
- response = socket.call(request)
24
+ response = socket.call(request, timeout)
25
25
  fail ZSS::Error.new(response.status, response.payload) if response.is_error?
26
26
 
27
27
  response.payload
@@ -29,10 +29,12 @@ module ZSS
29
29
 
30
30
  private
31
31
 
32
- def method_missing method, payload = nil, headers: {}
32
+ def method_missing method, *args
33
33
  # since we cannot use / on method names we replace _ with /
34
34
  verb = method.to_s.gsub('_', '/')
35
- call verb, payload, headers: headers
35
+ payload = args[0]
36
+ options = args[1] || {}
37
+ call verb, payload, options
36
38
  end
37
39
 
38
40
  def socket
@@ -2,7 +2,10 @@ module ZSS
2
2
  class Configuration
3
3
 
4
4
  def self.default
5
- Hashie::Mash.new frontend: 'tcp://127.0.0.1:5560'
5
+ Hashie::Mash.new(
6
+ frontend: 'tcp://127.0.0.1:5560',
7
+ backend: 'tcp://127.0.0.1:5559'
8
+ )
6
9
  end
7
10
 
8
11
  end
data/lib/zss/error.rb CHANGED
@@ -1,9 +1,12 @@
1
+ require 'json'
2
+
1
3
  module ZSS
2
4
  class Error < ::StandardError
3
5
 
4
- attr_reader :code, :developer_message, :user_message
6
+ attr_reader :code, :user_message
7
+ attr_accessor :developer_message
5
8
 
6
- def initialize code, payload
9
+ def initialize(code, payload)
7
10
  @code = code.to_i
8
11
  @developer_message = payload.developerMessage
9
12
  @user_message = payload.userMessage
@@ -11,5 +14,23 @@ module ZSS
11
14
  set_backtrace caller
12
15
  end
13
16
 
17
+ def self.[](code)
18
+ data = get_errors[code.to_s]
19
+ Error.new(code.to_i, data.body)
20
+ end
21
+
22
+ private
23
+
24
+ def self.get_errors
25
+ @errors ||= begin
26
+ path = File.join(
27
+ File.dirname(File.absolute_path(__FILE__)),
28
+ 'errors.json'
29
+ )
30
+ file = File.read(path)
31
+ Hashie::Mash.new(JSON.parse(file))
32
+ end
33
+ end
34
+
14
35
  end
15
36
  end
@@ -0,0 +1,59 @@
1
+ {
2
+ "400": {
3
+ "code": 400,
4
+ "body": {
5
+ "developerMessage": "The request cannot be fulfilled due to bad syntax.",
6
+ "userMessage": "An error occured",
7
+ "errorCode": 400
8
+ }
9
+ },
10
+ "401": {
11
+ "code": 401,
12
+ "body": {
13
+ "developerMessage": "User authentication token has expired or is missing",
14
+ "userMessage": "This resource is only available after logging in.",
15
+ "errorCode": 401
16
+ }
17
+ },
18
+ "403": {
19
+ "code": 403,
20
+ "body": {
21
+ "developerMessage": "User does not have enough privileges to access this resource.",
22
+ "userMessage": "You do not have access to this resource.",
23
+ "errorCode": 403
24
+ },
25
+ "headers": {}
26
+ },
27
+ "404": {
28
+ "code": 404,
29
+ "body": {
30
+ "developerMessage": "The resource could not be found.",
31
+ "userMessage": "The content you requested was not found.",
32
+ "errorCode": 404
33
+ }
34
+ },
35
+ "429": {
36
+ "code": 429,
37
+ "body": {
38
+ "developerMessage": "You have sent too many requests in a given amount of time.",
39
+ "userMessage": "Please wait a while before trying to access this content again",
40
+ "errorCode": 429
41
+ }
42
+ },
43
+ "500": {
44
+ "code": 500,
45
+ "body": {
46
+ "developerMessage": "There was an error while processing this request. There is probably something wrong with the API server.",
47
+ "userMessage": "There was an error while processing this request.",
48
+ "errorCode": 500
49
+ }
50
+ },
51
+ "599": {
52
+ "code": 599,
53
+ "body": {
54
+ "developerMessage": "Connection timeout while processing this request.",
55
+ "userMessage": "Connection timeout while processing this request.",
56
+ "errorCode": 599
57
+ }
58
+ }
59
+ }
data/lib/zss/message.rb CHANGED
@@ -31,6 +31,10 @@ module ZSS
31
31
 
32
32
  end
33
33
 
34
+ def req?
35
+ type == Type::REQ
36
+ end
37
+
34
38
  def self.parse(frames)
35
39
 
36
40
  frames.unshift(nil) if frames.length == 7
@@ -0,0 +1,43 @@
1
+ module ZSS
2
+
3
+ class Message
4
+
5
+ class SMI
6
+
7
+ SMI = 'SMI'
8
+
9
+ def self.down sid
10
+ Message.new(
11
+ address: Message::Address.new(
12
+ sid: SMI,
13
+ verb: 'DOWN'
14
+ ),
15
+ payload: sid
16
+ )
17
+ end
18
+
19
+ def self.up sid
20
+ Message.new(
21
+ address: Message::Address.new(
22
+ sid: SMI,
23
+ verb: 'UP'
24
+ ),
25
+ payload: sid
26
+ )
27
+ end
28
+
29
+ def self.heartbeat sid
30
+ Message.new(
31
+ address: Message::Address.new(
32
+ sid: SMI,
33
+ verb: 'HEARTBEAT'
34
+ ),
35
+ payload: sid
36
+ )
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ end
data/lib/zss/router.rb ADDED
@@ -0,0 +1,42 @@
1
+ module ZSS
2
+ class Router
3
+
4
+ def add(context, route, handler = nil)
5
+
6
+ fail "Invalid context!" unless context
7
+ fail "Invalid route: #{route}" unless route
8
+
9
+ handler ||= route.to_sym
10
+
11
+ fail "Invalid handler: #{handler}" unless context.respond_to? handler
12
+
13
+ routes[route.to_s.upcase] = get_proc(context, handler)
14
+ end
15
+
16
+ def get(route)
17
+ handler = routes[route.to_s.upcase]
18
+ return handler if handler
19
+
20
+ error = Error[404]
21
+ error.developer_message = "Invalid route #{route}!"
22
+ fail error
23
+ end
24
+
25
+ private
26
+
27
+ def routes
28
+ @routes ||= {}
29
+ end
30
+
31
+ def get_proc(context, handler)
32
+ receive_headers = context.method(handler).parameters.size == 2
33
+
34
+ if receive_headers
35
+ Proc.new { |p,h| context.send(handler, p, h) }
36
+ else
37
+ Proc.new { |p,h| context.send(handler, p) }
38
+ end
39
+ end
40
+
41
+ end
42
+ end
data/lib/zss/runner.rb ADDED
@@ -0,0 +1,37 @@
1
+ require 'daemons'
2
+
3
+ module ZSS
4
+ class Runner
5
+
6
+ def self.run(proc_name)
7
+ proc_name = proc_name.to_s
8
+ pid_path = log_path = './log'
9
+
10
+ FileUtils.mkdir_p pid_path
11
+ FileUtils.mkdir_p log_path
12
+
13
+ daemon_opts = {
14
+ multiple: true,
15
+ dir_mode: :normal,
16
+ dir: pid_path,
17
+ log_output: true,
18
+ stop_proc: lambda do
19
+ puts "stop #{proc_name} daemon..."
20
+ $stop_requested = true
21
+ end
22
+ }
23
+
24
+ puts "Starting #{proc_name}:\n\tPID: #{pid_path}\n\tLOGS: #{log_path}"
25
+
26
+ Daemons.run_proc proc_name, daemon_opts do
27
+ daemon = ZSS::ServiceRegister.get_service
28
+
29
+ puts "Started #{proc_name} daemon..."
30
+ daemon.run
31
+
32
+ puts "Stoping #{proc_name} daemon"
33
+ exit 0
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,147 @@
1
+ require 'em-zeromq'
2
+ require_relative 'router'
3
+ require_relative 'message/smi'
4
+
5
+ module ZSS
6
+ class Service
7
+
8
+ attr_reader :sid, :heartbeat, :backend, :identity
9
+
10
+ def initialize(sid, config = {})
11
+
12
+ fail Error[500] if sid.blank?
13
+
14
+ @sid = sid.to_s.upcase
15
+ @heartbeat = config.try(:heartbeat) || 1000
16
+ @backend = config.try(:backend) || Configuration.default.backend
17
+ @router = ZSS::Router.new
18
+ @identity = "#{sid}##{SecureRandom.uuid}"
19
+ end
20
+
21
+ def run
22
+ Thread.abort_on_exception = true
23
+
24
+ context = EM::ZeroMQ::Context.new(1)
25
+ fail RuntimeError, 'failed to create create_context' unless context
26
+
27
+ # puts "Starting SID: '#{sid}' ID: '#{identity}'"
28
+ # puts "Env: #{ZSS::Environment.env}"
29
+ # puts "Broker: #{backend}"
30
+
31
+ EM.run do
32
+ # handle interrupts
33
+ Signal.trap("INT") { stop }
34
+ Signal.trap("TERM") { stop }
35
+
36
+ connect_socket context
37
+
38
+ start_heartbeat_worker
39
+
40
+ # send up message
41
+ send Message::SMI.up(sid)
42
+ end
43
+ end
44
+
45
+ def add_route(context, route, handler = nil)
46
+ router.add(context, route, handler)
47
+ end
48
+
49
+ def stop
50
+ timer.cancel if timer
51
+
52
+ # puts "Stoping SID: '#{sid}' ID: '#{socket.identity}'"
53
+ EM.add_timer do
54
+ send Message::SMI.down(sid)
55
+ socket.disconnect backend
56
+ EM::stop
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ attr_accessor :socket, :router, :timer
63
+
64
+ def connect_socket(context)
65
+
66
+ @socket = context.socket ZMQ::DEALER
67
+ fail RuntimeError, 'failed to create socket' unless socket
68
+
69
+ socket.identity = identity
70
+ socket.setsockopt(ZMQ::LINGER, 0)
71
+ socket.on(:message, &method(:handle_frames))
72
+
73
+ socket.connect(backend)
74
+ end
75
+
76
+ def start_heartbeat_worker
77
+ @timer = EventMachine::PeriodicTimer.new(heartbeat / 1000) do
78
+ send Message::SMI.heartbeat(sid)
79
+ end
80
+ end
81
+
82
+ def handle_frames(*frames)
83
+ # we need to close frame to avoid memory leaks
84
+ frames = frames.map do |frame|
85
+ out_frame = frame.copy_out_string
86
+ frame.close
87
+ out_frame
88
+ end
89
+
90
+ handle Message.parse(frames)
91
+ end
92
+
93
+ def handle(message)
94
+ if message.req?
95
+ handle_request(message)
96
+ #else
97
+ # puts "heartbeat response received!"
98
+ end
99
+ rescue ZSS::Error => error
100
+ #puts "Erorr: ZSS::Error raised while processing request: #{e}"
101
+ reply_error error, message
102
+ rescue => e
103
+ #puts "Error while processing request: #{e}"
104
+ reply_error Error[500], message
105
+ end
106
+
107
+ def handle_request(message)
108
+ if message.address.sid != sid
109
+ error = Error[404]
110
+ error.developer_message = "Invalid SID: #{message.address.sid}!"
111
+ fail error
112
+ end
113
+
114
+ # the router returns an handler that receives payload and headers
115
+ handler = router.get(message.address.verb)
116
+ message.payload = handler.call(message.payload, message.headers)
117
+ reply message
118
+ end
119
+
120
+ def reply_error(error, message)
121
+ message.status = error.code
122
+ message.payload = {
123
+ errorCode: error.code,
124
+ userMessage: error.user_message,
125
+ developerMessage: error.developer_message
126
+ }
127
+ message.type = Message::Type::REP
128
+ send message
129
+ end
130
+
131
+ def reply(message)
132
+ #puts "reply #{message}"
133
+ message.status = 200
134
+ message.type = Message::Type::REP
135
+ send message
136
+ end
137
+
138
+ def send(msg)
139
+ frames = msg.to_frames
140
+ #remove identity frame on request
141
+ frames.shift if msg.req?
142
+ success = socket.send_msg(*frames)
143
+ puts "An Error ocurred while sending message" unless success
144
+ end
145
+
146
+ end
147
+ end