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,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