kamerling 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.
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