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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9be4e3ac2863218c3d390f4afb2e80f8da0c93be
4
- data.tar.gz: 98c18518041d91d383daa9c79c5bd46999de4b75
3
+ metadata.gz: 7e6dbf946f5fa033f8c544564030c65e230ac10c
4
+ data.tar.gz: 8dd41ffe1684f4ec003a63fed7e8058bfdc8dc88
5
5
  SHA512:
6
- metadata.gz: e1b0aeb3ea6a079fd8999f326e6800edcc9f6b56ad1ab0ed997fbf71223e571ae44a7e8c02dc0f5d7d11c65e4dc98b0eae55be57e532bfd90a8d9e61030a56b6
7
- data.tar.gz: ffe10141b36930b5bf9b4b904312ca96e62e652b51b35f1fceab122fabd7a77258cd9a137936ce6af9c48fefc5968784f189c619cb3685c53091fea66fabcde0
6
+ metadata.gz: 2c6994c506f7bc2af1b96ac2e3f74cb2fdafe08b7efb1a46c10340c6bdc1829bcb1151b4898be5094ad9d00cf07955b0fc1fa3136b0acdea727c10871e3a3377
7
+ data.tar.gz: 30e1fd388e616fab264f7d4d56652f093638cf88601b68cedf65158a888cafcb81e45da173c13775e33770959cde970c0b5e249b1a5551278e5e3291efd7b0b0
data/.rubocop.yml CHANGED
@@ -1,5 +1,6 @@
1
- AccessModifierIndentation:
2
- Enabled: false
1
+ AllCops:
2
+ Exclude:
3
+ - tmp/**/*
3
4
 
4
5
  AndOr:
5
6
  Enabled: false
@@ -7,18 +8,8 @@ AndOr:
7
8
  Documentation:
8
9
  Enabled: false
9
10
 
10
- EndAlignment:
11
+ SingleSpaceBeforeFirstArg:
11
12
  Enabled: false
12
13
 
13
- HandleExceptions:
14
- Exclude:
15
- - lib/kamerling/server/sock.rb
16
-
17
- IndentationWidth:
18
- Enabled: false
19
-
20
- MethodDefParentheses:
21
- EnforcedStyle: require_no_parentheses
22
-
23
14
  TrailingComma:
24
15
  EnforcedStyleForMultiline: comma
data/Gemfile CHANGED
@@ -1,3 +1,2 @@
1
1
  source 'https://rubygems.org'
2
2
  gemspec
3
- gem 'virtus', github: 'solnic/virtus'
data/Gemfile.lock CHANGED
@@ -1,18 +1,9 @@
1
- GIT
2
- remote: git://github.com/solnic/virtus.git
3
- revision: c31940a63d1a4a2ac30032a0c01a9a217b5f5f82
4
- specs:
5
- virtus (1.0.2)
6
- axiom-types (~> 0.1)
7
- coercible (~> 1.0)
8
- descendants_tracker (~> 0.0.3)
9
- equalizer (~> 0.0.9)
10
-
11
1
  PATH
12
2
  remote: .
13
3
  specs:
14
- kamerling (0.0.2)
4
+ kamerling (0.0.3)
15
5
  after_do (~> 0.3.0)
6
+ equalizer (~> 0.0.9)
16
7
  sequel (~> 4.4)
17
8
  sinatra (~> 1.4)
18
9
  slim (~> 2.0)
@@ -22,88 +13,97 @@ PATH
22
13
  GEM
23
14
  remote: https://rubygems.org/
24
15
  specs:
25
- after_do (0.3.0)
26
- ast (1.1.0)
27
- atomic (1.1.16)
28
- axiom-types (0.1.0)
29
- descendants_tracker (~> 0.0.3)
16
+ after_do (0.3.1)
17
+ ast (2.0.0)
18
+ astrolabe (1.3.0)
19
+ parser (>= 2.2.0.pre.3, < 3.0)
20
+ axiom-types (0.1.1)
21
+ descendants_tracker (~> 0.0.4)
30
22
  ice_nine (~> 0.11.0)
31
- thread_safe (~> 0.1.3)
32
- bogus (0.1.4)
23
+ thread_safe (~> 0.3, >= 0.3.1)
24
+ bogus (0.1.5)
33
25
  dependor (>= 0.0.4)
34
- celluloid (0.15.2)
35
- timers (~> 1.1.0)
36
- celluloid-io (0.15.0)
37
- celluloid (>= 0.15.0)
38
- nio4r (>= 0.5.0)
26
+ celluloid (0.16.0)
27
+ timers (~> 4.0.0)
28
+ coderay (1.1.0)
39
29
  coercible (1.0.0)
40
30
  descendants_tracker (~> 0.0.1)
41
31
  dependor (1.0.1)
42
- descendants_tracker (0.0.3)
32
+ descendants_tracker (0.0.4)
33
+ thread_safe (~> 0.3, >= 0.3.1)
43
34
  equalizer (0.0.9)
44
- ffi (1.9.3)
35
+ ffi (1.9.6)
36
+ hitimes (1.2.2)
45
37
  ice_nine (0.11.0)
46
- json (1.8.1)
47
- listen (2.7.1)
38
+ listen (2.7.11)
48
39
  celluloid (>= 0.15.2)
49
- celluloid-io (>= 0.15.0)
50
40
  rb-fsevent (>= 0.9.3)
51
41
  rb-inotify (>= 0.9)
52
- mini_portile (0.5.2)
53
- minitest (5.3.1)
42
+ method_source (0.8.2)
43
+ mini_portile (0.6.1)
44
+ minitest (5.4.2)
54
45
  minitest-focus (1.1.0)
55
46
  minitest (>= 4, < 6)
56
- nio4r (1.0.0)
57
- nokogiri (1.6.1)
58
- mini_portile (~> 0.5.0)
59
- parser (2.1.7)
60
- ast (~> 1.1)
47
+ nokogiri (1.6.4.1)
48
+ mini_portile (~> 0.6.0)
49
+ parser (2.2.0.pre.7)
50
+ ast (>= 1.1, < 3.0)
61
51
  slop (~> 3.4, >= 3.4.5)
62
52
  powerpack (0.0.9)
53
+ pry (0.10.1)
54
+ coderay (~> 1.1.0)
55
+ method_source (~> 0.8.1)
56
+ slop (~> 3.4)
63
57
  rack (1.5.2)
64
- rack-protection (1.5.2)
58
+ rack-protection (1.5.3)
65
59
  rack
66
60
  rack-test (0.6.2)
67
61
  rack (>= 1.0)
68
62
  rainbow (2.0.0)
69
- rake (10.1.1)
63
+ rake (10.3.2)
70
64
  rb-fsevent (0.9.4)
71
- rb-inotify (0.9.3)
65
+ rb-inotify (0.9.5)
72
66
  ffi (>= 0.5.0)
73
- reek (1.3.6)
74
- ruby2ruby (~> 2.0.7)
75
- ruby_parser (~> 3.2)
76
- sexp_processor
77
- rerun (0.9.0)
78
- listen (~> 2.7)
79
- rubocop (0.19.1)
80
- json (>= 1.7.7, < 2)
81
- parser (~> 2.1.7)
67
+ reek (1.4.0)
68
+ rainbow (>= 1.99, < 3.0)
69
+ ruby2ruby (>= 2.0.8, < 3.0)
70
+ ruby_parser (>= 3.5.0, < 4.0)
71
+ sexp_processor (~> 4.4)
72
+ rerun (0.10.0)
73
+ listen (~> 2.7, >= 2.7.3)
74
+ rubocop (0.27.1)
75
+ astrolabe (~> 1.3)
76
+ parser (>= 2.2.0.pre.7, < 3.0)
82
77
  powerpack (~> 0.0.6)
83
78
  rainbow (>= 1.99.1, < 3.0)
84
79
  ruby-progressbar (~> 1.4)
85
- ruby-progressbar (1.4.2)
86
- ruby2ruby (2.0.7)
80
+ ruby-progressbar (1.7.0)
81
+ ruby2ruby (2.1.3)
87
82
  ruby_parser (~> 3.1)
88
83
  sexp_processor (~> 4.0)
89
- ruby_parser (3.4.1)
84
+ ruby_parser (3.6.3)
90
85
  sexp_processor (~> 4.1)
91
- sequel (4.8.0)
92
- sexp_processor (4.4.2)
93
- sinatra (1.4.4)
86
+ sequel (4.16.0)
87
+ sexp_processor (4.4.4)
88
+ sinatra (1.4.5)
94
89
  rack (~> 1.4)
95
90
  rack-protection (~> 1.4)
96
91
  tilt (~> 1.3, >= 1.3.4)
97
- slim (2.0.2)
98
- temple (~> 0.6.6)
92
+ slim (2.1.0)
93
+ temple (~> 0.6.9)
99
94
  tilt (>= 1.3.3, < 2.1)
100
- slop (3.5.0)
101
- sqlite3 (1.3.9)
102
- temple (0.6.7)
103
- thread_safe (0.1.3)
104
- atomic
95
+ slop (3.6.0)
96
+ sqlite3 (1.3.10)
97
+ temple (0.6.10)
98
+ thread_safe (0.3.4)
105
99
  tilt (1.4.1)
106
- timers (1.1.0)
100
+ timers (4.0.1)
101
+ hitimes
102
+ virtus (1.0.3)
103
+ axiom-types (~> 0.1)
104
+ coercible (~> 1.0)
105
+ descendants_tracker (~> 0.0, >= 0.0.3)
106
+ equalizer (~> 0.0, >= 0.0.9)
107
107
 
108
108
  PLATFORMS
109
109
  ruby
@@ -114,9 +114,9 @@ DEPENDENCIES
114
114
  minitest (~> 5.0)
115
115
  minitest-focus (~> 1.1)
116
116
  nokogiri (~> 1.6)
117
+ pry (~> 0.10.0)
117
118
  rack-test (~> 0.6.2)
118
119
  rake (~> 10.1)
119
120
  reek (~> 1.3)
120
- rerun (~> 0.9.0)
121
- rubocop (~> 0.19.0)
122
- virtus!
121
+ rerun (~> 0.10.0)
122
+ rubocop (~> 0.27.0)
data/README.md CHANGED
@@ -3,4 +3,21 @@ Kamerling
3
3
 
4
4
  Kamerling is a lightweight server for managing a network of computation clients.
5
5
 
6
- © MMXIII Piotr Szotkowski <p.szotkowski@tele.pw.edu.pl>, licensed under AGPL 3 (see LICENCE)
6
+ HTTP API
7
+ --------
8
+
9
+ Minimal docs on the HTTP API (which happens to use XHTML and so should be mostly usable through a browser):
10
+
11
+ `GET /` returns links to clients (at `#clients [href]`) and project (at `#projects [href]`).
12
+
13
+ `GET /clients` contains information on and links to clients (at `#clients [data-class=client]`).
14
+
15
+ `GET /projects` contains links to projects (at `#projects [data-class=project]`).
16
+
17
+ `GET /projects/{uuid}` contains client (at `#clients [data-class=client]` and task (at `#tasks [data-class=task]`) information on a given project.
18
+
19
+ `POST /projects` creates a new project (given a `name` and an `uuid`).
20
+
21
+ `POST /projects/dispatch` dispatches tasks to all free clients.
22
+
23
+ © MMXIII-MMXIV Piotr Szotkowski <p.szotkowski@tele.pw.edu.pl>, licensed under AGPL 3 (see LICENCE)
data/Rakefile CHANGED
@@ -2,9 +2,9 @@ require 'rake/testtask'
2
2
  require 'reek/rake/task'
3
3
  require 'rubocop/rake_task'
4
4
 
5
- task default: %i[spec rubocop reek]
5
+ task default: %i(spec rubocop reek)
6
6
 
7
- Rake::TestTask.new :spec do |task|
7
+ Rake::TestTask.new(:spec) do |task|
8
8
  task.test_files = FileList['spec/**/*_spec.rb']
9
9
  task.warning = true
10
10
  end
@@ -15,4 +15,6 @@ Reek::Rake::Task.new do |task|
15
15
  task.reek_opts = '--quiet'
16
16
  end
17
17
 
18
- Rubocop::RakeTask.new
18
+ RuboCop::RakeTask.new do |task|
19
+ task.options << '--display-cop-names'
20
+ end
data/bin/kamerling CHANGED
@@ -4,6 +4,6 @@ require_relative '../lib/kamerling'
4
4
 
5
5
  Thread.abort_on_exception = true
6
6
 
7
- Kamerling::Logging.log_to Logger.new $stdout
7
+ Kamerling::Logging.log_to Logger.new($stdout)
8
8
 
9
9
  Kamerling::ServerRunner.new(ARGV).start.join
data/config/reek.yml CHANGED
@@ -1,7 +1,17 @@
1
1
  DuplicateMethodCall:
2
2
  exclude:
3
- - Kamerling#self.class_definition_from
4
- - Kamerling::Handler#handle
3
+ - Kamerling::Handler#handle
4
+ - Kamerling::Handler#process
5
+ - Kamerling::Logging#log_dispatcher
6
+ - Kamerling::Logging#log_server
7
+ - Kamerling::Mapper#to_h
8
+ - Kamerling::TaskDispatcher#dispatch_task
9
+
10
+ FeatureEnvy:
11
+ exclude:
12
+ # https://github.com/troessner/reek/issues/53
13
+ - Kamerling::NetDispatcher
14
+ - Kamerling::UUID
5
15
 
6
16
  IrresponsibleModule:
7
17
  enabled: false
@@ -11,4 +21,11 @@ NestedIterators:
11
21
 
12
22
  UnusedParameters:
13
23
  exclude:
14
- - Kamerling::Server::HTTP#initialize
24
+ - Kamerling::Server::HTTP#initialize
25
+
26
+ UtilityFunction:
27
+ exclude:
28
+ # https://github.com/troessner/reek/issues/27
29
+ - Kamerling::Mapper
30
+ - Kamerling::NetDispatcher
31
+ - Kamerling::UUID
data/kamerling.gemspec CHANGED
@@ -1,31 +1,34 @@
1
1
  Gem::Specification.new do |gem|
2
2
  gem.author = 'Piotr Szotkowski'
3
- gem.description = 'A network server for distributing computations across different clients speaking TCP or UDP.'
3
+ gem.description = 'A network server for distributing computations across ' \
4
+ 'different clients speaking TCP or UDP.'
4
5
  gem.email = 'p.szotkowski@tele.pw.edu.pl'
5
6
  gem.homepage = 'https://github.com/chastell/kamerling'
6
7
  gem.license = 'AGPL-3.0'
7
8
  gem.name = 'kamerling'
8
9
  gem.summary = 'Kamerling: a computation network server'
9
- gem.version = '0.0.2'
10
+ gem.version = '0.0.3'
10
11
 
11
- gem.files = `git ls-files -z`.split "\0"
12
- gem.executables = gem.files.grep(%r{^bin/}).map { |path| File.basename path }
13
- gem.test_files = gem.files.grep %r{^spec/.*\.rb$}
12
+ gem.files = `git ls-files -z`.split("\0")
13
+ gem.executables = gem.files.grep(/^bin\//).map { |path| File.basename(path) }
14
+ gem.test_files = gem.files.grep(/^spec\/.*\.rb$/)
14
15
 
15
- gem.add_dependency 'after_do', '~> 0.3.0'
16
- gem.add_dependency 'sequel', '~> 4.4'
17
- gem.add_dependency 'sinatra', '~> 1.4'
18
- gem.add_dependency 'slim', '~> 2.0'
19
- gem.add_dependency 'sqlite3', '~> 1.3'
20
- gem.add_dependency 'virtus', '~> 1.0'
16
+ gem.add_dependency 'after_do', '~> 0.3.0'
17
+ gem.add_dependency 'equalizer', '~> 0.0.9'
18
+ gem.add_dependency 'sequel', '~> 4.4'
19
+ gem.add_dependency 'sinatra', '~> 1.4'
20
+ gem.add_dependency 'slim', '~> 2.0'
21
+ gem.add_dependency 'sqlite3', '~> 1.3'
22
+ gem.add_dependency 'virtus', '~> 1.0'
21
23
 
22
24
  gem.add_development_dependency 'bogus', '~> 0.1.3'
23
25
  gem.add_development_dependency 'minitest', '~> 5.0'
24
26
  gem.add_development_dependency 'minitest-focus', '~> 1.1'
25
27
  gem.add_development_dependency 'nokogiri', '~> 1.6'
28
+ gem.add_development_dependency 'pry', '~> 0.10.0'
26
29
  gem.add_development_dependency 'rack-test', '~> 0.6.2'
27
30
  gem.add_development_dependency 'rake', '~> 10.1'
28
31
  gem.add_development_dependency 'reek', '~> 1.3'
29
- gem.add_development_dependency 'rerun', '~> 0.9.0'
30
- gem.add_development_dependency 'rubocop', '~> 0.19.0'
32
+ gem.add_development_dependency 'rerun', '~> 0.10.0'
33
+ gem.add_development_dependency 'rubocop', '~> 0.27.0'
31
34
  end
data/lib/kamerling.rb CHANGED
@@ -1,29 +1,3 @@
1
- require 'socket'
2
-
3
- require_relative 'kamerling/core_extensions'
4
-
5
- include Kamerling::CoreExtensions::Main
6
-
7
- require_relative 'kamerling/addr'
8
- require_relative 'kamerling/handler'
9
- require_relative 'kamerling/message'
10
- require_relative 'kamerling/net_dispatcher'
11
- require_relative 'kamerling/receiver'
12
- require_relative 'kamerling/registrar'
13
- require_relative 'kamerling/repo'
14
- require_relative 'kamerling/repos'
15
- require_relative 'kamerling/http_api'
16
- require_relative 'kamerling/server/http'
17
- require_relative 'kamerling/server/sock'
18
- require_relative 'kamerling/server/tcp'
19
- require_relative 'kamerling/server/udp'
20
- require_relative 'kamerling/server_runner'
21
- require_relative 'kamerling/task_dispatcher'
22
- require_relative 'kamerling/uuid'
23
- require_relative 'kamerling/uuid_entity'
24
- require_relative 'kamerling/client'
25
- require_relative 'kamerling/project'
26
- require_relative 'kamerling/registration'
27
- require_relative 'kamerling/task'
28
- require_relative 'kamerling/result'
1
+ require_relative 'kamerling/core_extensions/main'
29
2
  require_relative 'kamerling/logging'
3
+ require_relative 'kamerling/server_runner'
@@ -1,5 +1,15 @@
1
+ require 'socket'
2
+ require 'uri'
3
+ require_relative 'value'
4
+
1
5
  module Kamerling
2
- Addr = Struct.new :host, :port, :prot do
6
+ class Addr < Value
7
+ vals host: String, port: Integer, prot: Symbol
8
+
9
+ def self.[](host, port, prot)
10
+ new(host: host, port: port, prot: prot)
11
+ end
12
+
3
13
  def connectable?
4
14
  TCPSocket.open(*self).close
5
15
  true
@@ -12,11 +22,15 @@ module Kamerling
12
22
  end
13
23
 
14
24
  def to_h
15
- super.merge prot: prot.to_s
25
+ attributes.merge(prot: prot.to_s)
16
26
  end
17
27
 
18
28
  def to_s
19
- "#{host}:#{port} (#{prot})"
29
+ uri.to_s
30
+ end
31
+
32
+ def uri
33
+ URI.parse("#{prot.downcase}://#{host}:#{port}")
20
34
  end
21
35
  end
22
36
  end
@@ -1,14 +1,13 @@
1
- module Kamerling class Client < UUIDEntity
2
- attribute :addr, Addr
3
- attribute :busy, Boolean, default: false
1
+ require_relative 'addr'
2
+ require_relative 'uuid_entity'
4
3
 
5
- def self.from_h hash
6
- super.tap do |client|
7
- client.addr = Addr[hash[:host], hash[:port], hash[:prot]]
8
- end
9
- end
4
+ module Kamerling
5
+ class Client < UUIDEntity
6
+ attrs addr: Addr, busy: Boolean, type: Symbol
7
+ defaults busy: false
10
8
 
11
- def to_h
12
- super.reject { |key, _| key == :addr }.merge addr.to_h
9
+ def to_h
10
+ attributes.merge(type: type.to_s)
11
+ end
13
12
  end
14
- end end
13
+ end