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,46 +1,52 @@
1
+ require 'socket'
1
2
  require_relative '../../spec_helper'
3
+ require_relative '../../../lib/kamerling/addr'
4
+ require_relative '../../../lib/kamerling/handler'
5
+ require_relative '../../../lib/kamerling/message'
6
+ require_relative '../../../lib/kamerling/server/tcp'
2
7
 
3
- module Kamerling describe Server::TCP do
4
- let(:addr) { Addr['localhost', 1981, :TCP] }
8
+ module Kamerling
9
+ describe Server::TCP do
10
+ let(:addr) { Addr['localhost', 1981, :TCP] }
5
11
 
6
- describe '#addr' do
7
- it 'returns the server’s host + port as a TCP addr' do
8
- Server::TCP.new(addr: addr).addr.must_equal addr
12
+ describe '#addr' do
13
+ it 'returns the server’s host + port as a TCP addr' do
14
+ Server::TCP.new(addr: addr).addr.must_equal addr
15
+ end
9
16
  end
10
- end
11
17
 
12
- describe '#start' do
13
- it 'listens on a TCP port and passes received inputs to the handler' do
14
- server = Server::TCP.new addr: addr, handler: handler = fake(:handler)
15
- server.start
16
- s_addr_foo = TCPSocket.open(*server.addr) do |socket|
17
- socket << 'foo'
18
- Addr[*socket.local_address.ip_unpack, :TCP]
18
+ describe '#start' do
19
+ it 'listens on a TCP port and passes received inputs to the handler' do
20
+ server = Server::TCP.new(addr: addr, handler: handler = fake(:handler))
21
+ server.start
22
+ s_addr_foo = TCPSocket.open(*server.addr) do |socket|
23
+ socket << 'DATA'
24
+ Addr[*socket.local_address.ip_unpack, :TCP]
25
+ end
26
+ s_addr_bar = TCPSocket.open(*server.addr) do |socket|
27
+ socket << 'PING'
28
+ Addr[*socket.local_address.ip_unpack, :TCP]
29
+ end
30
+ run_all_threads
31
+ server.stop
32
+ handler.must_have_received :handle, [Message.parse('DATA'), s_addr_foo]
33
+ handler.must_have_received :handle, [Message.parse('PING'), s_addr_bar]
19
34
  end
20
- s_addr_bar = TCPSocket.open(*server.addr) do |socket|
21
- socket << 'bar'
22
- Addr[*socket.local_address.ip_unpack, :TCP]
23
- end
24
- run_all_threads
25
- server.stop
26
- handler.must_have_received :handle, ['foo', s_addr_foo]
27
- handler.must_have_received :handle, ['bar', s_addr_bar]
28
- end
29
35
 
30
- it 'doesn’t blow up on unknown inputs' do
31
- server = Server::TCP.new addr: addr, handler: handler = fake(:handler)
32
- server.start
33
- stub(handler).handle('foo', any(Addr)) { fail Handler::UnknownInput }
34
- TCPSocket.open(*server.addr) { |socket| socket << 'foo' }
35
- server.stop
36
+ it 'doesn’t blow up on unknown inputs' do
37
+ server = Server::TCP.new(addr: addr)
38
+ server.start
39
+ TCPSocket.open(*server.addr) { |socket| socket << 'foo' }
40
+ server.stop
41
+ end
36
42
  end
37
- end
38
43
 
39
- describe '#stop' do
40
- it 'stops the server' do
41
- server = Server::TCP.new(addr: addr).start
42
- server.stop
43
- -> { TCPSocket.open(*addr).close }.must_raise Errno::ECONNREFUSED
44
+ describe '#stop' do
45
+ it 'stops the server' do
46
+ server = Server::TCP.new(addr: addr).start
47
+ server.stop
48
+ -> { TCPSocket.open(*addr).close }.must_raise Errno::ECONNREFUSED
49
+ end
44
50
  end
45
51
  end
46
- end end
52
+ end
@@ -1,44 +1,50 @@
1
+ require 'socket'
1
2
  require_relative '../../spec_helper'
3
+ require_relative '../../../lib/kamerling/addr'
4
+ require_relative '../../../lib/kamerling/handler'
5
+ require_relative '../../../lib/kamerling/message'
6
+ require_relative '../../../lib/kamerling/server/udp'
2
7
 
3
- module Kamerling describe Server::UDP do
4
- let(:addr) { Addr['localhost', 1979, :UDP] }
8
+ module Kamerling
9
+ describe Server::UDP do
10
+ let(:addr) { Addr['localhost', 1979, :UDP] }
5
11
 
6
- describe '#start' do
7
- it 'listens on an UDP port and passes received inputs to the handler' do
8
- server = Server::UDP.new addr: addr, handler: handler = fake(:handler)
9
- server.start
10
- foo = UDPSocket.new
11
- bar = UDPSocket.new
12
- foo.send 'foo', 0, *server.addr
13
- bar.send 'bar', 0, *server.addr
14
- foo_addr = Addr['127.0.0.1', foo.addr[1], :UDP]
15
- bar_addr = Addr['127.0.0.1', bar.addr[1], :UDP]
16
- run_all_threads
17
- server.stop
18
- handler.must_have_received :handle, ['foo', foo_addr]
19
- handler.must_have_received :handle, ['bar', bar_addr]
20
- end
12
+ describe '#start' do
13
+ it 'listens on an UDP port and passes received inputs to the handler' do
14
+ server = Server::UDP.new(addr: addr, handler: handler = fake(:handler))
15
+ server.start
16
+ foo = UDPSocket.new
17
+ bar = UDPSocket.new
18
+ 3.times do
19
+ foo.send 'DATA', 0, *server.addr
20
+ bar.send 'PING', 0, *server.addr
21
+ end
22
+ run_all_threads
23
+ server.stop
24
+ handler.must_have_received :handle, [Message.parse('DATA'), any(Addr)]
25
+ handler.must_have_received :handle, [Message.parse('PING'), any(Addr)]
26
+ end
21
27
 
22
- it 'doesn’t blow up on unknown inputs' do
23
- server = Server::UDP.new addr: addr, handler: handler = fake(:handler)
24
- server.start
25
- stub(handler).handle('foo', any(Addr)) { fail Handler::UnknownInput }
26
- UDPSocket.new.send 'foo', 0, *server.addr
27
- run_all_threads
28
- server.stop
28
+ it 'doesn’t blow up on unknown inputs' do
29
+ server = Server::UDP.new(addr: addr)
30
+ server.start
31
+ UDPSocket.new.send 'foo', 0, *server.addr
32
+ run_all_threads
33
+ server.stop
34
+ end
29
35
  end
30
- end
31
36
 
32
- describe '#stop' do
33
- it 'closes the socket (and thus allows rebinding to it)' do
34
- Server::UDP.new(addr: addr).start.stop
35
- UDPSocket.new.tap { |socket| socket.bind(*addr) }.close
37
+ describe '#stop' do
38
+ it 'closes the socket (and thus allows rebinding to it)' do
39
+ Server::UDP.new(addr: addr).start.stop
40
+ UDPSocket.new.tap { |socket| socket.bind(*addr) }.close
41
+ end
36
42
  end
37
- end
38
43
 
39
- describe '#addr' do
40
- it 'returns the server’s host + port as an UDP addr' do
41
- Server::UDP.new(addr: addr).addr.must_equal addr
44
+ describe '#addr' do
45
+ it 'returns the server’s host + port as an UDP addr' do
46
+ Server::UDP.new(addr: addr).addr.must_equal addr
47
+ end
42
48
  end
43
49
  end
44
- end end
50
+ end
@@ -1,65 +1,74 @@
1
+ require 'sequel'
1
2
  require_relative '../spec_helper'
3
+ require_relative '../../lib/kamerling/addr'
4
+ require_relative '../../lib/kamerling/repos'
5
+ require_relative '../../lib/kamerling/server_runner'
6
+ require_relative '../../lib/kamerling/server/http'
7
+ require_relative '../../lib/kamerling/server/tcp'
8
+ require_relative '../../lib/kamerling/server/udp'
2
9
 
3
- module Kamerling describe ServerRunner do
4
- let(:http) { fake { Server::HTTP } }
5
- let(:tcp) { fake { Server::TCP } }
6
- let(:udp) { fake { Server::UDP } }
7
- let(:http_cl) { fake(as: :class) { Server::HTTP } }
8
- let(:tcp_cl) { fake(as: :class) { Server::TCP } }
9
- let(:udp_cl) { fake(as: :class) { Server::UDP } }
10
- let(:classes) { { http: http_cl, tcp: tcp_cl, udp: udp_cl } }
10
+ module Kamerling
11
+ describe ServerRunner do
12
+ let(:http) { fake { Server::HTTP } }
13
+ let(:tcp) { fake { Server::TCP } }
14
+ let(:udp) { fake { Server::UDP } }
15
+ let(:http_cl) { fake(as: :class) { Server::HTTP } }
16
+ let(:tcp_cl) { fake(as: :class) { Server::TCP } }
17
+ let(:udp_cl) { fake(as: :class) { Server::UDP } }
18
+ let(:classes) { { http: http_cl, tcp: tcp_cl, udp: udp_cl } }
11
19
 
12
- before do
13
- http_addr = Addr['0.0.0.0', 1234, :TCP]
14
- tcp_addr = Addr['0.0.0.0', 3456, :TCP]
15
- udp_addr = Addr['0.0.0.0', 5678, :UDP]
16
- stub(http_cl).new(addr: http_addr) { http }
17
- stub(tcp_cl).new(addr: tcp_addr) { tcp }
18
- stub(udp_cl).new(addr: udp_addr) { udp }
19
- end
20
-
21
- describe '.new' do
22
- it 'hooks to the given database' do
23
- args = %w(--host 0.0.0.0 --db sqlite::memory:)
24
- db = fake { Sequel::SQLite::Database }
25
- orm = fake :sequel, as: :class
26
- stub(orm).connect('sqlite::memory:') { db }
27
- repos = fake :repos, as: :class
28
- ServerRunner.new args, classes: classes, orm: orm, repos: repos
29
- repos.must_have_received :db=, [db]
20
+ before do
21
+ http_addr = Addr['0.0.0.0', 1234, :TCP]
22
+ tcp_addr = Addr['0.0.0.0', 3456, :TCP]
23
+ udp_addr = Addr['0.0.0.0', 5678, :UDP]
24
+ stub(http_cl).new(addr: http_addr) { http }
25
+ stub(tcp_cl).new(addr: tcp_addr) { tcp }
26
+ stub(udp_cl).new(addr: udp_addr) { udp }
30
27
  end
31
- end
32
28
 
33
- describe '#join' do
34
- it 'joins all the created servers' do
35
- args = %w(--host 0.0.0.0 --http 1234)
36
- ServerRunner.new(args, classes: classes).join
37
- http.must_have_received :join, []
38
- tcp.wont_have_received :join, []
39
- udp.wont_have_received :join, []
29
+ describe '.new' do
30
+ it 'hooks to the given database' do
31
+ args = %w(--host 0.0.0.0 --db sqlite::memory:)
32
+ db = fake { Sequel::SQLite::Database }
33
+ orm = fake(:sequel, as: :class)
34
+ stub(orm).connect('sqlite::memory:') { db }
35
+ repos = fake(:repos, as: :class)
36
+ ServerRunner.new args, classes: classes, orm: orm, repos: repos
37
+ repos.must_have_received :db=, [db]
38
+ end
40
39
  end
41
- end
42
40
 
43
- describe '#start' do
44
- it 'starts the servers based on the given command-line parameters' do
45
- args = %w(--host 0.0.0.0 --http 1234 --tcp 3456 --udp 5678)
46
- ServerRunner.new(args, classes: classes).start
47
- http.must_have_received :start, []
48
- tcp.must_have_received :start, []
49
- udp.must_have_received :start, []
41
+ describe '#join' do
42
+ it 'joins all the created servers' do
43
+ args = %w(--host 0.0.0.0 --http 1234)
44
+ ServerRunner.new(args, classes: classes).join
45
+ http.must_have_received :join, []
46
+ tcp.wont_have_received :join, []
47
+ udp.wont_have_received :join, []
48
+ end
50
49
  end
51
50
 
52
- it 'starts only the servers for which the port was given' do
53
- args = %w(--host 0.0.0.0 --http 1234)
54
- ServerRunner.new(args, classes: classes).start
55
- http.must_have_received :start, []
56
- tcp.wont_have_received :start, []
57
- udp.wont_have_received :start, []
58
- end
51
+ describe '#start' do
52
+ it 'starts the servers based on the given command-line parameters' do
53
+ args = %w(--host 0.0.0.0 --http 1234 --tcp 3456 --udp 5678)
54
+ ServerRunner.new(args, classes: classes).start
55
+ http.must_have_received :start, []
56
+ tcp.must_have_received :start, []
57
+ udp.must_have_received :start, []
58
+ end
59
+
60
+ it 'starts only the servers for which the port was given' do
61
+ args = %w(--host 0.0.0.0 --http 1234)
62
+ ServerRunner.new(args, classes: classes).start
63
+ http.must_have_received :start, []
64
+ tcp.wont_have_received :start, []
65
+ udp.wont_have_received :start, []
66
+ end
59
67
 
60
- it 'returns self' do
61
- sr = ServerRunner.new [], classes: classes
62
- sr.start.must_equal sr
68
+ it 'returns self' do
69
+ server_runner = ServerRunner.new([], classes: classes)
70
+ server_runner.start.must_equal server_runner
71
+ end
63
72
  end
64
73
  end
65
- end end
74
+ end
@@ -0,0 +1,36 @@
1
+ require_relative '../spec_helper'
2
+ require_relative '../../lib/kamerling/addr'
3
+ require_relative '../../lib/kamerling/settings'
4
+
5
+ module Kamerling
6
+ describe Settings do
7
+ let(:args) { %w(--db db --host 0.0.0.0 --http 2009 --tcp 1981 --udp 1979) }
8
+
9
+ describe '.new' do
10
+ it 'has sane defaults' do
11
+ Settings.new([]).db.must_equal 'sqlite::memory:'
12
+ Settings.new([]).host.must_equal '127.0.0.1'
13
+ end
14
+
15
+ it 'parses the passed settings' do
16
+ settings = Settings.new(args)
17
+ settings.db.must_equal 'db'
18
+ settings.host.must_equal '0.0.0.0'
19
+ settings.http.must_equal 2009
20
+ settings.tcp.must_equal 1981
21
+ settings.udp.must_equal 1979
22
+ end
23
+ end
24
+
25
+ describe '#server_addrs' do
26
+ it 'returns the server Addrs' do
27
+ Settings.new([]).server_addrs.must_equal({})
28
+ Settings.new(args).server_addrs.must_equal(
29
+ http: Addr['0.0.0.0', 2009, :TCP],
30
+ tcp: Addr['0.0.0.0', 1981, :TCP],
31
+ udp: Addr['0.0.0.0', 1979, :UDP],
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,23 +1,46 @@
1
1
  require_relative '../spec_helper'
2
+ require_relative '../../lib/kamerling/addr'
3
+ require_relative '../../lib/kamerling/client'
4
+ require_relative '../../lib/kamerling/dispatch'
5
+ require_relative '../../lib/kamerling/message'
6
+ require_relative '../../lib/kamerling/net_dispatcher'
7
+ require_relative '../../lib/kamerling/project'
8
+ require_relative '../../lib/kamerling/repos'
9
+ require_relative '../../lib/kamerling/task'
10
+ require_relative '../../lib/kamerling/task_dispatcher'
11
+ require_relative '../../lib/kamerling/uuid'
2
12
 
3
- module Kamerling describe TaskDispatcher do
4
- describe '#dispatch' do
5
- it 'dispatches tasks to free clients and marks them as busy' do
6
- addr = fake :addr
7
- client = fake :client, addr: addr, uuid: UUID['16B client UUID']
8
- project = fake :project, uuid: UUID['16B project UUID']
9
- task = fake :task, data: 'data', uuid: UUID['16B task UUID']
10
- repos = fake :repos, as: :class, projects: [project]
13
+ module Kamerling
14
+ describe TaskDispatcher do
15
+ let(:addr) { Addr.new }
16
+ let(:client) { Client.new(addr: addr) }
17
+ let(:net_dispatcher) { fake(:net_dispatcher, as: :class) }
18
+ let(:project) { Project.new }
19
+ let(:repos) { fake(:repos, as: :class, projects: [project]) }
20
+ let(:task) { Task.new(data: 'data') }
21
+
22
+ before do
11
23
  stub(repos).next_task_for(project) { task }
12
24
  stub(repos).free_clients_for(project) { [client] }
13
- net_dispatcher = fake :net_dispatcher, as: :class
25
+ td = TaskDispatcher.new(net_dispatcher: net_dispatcher, repos: repos)
26
+ td.dispatch_all
27
+ end
28
+
29
+ describe '#dispatch_all' do
30
+ it 'dispatches tasks to free clients' do
31
+ message = Message.build(client: client, payload: 'data',
32
+ project: project, task: task, type: :DATA)
33
+ net_dispatcher.must_have_received :dispatch, [addr, message]
34
+ end
14
35
 
15
- TaskDispatcher.new(net_dispatcher: net_dispatcher, repos: repos).dispatch
36
+ it 'marks clients as busy and persists the change' do
37
+ assert client.busy
38
+ repos.must_have_received :<<, [client]
39
+ end
16
40
 
17
- header = "DATA#{"\0" * 12}16B client UUID16B project UUID16B task "
18
- net_dispatcher.must_have_received :dispatch, [addr, header + 'UUIDdata']
19
- client.must_have_received :busy=, [true]
20
- repos.must_have_received :<<, [client]
41
+ it 'creates and stores a Dispatch object along the way' do
42
+ repos.must_have_received :<<, [any(Dispatch)]
43
+ end
21
44
  end
22
45
  end
23
- end end
46
+ end
@@ -1,9 +1,13 @@
1
1
  require_relative '../spec_helper'
2
+ require_relative '../../lib/kamerling/project'
3
+ require_relative '../../lib/kamerling/task'
2
4
 
3
- module Kamerling describe Task do
4
- describe '#done' do
5
- it 'defaults to false' do
6
- Task.new(data: 'data', project: fake(:project)).done.must_equal false
5
+ module Kamerling
6
+ describe Task do
7
+ describe '#done' do
8
+ it 'defaults to false' do
9
+ refute Task.new(data: 'data', project: Project.new).done
10
+ end
7
11
  end
8
12
  end
9
- end end
13
+ end
@@ -1,35 +1,63 @@
1
1
  require_relative '../spec_helper'
2
+ require_relative '../../lib/kamerling/uuid_entity'
2
3
 
3
- module Kamerling describe UUIDEntity do
4
- describe '.from_h' do
5
- it 'deserialises the object from a Hash' do
6
- Trivial = Class.new(UUIDEntity) { attribute :question, Symbol }
7
- Trivial.from_h(question: :answer).question.must_equal :answer
4
+ module Kamerling
5
+ describe UUIDEntity do
6
+ describe '.attrs' do
7
+ it 'allows defining attributes in a key → class manner' do
8
+ person = Class.new(UUIDEntity) { attrs name: String, born: Integer }
9
+ person.attribute_set.map(&:name).must_equal %i(uuid name born)
10
+ end
8
11
  end
9
- end
10
12
 
11
- describe '.new' do
12
- it 'creates a class with an UUID property defaulting to a random UUID' do
13
- AttrLess = Class.new UUIDEntity
14
- AttrLess.new.uuid.must_match(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/)
15
- AttrLess.new.uuid.wont_equal AttrLess.new.uuid
13
+ describe '.defaults' do
14
+ it 'allows defining attribute defaults' do
15
+ song = Class.new(UUIDEntity) do
16
+ attrs title: String, genre: Symbol
17
+ defaults genre: :ragga
18
+ end
19
+ song.new.genre.must_equal :ragga
20
+ end
16
21
  end
17
- end
18
22
 
19
- describe '#==' do
20
- it 'reports UUID-based euqality' do
21
- Actor = Class.new(UUIDEntity) { attribute :name, Symbol }
22
- Actor.new(name: :laurel).wont_equal Actor.new name: :laurel
23
- uuid = UUID.new
24
- Actor.new(name: :laurel, uuid: uuid)
25
- .must_equal Actor.new name: :hardy, uuid: uuid
23
+ describe '.new' do
24
+ it 'creates a class with an UUID property defaulting to a random UUID' do
25
+ attr_less = Class.new(UUIDEntity)
26
+ attr_less.new.uuid.must_match(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/)
27
+ attr_less.new.uuid.wont_equal attr_less.new.uuid
28
+ end
29
+
30
+ it 'deserialises the object from a Hash' do
31
+ trivial = Class.new(UUIDEntity) { attrs question: Symbol }
32
+ trivial.new(question: :answer).question.must_equal :answer
33
+ end
26
34
  end
27
- end
28
35
 
29
- describe '#to_h' do
30
- it 'serialises the object to a Hash' do
31
- Hashble = Class.new(UUIDEntity) { attribute :param, Symbol }
32
- Hashble.new(param: :val).to_h.must_equal param: :val, uuid: any(String)
36
+ describe '#==' do
37
+ it 'reports UUID-based euqality' do
38
+ actor = Class.new(UUIDEntity) { attrs name: Symbol }
39
+ actor.new(name: :laurel).wont_equal actor.new(name: :laurel)
40
+ uuid = UUID.new
41
+ actor.new(name: :laurel, uuid: uuid)
42
+ .must_equal actor.new(name: :hardy, uuid: uuid)
43
+ end
44
+ end
45
+
46
+ describe '#to_h' do
47
+ it 'serialises the object to a Hash' do
48
+ hashble = Class.new(UUIDEntity) { attrs param: Symbol }
49
+ hashble.new(param: :val).to_h.must_equal param: :val, uuid: any(String)
50
+ end
51
+
52
+ it 'serialises related UUIDEntities' do
53
+ child = Class.new(UUIDEntity) { attrs name: String }
54
+ parent = Class.new(UUIDEntity) { attrs child: child, name: String }
55
+ zosia = child.new(name: 'Zosia')
56
+ marta = parent.new(child: zosia, name: 'Marta')
57
+ zosia_hash = { name: 'Zosia', uuid: zosia.uuid }
58
+ marta_hash = { child: zosia_hash, name: 'Marta', uuid: marta.uuid }
59
+ marta.to_h.must_equal marta_hash
60
+ end
33
61
  end
34
62
  end
35
- end end
63
+ end