kamerling 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -13
  3. data/Gemfile +0 -1
  4. data/Gemfile.lock +64 -64
  5. data/README.md +18 -1
  6. data/Rakefile +5 -3
  7. data/bin/kamerling +1 -1
  8. data/config/reek.yml +20 -3
  9. data/kamerling.gemspec +16 -13
  10. data/lib/kamerling.rb +2 -28
  11. data/lib/kamerling/addr.rb +17 -3
  12. data/lib/kamerling/client.rb +10 -11
  13. data/lib/kamerling/core_extensions/main.rb +14 -14
  14. data/lib/kamerling/dispatch.rb +13 -0
  15. data/lib/kamerling/handler.rb +16 -25
  16. data/lib/kamerling/http_api.rb +50 -34
  17. data/lib/kamerling/logging.rb +36 -16
  18. data/lib/kamerling/mapper.rb +33 -0
  19. data/lib/kamerling/message.rb +50 -37
  20. data/lib/kamerling/migrations/2_results_received_at.rb +7 -0
  21. data/lib/kamerling/migrations/3_dispatches.rb +17 -0
  22. data/lib/kamerling/migrations/4_registrations_registered_at.rb +7 -0
  23. data/lib/kamerling/migrations/5_clients_type.rb +7 -0
  24. data/lib/kamerling/net_dispatcher.rb +12 -7
  25. data/lib/kamerling/project.rb +7 -3
  26. data/lib/kamerling/receiver.rb +38 -10
  27. data/lib/kamerling/registrar.rb +45 -8
  28. data/lib/kamerling/registration.rb +9 -10
  29. data/lib/kamerling/repo.rb +33 -26
  30. data/lib/kamerling/repos.rb +52 -45
  31. data/lib/kamerling/result.rb +10 -11
  32. data/lib/kamerling/server/http.rb +28 -21
  33. data/lib/kamerling/server/sock.rb +32 -24
  34. data/lib/kamerling/server/tcp.rb +23 -15
  35. data/lib/kamerling/server/udp.rb +24 -16
  36. data/lib/kamerling/server_runner.rb +30 -41
  37. data/lib/kamerling/settings.rb +28 -0
  38. data/lib/kamerling/task.rb +7 -7
  39. data/lib/kamerling/task_dispatcher.rb +31 -22
  40. data/lib/kamerling/uuid.rb +13 -11
  41. data/lib/kamerling/uuid_entity.rb +23 -9
  42. data/lib/kamerling/value.rb +13 -0
  43. data/lib/kamerling/views/clients.slim +3 -1
  44. data/lib/kamerling/views/project.slim +8 -4
  45. data/lib/kamerling/views/projects.slim +7 -1
  46. data/spec/kamerling/addr_spec.rb +32 -22
  47. data/spec/kamerling/client_spec.rb +9 -5
  48. data/spec/kamerling/core_extensions/main_spec.rb +18 -13
  49. data/spec/kamerling/dispatch_spec.rb +16 -0
  50. data/spec/kamerling/handler_spec.rb +24 -34
  51. data/spec/kamerling/http_api_spec.rb +94 -73
  52. data/spec/kamerling/logging_spec.rb +93 -62
  53. data/spec/kamerling/mapper_spec.rb +151 -0
  54. data/spec/kamerling/message_spec.rb +73 -49
  55. data/spec/kamerling/net_dispatcher_spec.rb +22 -16
  56. data/spec/kamerling/receiver_spec.rb +29 -19
  57. data/spec/kamerling/registrar_spec.rb +43 -15
  58. data/spec/kamerling/registration_spec.rb +17 -0
  59. data/spec/kamerling/repo_spec.rb +63 -47
  60. data/spec/kamerling/repos_spec.rb +121 -109
  61. data/spec/kamerling/result_spec.rb +16 -0
  62. data/spec/kamerling/server/http_spec.rb +19 -14
  63. data/spec/kamerling/server/tcp_spec.rb +41 -35
  64. data/spec/kamerling/server/udp_spec.rb +40 -34
  65. data/spec/kamerling/server_runner_spec.rb +62 -53
  66. data/spec/kamerling/settings_spec.rb +36 -0
  67. data/spec/kamerling/task_dispatcher_spec.rb +38 -15
  68. data/spec/kamerling/task_spec.rb +9 -5
  69. data/spec/kamerling/uuid_entity_spec.rb +53 -25
  70. data/spec/kamerling/uuid_spec.rb +19 -16
  71. data/spec/kamerling/value_spec.rb +21 -0
  72. data/spec/spec_helper.rb +3 -6
  73. metadata +54 -8
  74. data/lib/kamerling/core_extensions.rb +0 -1
@@ -1,17 +1,17 @@
1
- module Kamerling module CoreExtensions module Main
2
- module_function
1
+ module Kamerling
2
+ module CoreExtensions
3
+ module Main
4
+ module_function
3
5
 
4
- def req param
5
- method = caller.first[/`(.*)'$/, 1]
6
- callsite = is_a?(Class) ? "#{name}.#{method}" : "#{self.class}##{method}"
7
- fail "#{callsite}: param #{param} is required"
6
+ def warn_off
7
+ verbose = $VERBOSE
8
+ $VERBOSE = false
9
+ yield
10
+ ensure
11
+ $VERBOSE = verbose
12
+ end
13
+ end
8
14
  end
15
+ end
9
16
 
10
- def warn_off
11
- verbose = $VERBOSE
12
- $VERBOSE = false
13
- yield
14
- ensure
15
- $VERBOSE = verbose
16
- end
17
- end end end
17
+ include Kamerling::CoreExtensions::Main
@@ -0,0 +1,13 @@
1
+ require_relative 'addr'
2
+ require_relative 'client'
3
+ require_relative 'project'
4
+ require_relative 'task'
5
+ require_relative 'uuid_entity'
6
+
7
+ module Kamerling
8
+ class Dispatch < UUIDEntity
9
+ attrs addr: Addr, client: Client, dispatched_at: Time, project: Project,
10
+ task: Task
11
+ defaults dispatched_at: -> (*) { Time.now }
12
+ end
13
+ end
@@ -1,29 +1,20 @@
1
- module Kamerling class Handler
2
- UnknownInput = Class.new RuntimeError
1
+ require_relative 'receiver'
2
+ require_relative 'registrar'
3
3
 
4
- def initialize receiver: Receiver.new, registrar: Registrar.new
5
- @receiver, @registrar = receiver, registrar
6
- end
7
-
8
- def handle input, addr
9
- process Message.new(input), addr
10
- rescue Message::UnknownType => exception
11
- raise UnknownInput, exception.message
12
- end
13
-
14
- attr_reader :receiver, :registrar
15
- private :receiver, :registrar
16
-
17
- private
4
+ module Kamerling
5
+ class Handler
6
+ def initialize(receiver: Receiver, registrar: Registrar)
7
+ @receiver, @registrar = receiver, registrar
8
+ end
18
9
 
19
- def process message, addr
20
- case message.type
21
- when :RGST
22
- registrar.register addr: addr, client_uuid: message.client_uuid,
23
- project_uuid: message.project_uuid
24
- when :RSLT
25
- receiver.receive addr: addr, client_uuid: message.client_uuid,
26
- data: message.payload, task_uuid: message.task_uuid
10
+ def handle(message, addr)
11
+ case message.type
12
+ when :RGST then registrar.register addr: addr, message: message
13
+ when :RSLT then receiver.receive addr: addr, message: message
14
+ end
27
15
  end
16
+
17
+ attr_reader :receiver, :registrar
18
+ private :receiver, :registrar
28
19
  end
29
- end end
20
+ end
@@ -1,38 +1,54 @@
1
1
  require 'forwardable'
2
2
  require 'sinatra/base'
3
3
  require 'slim'
4
-
5
- module Kamerling class HTTPAPI < Sinatra::Base
6
- extend Forwardable
7
-
8
- configure { set repos: Repos }
9
-
10
- get '/' do
11
- slim :root
12
- end
13
-
14
- get '/clients' do
15
- warn_off { slim :clients, locals: { clients: repos.clients } }
16
- end
17
-
18
- get '/projects' do
19
- warn_off { slim :projects, locals: { projects: repos.projects } }
4
+ require_relative 'project'
5
+ require_relative 'repos'
6
+ require_relative 'task_dispatcher'
7
+ require_relative 'uuid'
8
+
9
+ module Kamerling
10
+ class HTTPAPI < Sinatra::Base
11
+ extend Forwardable
12
+
13
+ delegate %i(task_dispatcher repos) => :settings
14
+
15
+ configure { set task_dispatcher: TaskDispatcher.new, repos: Repos }
16
+
17
+ get '/' do
18
+ render_template :root
19
+ end
20
+
21
+ get '/clients' do
22
+ render_template :clients, locals: { clients: repos.clients }
23
+ end
24
+
25
+ get '/projects' do
26
+ render_template :projects, locals: { projects: repos.projects }
27
+ end
28
+
29
+ get '/projects/:project_uuid' do
30
+ project = repos.project(params['project_uuid'])
31
+ clients = repos.clients_for(project)
32
+ tasks = repos.tasks_for(project)
33
+ render_template :project, locals: { clients: clients, tasks: tasks }
34
+ end
35
+
36
+ post '/projects' do
37
+ name = params.fetch('name')
38
+ uuid = params.fetch('uuid')
39
+ repos << Project.new(name: name, uuid: uuid)
40
+ redirect '/projects'
41
+ end
42
+
43
+ post '/projects/dispatch' do
44
+ task_dispatcher.dispatch_all
45
+ redirect '/projects'
46
+ end
47
+
48
+ private
49
+
50
+ def render_template(template, locals: {})
51
+ warn_off { slim template, locals: locals }
52
+ end
20
53
  end
21
-
22
- get '/projects/:project_uuid' do
23
- project = repos.project params['project_uuid']
24
- clients = repos.clients_for project
25
- tasks = repos.tasks_for project
26
- warn_off { slim :project, locals: { clients: clients, tasks: tasks } }
27
- end
28
-
29
- post '/projects' do
30
- uuid = params.fetch('uuid') { UUID.new }
31
- repos << Project.new(name: params['name'], uuid: uuid)
32
- redirect '/projects'
33
- end
34
-
35
- private
36
-
37
- delegate repos: :settings
38
- end end
54
+ end
@@ -1,32 +1,52 @@
1
1
  require 'after_do'
2
2
  require 'logger'
3
+ require_relative 'net_dispatcher'
4
+ require_relative 'server/sock'
3
5
 
4
- module Kamerling module Logging
5
- module_function
6
+ module Kamerling
7
+ class Logging
8
+ def self.log_to(logger = Logger.new($stdout))
9
+ new logger
10
+ end
6
11
 
7
- def log_to logger = Logger.new($stdout)
8
- log_dispatcher_to logger
9
- log_server_to logger
10
- end
12
+ def initialize(logger)
13
+ @logger = logger
14
+ log_dispatcher
15
+ log_server
16
+ end
17
+
18
+ attr_reader :logger
19
+ private :logger
11
20
 
12
- class << self
13
21
  private
14
22
 
15
- def log_dispatcher_to logger
23
+ def log_dispatcher
16
24
  NetDispatcher.singleton_class.extend AfterDo
17
- NetDispatcher.singleton_class.before :dispatch do |addr, bytes|
18
- logger.debug "sent #{addr} #{bytes}"
25
+ NetDispatcher.singleton_class.before(:dispatch) do |addr, message|
26
+ logger.debug "sent #{addr} #{message.to_hex}"
19
27
  end
20
28
  end
21
29
 
22
- def log_server_to logger
30
+ def log_server
23
31
  Server::Sock.extend AfterDo
32
+ log_server_lifecycle
33
+ log_server_communication
34
+ end
35
+
36
+ def log_server_communication
37
+ Server::Sock.before(:handle) do |input, client_addr|
38
+ begin
39
+ logger.info "connect #{client_addr}"
40
+ logger.debug "received #{client_addr} #{Message.parse(input).to_hex}"
41
+ rescue Message::UnknownType
42
+ logger.debug "received #{client_addr} unknown message type"
43
+ end
44
+ end
45
+ end
46
+
47
+ def log_server_lifecycle
24
48
  Server::Sock.before(:start) { |srv| logger.info "start #{srv.addr}" }
25
49
  Server::Sock.after(:stop) { |srv| logger.info "stop #{srv.addr}" }
26
- Server::Sock.before :handle do |input, client_addr|
27
- logger.info "connect #{client_addr}"
28
- logger.debug "received #{client_addr} #{input}"
29
- end
30
50
  end
31
51
  end
32
- end end
52
+ end
@@ -0,0 +1,33 @@
1
+ require_relative 'addr'
2
+
3
+ module Kamerling
4
+ module Mapper
5
+ module_function
6
+
7
+ def from_h(klass, hash, repos: Repos)
8
+ attributes = hash.map do |key, value|
9
+ case key
10
+ when :host, :port, :prot then [:addr, Addr.new(hash)]
11
+ when /_uuid$/ then object_pair_from(key, value, repos)
12
+ else [key, value]
13
+ end
14
+ end.to_h
15
+ klass.new(attributes)
16
+ end
17
+
18
+ def object_pair_from(key, value, repos)
19
+ type = key[/(.*)_uuid$/, 1].to_sym
20
+ [type, repos[Kamerling.const_get(type.capitalize)][value]]
21
+ end
22
+
23
+ def to_h(object)
24
+ object.to_h.reduce({}) do |hash, (key, value)|
25
+ hash.merge case value
26
+ when Addr then value.to_h
27
+ when Hash then { :"#{key}_uuid" => value[:uuid] }
28
+ else { key => value }
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,47 +1,60 @@
1
- module Kamerling class Message
2
- UnknownType = Class.new RuntimeError
1
+ require 'equalizer'
2
+ require_relative 'uuid'
3
3
 
4
- def self.[] client: req(:client), payload: req(:payload),
5
- project: req(:project), task: req(:task), type: req(:type)
6
- new "#{type}\0\0\0\0\0\0\0\0\0\0\0\0" + UUID.bin(client.uuid) +
7
- UUID.bin(project.uuid) + UUID.bin(task.uuid) + payload
8
- end
4
+ module Kamerling
5
+ class Message
6
+ KNOWN_TYPES = %i(DATA PING RGST RSLT)
7
+ UnknownType = Class.new(RuntimeError)
9
8
 
10
- def initialize raw
11
- @raw = raw
12
- type = raw[0..3]
13
- known_types = %w(DATA PING RGST RSLT)
14
- fail UnknownType, type unless known_types.include? type or type.empty?
15
- end
9
+ include Equalizer.new(:raw)
16
10
 
17
- def == other
18
- raw == other.raw
19
- end
11
+ def self.build(client:, payload:, project:, task:, type:)
12
+ new([type, "\0\0\0\0\0\0\0\0\0\0\0\0", UUID.bin(client.uuid),
13
+ UUID.bin(project.uuid), UUID.bin(task.uuid), payload].join)
14
+ end
20
15
 
21
- def client_uuid
22
- UUID[raw[16..31]]
23
- end
16
+ def self.parse(raw)
17
+ new(raw)
18
+ end
24
19
 
25
- def payload
26
- raw[64..-1]
27
- end
20
+ def initialize(raw)
21
+ @raw = raw
22
+ fail UnknownType, type unless KNOWN_TYPES.include?(type) or type.empty?
23
+ end
28
24
 
29
- def project_uuid
30
- UUID[raw[32..47]]
31
- end
25
+ def client_type
26
+ raw[4..7].to_sym
27
+ end
32
28
 
33
- def task_uuid
34
- UUID[raw[48..63]]
35
- end
29
+ def client_uuid
30
+ UUID[raw[16..31]]
31
+ end
36
32
 
37
- def to_s
38
- raw
39
- end
33
+ def payload
34
+ raw[64..-1]
35
+ end
40
36
 
41
- def type
42
- raw[0..3].to_sym
43
- end
37
+ def project_uuid
38
+ UUID[raw[32..47]]
39
+ end
40
+
41
+ def task_uuid
42
+ UUID[raw[48..63]]
43
+ end
44
+
45
+ def to_hex
46
+ raw.unpack('H*').first.scan(/../).join(' ')
47
+ end
44
48
 
45
- attr_reader :raw
46
- private :raw
47
- end end
49
+ def to_s
50
+ raw
51
+ end
52
+
53
+ def type
54
+ raw[0..3].to_sym
55
+ end
56
+
57
+ attr_reader :raw
58
+ protected :raw
59
+ end
60
+ end
@@ -0,0 +1,7 @@
1
+ Sequel.migration do
2
+ change do
3
+ alter_table :results do
4
+ add_column :received_at, :timestamp
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,17 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table :dispatches do
4
+ uuid :uuid, primary_key: true
5
+ inet :host, null: false
6
+ integer :port, null: false
7
+ string :prot, null: false
8
+ timestamp :dispatched_at, null: false
9
+ foreign_key :client_uuid, :clients, index: true, null: false,
10
+ type: :uuid
11
+ foreign_key :project_uuid, :projects, index: true, null: false,
12
+ type: :uuid
13
+ foreign_key :task_uuid, :tasks, index: true, null: false,
14
+ type: :uuid
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ Sequel.migration do
2
+ change do
3
+ alter_table :registrations do
4
+ add_column :registered_at, :timestamp
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ Sequel.migration do
2
+ change do
3
+ alter_table :clients do
4
+ add_column :type, :string
5
+ end
6
+ end
7
+ end
@@ -1,10 +1,15 @@
1
- module Kamerling module NetDispatcher
2
- module_function
1
+ require 'socket'
3
2
 
4
- def dispatch addr, bytes
5
- case addr.prot
6
- when :TCP then TCPSocket.open(*addr) { |socket| socket << bytes }
7
- when :UDP then UDPSocket.new.send bytes, 0, *addr
3
+ module Kamerling
4
+ module NetDispatcher
5
+ module_function
6
+
7
+ def dispatch(addr, message)
8
+ bytes = message.to_s
9
+ case addr.prot
10
+ when :TCP then TCPSocket.open(*addr) { |socket| socket << bytes }
11
+ when :UDP then UDPSocket.new.send bytes, 0, *addr
12
+ end
8
13
  end
9
14
  end
10
- end end
15
+ end