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