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,19 +1,27 @@
1
- module Kamerling module Server class TCP < Sock
2
- private
1
+ require 'socket'
2
+ require_relative '../addr'
3
+ require_relative 'sock'
3
4
 
4
- def handle_connection socket
5
- client_addr = Addr[*socket.remote_address.ip_unpack, :TCP]
6
- input = socket.read
7
- handle input, client_addr
8
- ensure
9
- socket.close
10
- end
5
+ module Kamerling
6
+ module Server
7
+ class TCP < Sock
8
+ private
11
9
 
12
- def run_loop
13
- Socket.tcp_server_loop(*addr) { |socket| handle_connection socket }
14
- end
10
+ def handle_connection(socket)
11
+ client_addr = Addr[*socket.remote_address.ip_unpack, :TCP]
12
+ input = socket.read
13
+ handle input, client_addr
14
+ ensure
15
+ socket.close
16
+ end
17
+
18
+ def run_loop
19
+ Socket.tcp_server_loop(*addr) { |socket| handle_connection socket }
20
+ end
15
21
 
16
- def wait_till_started
17
- loop { break if addr.connectable? }
22
+ def wait_till_started
23
+ loop { break if addr.connectable? }
24
+ end
25
+ end
18
26
  end
19
- end end end
27
+ end
@@ -1,20 +1,28 @@
1
- module Kamerling module Server class UDP < Sock
2
- private
1
+ require 'socket'
2
+ require_relative '../addr'
3
+ require_relative 'sock'
3
4
 
4
- def handle_connection socket
5
- input, conn = socket.recvfrom 2**16
6
- client_addr = Addr[conn[3], conn[1], :UDP]
7
- handle input, client_addr
8
- end
5
+ module Kamerling
6
+ module Server
7
+ class UDP < Sock
8
+ private
9
9
 
10
- def run_loop
11
- socket = UDPSocket.new.tap { |server| server.bind(*addr) }
12
- loop { handle_connection socket if IO.select [socket] }
13
- ensure
14
- socket.close if socket
15
- end
10
+ def handle_connection(socket)
11
+ input, conn = socket.recvfrom 2**16
12
+ client_addr = Addr[conn[3], conn[1], :UDP]
13
+ handle input, client_addr
14
+ end
15
+
16
+ def run_loop
17
+ socket = UDPSocket.new.tap { |server| server.bind(*addr) }
18
+ loop { handle_connection socket if IO.select [socket] }
19
+ ensure
20
+ socket.close if socket
21
+ end
16
22
 
17
- def wait_till_started
18
- 200.times { thread.run }
23
+ def wait_till_started
24
+ 200.times { thread.run }
25
+ end
26
+ end
19
27
  end
20
- end end end
28
+ end
@@ -1,48 +1,37 @@
1
- require 'optparse'
2
-
3
- module Kamerling class ServerRunner
4
- def initialize args, classes: def_classes, orm: Sequel, repos: Repos
5
- @args = args
6
- repos.db = orm.connect settings.db
7
- @servers = { http: :TCP, tcp: :TCP, udp: :UDP }.map do |type, prot|
8
- addr = Addr[settings.host, settings.send(type), prot]
9
- classes[type].new addr: addr if addr.port
10
- end.compact
11
- end
12
-
13
- def join
14
- servers.each(&:join)
15
- end
16
-
17
- def start
18
- servers.each(&:start)
19
- self
20
- end
21
-
22
- attr_reader :args, :servers
23
- private :args, :servers
1
+ require 'sequel'
2
+ require_relative 'addr'
3
+ require_relative 'repos'
4
+ require_relative 'server/http'
5
+ require_relative 'server/tcp'
6
+ require_relative 'server/udp'
7
+ require_relative 'settings'
8
+
9
+ module Kamerling
10
+ class ServerRunner
11
+ def initialize(args, classes: def_classes, orm: Sequel, repos: Repos)
12
+ settings = Settings.new(args)
13
+ repos.db = orm.connect(settings.db)
14
+ @servers = settings.server_addrs.map do |type, addr|
15
+ classes[type].new(addr: addr)
16
+ end
17
+ end
24
18
 
25
- private
19
+ def join
20
+ servers.each(&:join)
21
+ end
26
22
 
27
- Settings = Struct.new(*%i(db host http tcp udp))
23
+ def start
24
+ servers.each(&:start)
25
+ self
26
+ end
28
27
 
29
- def def_classes
30
- { http: Server::HTTP, tcp: Server::TCP, udp: Server::UDP }
31
- end
28
+ attr_reader :servers
29
+ private :servers
32
30
 
33
- def defaults
34
- Settings.new 'sqlite::memory:', '127.0.0.1'
35
- end
31
+ private
36
32
 
37
- def settings
38
- @settings ||= defaults.tap do |set|
39
- OptionParser.new do |opt|
40
- opt.on("--db #{set.db}", String, 'database') { |db| set.db = db }
41
- opt.on("--host #{set.host}", String, 'host') { |host| set.host = host }
42
- opt.on('--http 0', Integer, 'HTTP port') { |http| set.http = http }
43
- opt.on('--tcp 0', Integer, 'TCP port') { |tcp| set.tcp = tcp }
44
- opt.on('--udp 0', Integer, 'UDP port') { |udp| set.udp = udp }
45
- end.parse! args
33
+ def def_classes
34
+ { http: Server::HTTP, tcp: Server::TCP, udp: Server::UDP }
46
35
  end
47
36
  end
48
- end end
37
+ end
@@ -0,0 +1,28 @@
1
+ require 'optparse'
2
+ require_relative 'addr'
3
+ require_relative 'value'
4
+
5
+ module Kamerling
6
+ class Settings < Value
7
+ vals db: String, host: String, http: Integer, tcp: Integer, udp: Integer
8
+
9
+ def initialize(args)
10
+ super db: 'sqlite::memory:', host: '127.0.0.1'
11
+ OptionParser.new do |opt|
12
+ opt.on("--db #{db}", String, 'database') { |db| self.db = db }
13
+ opt.on("--host #{host}", String, 'host') { |host| self.host = host }
14
+ opt.on('--http 0', Integer, 'HTTP port') { |http| self.http = http }
15
+ opt.on('--tcp 0', Integer, 'TCP port') { |tcp| self.tcp = tcp }
16
+ opt.on('--udp 0', Integer, 'UDP port') { |udp| self.udp = udp }
17
+ end.parse! args
18
+ end
19
+
20
+ def server_addrs
21
+ {
22
+ http: Addr[host, http, :TCP],
23
+ tcp: Addr[host, tcp, :TCP],
24
+ udp: Addr[host, udp, :UDP],
25
+ }.select { |_, addr| addr.port }
26
+ end
27
+ end
28
+ end
@@ -1,9 +1,9 @@
1
- module Kamerling class Task < UUIDEntity
2
- attribute :data, String
3
- attribute :done, Boolean, default: false
4
- attribute :project, Project
1
+ require_relative 'project'
2
+ require_relative 'uuid_entity'
5
3
 
6
- def to_h
7
- super.reject { |key, _| key == :project }.merge project_uuid: project.uuid
4
+ module Kamerling
5
+ class Task < UUIDEntity
6
+ attrs data: String, done: Boolean, project: Project
7
+ defaults done: false
8
8
  end
9
- end end
9
+ end
@@ -1,29 +1,38 @@
1
- module Kamerling class TaskDispatcher
2
- def initialize net_dispatcher: NetDispatcher, repos: Repos
3
- @net_dispatcher = net_dispatcher
4
- @repos = repos
5
- end
1
+ require_relative 'dispatch'
2
+ require_relative 'message'
3
+ require_relative 'net_dispatcher'
4
+ require_relative 'repos'
5
+
6
+ module Kamerling
7
+ class TaskDispatcher
8
+ def initialize(net_dispatcher: NetDispatcher, repos: Repos)
9
+ @net_dispatcher = net_dispatcher
10
+ @repos = repos
11
+ end
6
12
 
7
- def dispatch
8
- repos.projects.each do |project|
9
- repos.free_clients_for(project).each do |client|
10
- task = repos.next_task_for(project)
11
- dispatch_task client: client, project: project, task: task if task
13
+ def dispatch_all
14
+ repos.projects.each do |project|
15
+ repos.free_clients_for(project).each do |client|
16
+ task = repos.next_task_for(project)
17
+ dispatch_task client: client, project: project, task: task if task
18
+ end
12
19
  end
13
20
  end
14
- end
15
21
 
16
- attr_reader :net_dispatcher, :repos
17
- private :net_dispatcher, :repos
22
+ attr_reader :net_dispatcher, :repos
23
+ private :net_dispatcher, :repos
18
24
 
19
- private
25
+ private
20
26
 
21
- def dispatch_task client: req(:client), project: req(:project),
22
- task: req(:task)
23
- message = Message[client: client, payload: task.data, project: project,
24
- task: task, type: :DATA]
25
- net_dispatcher.dispatch client.addr, message.to_s
26
- client.busy = true
27
- repos << client
27
+ def dispatch_task(client:, project:, task:)
28
+ message = Message.build(client: client, payload: task.data,
29
+ project: project, task: task, type: :DATA)
30
+ dispatch = Dispatch.new(addr: client.addr, client: client,
31
+ project: project, task: task)
32
+ net_dispatcher.dispatch client.addr, message
33
+ client.busy = true
34
+ repos << client
35
+ repos << dispatch
36
+ end
28
37
  end
29
- end end
38
+ end
@@ -1,17 +1,19 @@
1
1
  require 'securerandom'
2
2
 
3
- module Kamerling module UUID
4
- module_function
3
+ module Kamerling
4
+ module UUID
5
+ module_function
5
6
 
6
- def [] bin
7
- bin.unpack('H8H4H4H4H12').join '-'
8
- end
7
+ def [](bin)
8
+ bin.unpack('H8H4H4H4H12').join('-')
9
+ end
9
10
 
10
- def bin uuid
11
- [uuid.tr('-', '')].pack 'H*'
12
- end
11
+ def bin(uuid)
12
+ [uuid.tr('-', '')].pack('H*')
13
+ end
13
14
 
14
- def new
15
- SecureRandom.uuid
15
+ def new
16
+ SecureRandom.uuid
17
+ end
16
18
  end
17
- end end
19
+ end
@@ -1,15 +1,29 @@
1
+ require 'equalizer'
1
2
  require 'virtus'
3
+ require_relative 'uuid'
2
4
 
3
- module Kamerling class UUIDEntity
4
- include Equalizer.new :uuid
5
+ module Kamerling
6
+ class UUIDEntity
7
+ include Equalizer.new(:uuid)
5
8
 
6
- include Virtus.model
9
+ include Virtus.model
7
10
 
8
- attribute :uuid, String, default: -> * { UUID.new }
11
+ attribute :uuid, String, default: -> (*) { UUID.new }
9
12
 
10
- class << self
11
- alias_method :from_h, :new
12
- end
13
+ def self.attrs(hash = {})
14
+ hash.each { |name, klass| attribute name, klass }
15
+ end
16
+
17
+ def self.defaults(hash = {})
18
+ hash.each do |name, default|
19
+ warn_off { attribute name, attribute_set[name].type, default: default }
20
+ end
21
+ end
13
22
 
14
- alias_method :to_h, :attributes
15
- end end
23
+ def to_h
24
+ attributes.map do |key, value|
25
+ value.is_a?(UUIDEntity) ? [key, value.to_h] : [key, value]
26
+ end.to_h
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ require 'virtus'
2
+
3
+ module Kamerling
4
+ class Value
5
+ include Virtus.value_object
6
+
7
+ def self.vals(hash = {})
8
+ values do
9
+ hash.each { |name, klass| attribute name, klass }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -2,5 +2,7 @@ nav
2
2
  ul#clients
3
3
  - clients.each do |client|
4
4
  li
5
- a data-uuid=client.uuid href="/clients/#{client.uuid}" rel='client'
5
+ a(data-addr=client.addr data-busy=client.busy.to_s data-class='client'
6
+ data-type=client.type data-uuid=client.uuid
7
+ href="/clients/#{client.uuid}")
6
8
  = client.busy
@@ -2,10 +2,14 @@ nav
2
2
  ul#clients
3
3
  - clients.each do |client|
4
4
  li
5
- a data-busy=client.busy.to_s data-uuid=client.uuid href="/clients/#{client.uuid}" rel='client'
6
- = client.busy
5
+ a(data-busy=client.busy.to_s data-class='client' data-type=client.type
6
+ data-uuid=client.uuid href="/clients/#{client.uuid}")
7
+ = client.uuid
8
+ = "busy: #{client.busy.to_s}"
7
9
  ul#tasks
8
10
  - tasks.each do |task|
9
11
  li
10
- a data-done=task.done.to_s data-uuid=task.uuid href="/tasks/#{task.uuid}" rel='task'
11
- = task.done
12
+ a(data-class='task' data-done=task.done.to_s data-uuid=task.uuid
13
+ href="/tasks/#{task.uuid}")
14
+ = task.uuid
15
+ = "done: #{task.done.to_s}"
@@ -2,8 +2,14 @@ nav
2
2
  ul#projects
3
3
  - projects.each do |project|
4
4
  li
5
- a data-uuid=project.uuid href="/projects/#{project.uuid}" rel='project'
5
+ a(data-class='project' data-uuid=project.uuid
6
+ href="/projects/#{project.uuid}")
6
7
  = project.name
7
8
 
8
9
  form action='/projects' method='POST'
9
10
  input name='name'
11
+ input name='uuid' type='hidden' value=Kamerling::UUID.new
12
+ input type='submit' value='create'
13
+
14
+ form action='/projects/dispatch' method='POST'
15
+ input type='submit' value='dispatch'
@@ -1,33 +1,43 @@
1
+ require 'socket'
1
2
  require_relative '../spec_helper'
3
+ require_relative '../../lib/kamerling/addr'
2
4
 
3
- module Kamerling describe Addr do
4
- let(:addr) { Addr['localhost', 1981, :TCP] }
5
+ module Kamerling
6
+ describe Addr do
7
+ let(:addr) { Addr['localhost', 1981, :TCP] }
5
8
 
6
- describe '#connectable?' do
7
- it 'is a predicate whether the (TCP) address is connectable' do
8
- server = TCPServer.new(*addr)
9
- addr.must_be :connectable?
10
- server.close
11
- addr.wont_be :connectable?
9
+ describe '#connectable?' do
10
+ it 'is a predicate whether the (TCP) address is connectable' do
11
+ server = TCPServer.new(*addr)
12
+ addr.must_be :connectable?
13
+ server.close
14
+ addr.wont_be :connectable?
15
+ end
12
16
  end
13
- end
14
17
 
15
- describe '#to_a' do
16
- it 'returns host + port for splat use' do
17
- splat = *addr
18
- splat.must_equal ['localhost', 1981]
18
+ describe '#to_a' do
19
+ it 'returns host + port for splat use' do
20
+ splat = *addr
21
+ splat.must_equal ['localhost', 1981]
22
+ end
19
23
  end
20
- end
21
24
 
22
- describe '#to_h' do
23
- it 'returns a Hash with Integer and String values' do
24
- addr.to_h.must_equal host: 'localhost', port: 1981, prot: 'TCP'
25
+ describe '#to_h' do
26
+ it 'returns a Hash with Integer and String values' do
27
+ addr.to_h.must_equal host: 'localhost', port: 1981, prot: 'TCP'
28
+ end
29
+ end
30
+
31
+ describe '#to_s' do
32
+ it 'returns the Addr in URI notation' do
33
+ addr.to_s.must_equal 'tcp://localhost:1981'
34
+ end
25
35
  end
26
- end
27
36
 
28
- describe '#to_s' do
29
- it 'returns the Addr in ‘host:port (protocol)’ notation' do
30
- addr.to_s.must_equal 'localhost:1981 (TCP)'
37
+ describe '#uri' do
38
+ it 'returns and URI representation of the Addr' do
39
+ addr.uri.must_equal URI.parse('tcp://localhost:1981')
40
+ end
31
41
  end
32
42
  end
33
- end end
43
+ end