cuboid 0.0.0 → 0.0.1alpha

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 (221) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +0 -0
  3. data/Gemfile +20 -5
  4. data/LICENSE.md +22 -0
  5. data/README.md +158 -19
  6. data/Rakefile +56 -3
  7. data/config/paths.yml +15 -0
  8. data/cuboid.gemspec +61 -23
  9. data/lib/cuboid.rb +96 -4
  10. data/lib/cuboid/application.rb +326 -0
  11. data/lib/cuboid/application/parts/data.rb +18 -0
  12. data/lib/cuboid/application/parts/report.rb +29 -0
  13. data/lib/cuboid/application/parts/state.rb +274 -0
  14. data/lib/cuboid/application/runtime.rb +25 -0
  15. data/lib/cuboid/banner.rb +13 -0
  16. data/lib/cuboid/data.rb +86 -0
  17. data/lib/cuboid/data/application.rb +52 -0
  18. data/lib/cuboid/error.rb +9 -0
  19. data/lib/cuboid/option_group.rb +129 -0
  20. data/lib/cuboid/option_groups.rb +8 -0
  21. data/lib/cuboid/option_groups/datastore.rb +23 -0
  22. data/lib/cuboid/option_groups/dispatcher.rb +38 -0
  23. data/lib/cuboid/option_groups/output.rb +14 -0
  24. data/lib/cuboid/option_groups/paths.rb +184 -0
  25. data/lib/cuboid/option_groups/report.rb +39 -0
  26. data/lib/cuboid/option_groups/rpc.rb +105 -0
  27. data/lib/cuboid/option_groups/scheduler.rb +27 -0
  28. data/lib/cuboid/option_groups/snapshot.rb +13 -0
  29. data/lib/cuboid/option_groups/system.rb +10 -0
  30. data/lib/cuboid/options.rb +254 -0
  31. data/lib/cuboid/processes.rb +13 -0
  32. data/lib/cuboid/processes/dispatchers.rb +140 -0
  33. data/lib/cuboid/processes/executables/base.rb +54 -0
  34. data/lib/cuboid/processes/executables/dispatcher.rb +5 -0
  35. data/lib/cuboid/processes/executables/instance.rb +12 -0
  36. data/lib/cuboid/processes/executables/rest_service.rb +13 -0
  37. data/lib/cuboid/processes/executables/scheduler.rb +5 -0
  38. data/lib/cuboid/processes/helpers.rb +4 -0
  39. data/lib/cuboid/processes/helpers/dispatchers.rb +23 -0
  40. data/lib/cuboid/processes/helpers/instances.rb +39 -0
  41. data/lib/cuboid/processes/helpers/processes.rb +23 -0
  42. data/lib/cuboid/processes/helpers/schedulers.rb +23 -0
  43. data/lib/cuboid/processes/instances.rb +203 -0
  44. data/lib/cuboid/processes/manager.rb +262 -0
  45. data/lib/cuboid/processes/schedulers.rb +128 -0
  46. data/lib/cuboid/report.rb +220 -0
  47. data/lib/cuboid/rest/server.rb +165 -0
  48. data/lib/cuboid/rest/server/instance_helpers.rb +99 -0
  49. data/lib/cuboid/rest/server/routes/dispatcher.rb +41 -0
  50. data/lib/cuboid/rest/server/routes/grid.rb +41 -0
  51. data/lib/cuboid/rest/server/routes/instances.rb +131 -0
  52. data/lib/cuboid/rest/server/routes/scheduler.rb +140 -0
  53. data/lib/cuboid/rpc/client.rb +3 -0
  54. data/lib/cuboid/rpc/client/base.rb +58 -0
  55. data/lib/cuboid/rpc/client/dispatcher.rb +58 -0
  56. data/lib/cuboid/rpc/client/instance.rb +100 -0
  57. data/lib/cuboid/rpc/client/instance/service.rb +37 -0
  58. data/lib/cuboid/rpc/client/scheduler.rb +46 -0
  59. data/lib/cuboid/rpc/serializer.rb +92 -0
  60. data/lib/cuboid/rpc/server/active_options.rb +38 -0
  61. data/lib/cuboid/rpc/server/application_wrapper.rb +138 -0
  62. data/lib/cuboid/rpc/server/base.rb +63 -0
  63. data/lib/cuboid/rpc/server/dispatcher.rb +317 -0
  64. data/lib/cuboid/rpc/server/dispatcher/node.rb +247 -0
  65. data/lib/cuboid/rpc/server/dispatcher/service.rb +145 -0
  66. data/lib/cuboid/rpc/server/instance.rb +338 -0
  67. data/lib/cuboid/rpc/server/output.rb +92 -0
  68. data/lib/cuboid/rpc/server/scheduler.rb +482 -0
  69. data/lib/cuboid/ruby.rb +4 -0
  70. data/lib/cuboid/ruby/array.rb +17 -0
  71. data/lib/cuboid/ruby/hash.rb +41 -0
  72. data/lib/cuboid/ruby/object.rb +32 -0
  73. data/lib/cuboid/snapshot.rb +186 -0
  74. data/lib/cuboid/state.rb +94 -0
  75. data/lib/cuboid/state/application.rb +309 -0
  76. data/lib/cuboid/state/options.rb +27 -0
  77. data/lib/cuboid/support.rb +11 -0
  78. data/lib/cuboid/support/buffer.rb +3 -0
  79. data/lib/cuboid/support/buffer/autoflush.rb +61 -0
  80. data/lib/cuboid/support/buffer/base.rb +91 -0
  81. data/lib/cuboid/support/cache.rb +7 -0
  82. data/lib/cuboid/support/cache/base.rb +226 -0
  83. data/lib/cuboid/support/cache/least_cost_replacement.rb +77 -0
  84. data/lib/cuboid/support/cache/least_recently_pushed.rb +21 -0
  85. data/lib/cuboid/support/cache/least_recently_used.rb +31 -0
  86. data/lib/cuboid/support/cache/preference.rb +31 -0
  87. data/lib/cuboid/support/cache/random_replacement.rb +20 -0
  88. data/lib/cuboid/support/crypto.rb +2 -0
  89. data/lib/cuboid/support/crypto/rsa_aes_cbc.rb +86 -0
  90. data/lib/cuboid/support/database.rb +5 -0
  91. data/lib/cuboid/support/database/base.rb +177 -0
  92. data/lib/cuboid/support/database/categorized_queue.rb +195 -0
  93. data/lib/cuboid/support/database/hash.rb +300 -0
  94. data/lib/cuboid/support/database/queue.rb +149 -0
  95. data/lib/cuboid/support/filter.rb +3 -0
  96. data/lib/cuboid/support/filter/base.rb +110 -0
  97. data/lib/cuboid/support/filter/set.rb +29 -0
  98. data/lib/cuboid/support/glob.rb +27 -0
  99. data/lib/cuboid/support/mixins.rb +8 -0
  100. data/lib/cuboid/support/mixins/observable.rb +99 -0
  101. data/lib/cuboid/support/mixins/parts.rb +20 -0
  102. data/lib/cuboid/support/mixins/profiler.rb +93 -0
  103. data/lib/cuboid/support/mixins/spec_instances.rb +65 -0
  104. data/lib/cuboid/support/mixins/terminal.rb +57 -0
  105. data/lib/cuboid/system.rb +119 -0
  106. data/lib/cuboid/system/platforms.rb +84 -0
  107. data/lib/cuboid/system/platforms/linux.rb +26 -0
  108. data/lib/cuboid/system/platforms/mixins/unix.rb +46 -0
  109. data/lib/cuboid/system/platforms/osx.rb +25 -0
  110. data/lib/cuboid/system/platforms/windows.rb +81 -0
  111. data/lib/cuboid/system/slots.rb +143 -0
  112. data/lib/cuboid/ui/output.rb +52 -0
  113. data/lib/cuboid/ui/output_interface.rb +43 -0
  114. data/lib/cuboid/ui/output_interface/abstract.rb +68 -0
  115. data/lib/cuboid/ui/output_interface/controls.rb +84 -0
  116. data/lib/cuboid/ui/output_interface/error_logging.rb +119 -0
  117. data/lib/cuboid/ui/output_interface/implemented.rb +58 -0
  118. data/lib/cuboid/ui/output_interface/personalization.rb +62 -0
  119. data/lib/cuboid/utilities.rb +155 -0
  120. data/lib/cuboid/version.rb +4 -3
  121. data/lib/version +1 -0
  122. data/logs/placeholder +0 -0
  123. data/spec/cuboid/application/parts/data_spec.rb +12 -0
  124. data/spec/cuboid/application/parts/report_spec.rb +6 -0
  125. data/spec/cuboid/application/parts/state_spec.rb +192 -0
  126. data/spec/cuboid/application/runtime_spec.rb +21 -0
  127. data/spec/cuboid/application_spec.rb +37 -0
  128. data/spec/cuboid/data/application_spec.rb +22 -0
  129. data/spec/cuboid/data_spec.rb +47 -0
  130. data/spec/cuboid/error_spec.rb +23 -0
  131. data/spec/cuboid/option_groups/datastore_spec.rb +54 -0
  132. data/spec/cuboid/option_groups/dispatcher_spec.rb +12 -0
  133. data/spec/cuboid/option_groups/output_spec.rb +11 -0
  134. data/spec/cuboid/option_groups/paths_spec.rb +184 -0
  135. data/spec/cuboid/option_groups/report_spec.rb +26 -0
  136. data/spec/cuboid/option_groups/rpc_spec.rb +53 -0
  137. data/spec/cuboid/option_groups/snapshot_spec.rb +26 -0
  138. data/spec/cuboid/option_groups/system.rb +12 -0
  139. data/spec/cuboid/options_spec.rb +218 -0
  140. data/spec/cuboid/report_spec.rb +221 -0
  141. data/spec/cuboid/rest/server_spec.rb +1205 -0
  142. data/spec/cuboid/rpc/client/base_spec.rb +151 -0
  143. data/spec/cuboid/rpc/client/dispatcher_spec.rb +13 -0
  144. data/spec/cuboid/rpc/client/instance_spec.rb +38 -0
  145. data/spec/cuboid/rpc/server/active_options_spec.rb +21 -0
  146. data/spec/cuboid/rpc/server/base_spec.rb +60 -0
  147. data/spec/cuboid/rpc/server/dispatcher/node_spec.rb +222 -0
  148. data/spec/cuboid/rpc/server/dispatcher/service_spec.rb +112 -0
  149. data/spec/cuboid/rpc/server/dispatcher_spec.rb +317 -0
  150. data/spec/cuboid/rpc/server/instance_spec.rb +307 -0
  151. data/spec/cuboid/rpc/server/output_spec.rb +32 -0
  152. data/spec/cuboid/rpc/server/scheduler_spec.rb +400 -0
  153. data/spec/cuboid/ruby/array_spec.rb +77 -0
  154. data/spec/cuboid/ruby/hash_spec.rb +63 -0
  155. data/spec/cuboid/ruby/object_spec.rb +22 -0
  156. data/spec/cuboid/snapshot_spec.rb +123 -0
  157. data/spec/cuboid/state/application_spec.rb +538 -0
  158. data/spec/cuboid/state/options_spec.rb +37 -0
  159. data/spec/cuboid/state_spec.rb +53 -0
  160. data/spec/cuboid/support/buffer/autoflush_spec.rb +78 -0
  161. data/spec/cuboid/support/buffer/base_spec.rb +193 -0
  162. data/spec/cuboid/support/cache/least_cost_replacement_spec.rb +61 -0
  163. data/spec/cuboid/support/cache/least_recently_pushed_spec.rb +90 -0
  164. data/spec/cuboid/support/cache/least_recently_used_spec.rb +80 -0
  165. data/spec/cuboid/support/cache/preference_spec.rb +37 -0
  166. data/spec/cuboid/support/cache/random_replacement_spec.rb +42 -0
  167. data/spec/cuboid/support/crypto/rsa_aes_cbc_spec.rb +28 -0
  168. data/spec/cuboid/support/database/categorized_queue_spec.rb +327 -0
  169. data/spec/cuboid/support/database/hash_spec.rb +204 -0
  170. data/spec/cuboid/support/database/scheduler_spec.rb +325 -0
  171. data/spec/cuboid/support/filter/set_spec.rb +19 -0
  172. data/spec/cuboid/support/glob_spec.rb +75 -0
  173. data/spec/cuboid/support/mixins/observable_spec.rb +95 -0
  174. data/spec/cuboid/system/platforms/linux_spec.rb +31 -0
  175. data/spec/cuboid/system/platforms/osx_spec.rb +32 -0
  176. data/spec/cuboid/system/platforms/windows_spec.rb +41 -0
  177. data/spec/cuboid/system/slots_spec.rb +202 -0
  178. data/spec/cuboid/system_spec.rb +105 -0
  179. data/spec/cuboid/utilities_spec.rb +131 -0
  180. data/spec/spec_helper.rb +46 -0
  181. data/spec/support/factories/placeholder +0 -0
  182. data/spec/support/factories/scan_report.rb +18 -0
  183. data/spec/support/fixtures/empty/placeholder +0 -0
  184. data/spec/support/fixtures/executables/node.rb +50 -0
  185. data/spec/support/fixtures/mock_app.rb +61 -0
  186. data/spec/support/fixtures/mock_app/test_service.rb +64 -0
  187. data/spec/support/fixtures/services/echo.rb +64 -0
  188. data/spec/support/helpers/framework.rb +3 -0
  189. data/spec/support/helpers/matchers.rb +5 -0
  190. data/spec/support/helpers/misc.rb +3 -0
  191. data/spec/support/helpers/paths.rb +15 -0
  192. data/spec/support/helpers/request_helpers.rb +38 -0
  193. data/spec/support/helpers/requires.rb +8 -0
  194. data/spec/support/helpers/resets.rb +52 -0
  195. data/spec/support/helpers/web_server.rb +15 -0
  196. data/spec/support/lib/factory.rb +107 -0
  197. data/spec/support/lib/web_server_client.rb +41 -0
  198. data/spec/support/lib/web_server_dispatcher.rb +25 -0
  199. data/spec/support/lib/web_server_manager.rb +118 -0
  200. data/spec/support/logs/placeholder +0 -0
  201. data/spec/support/pems/cacert.pem +37 -0
  202. data/spec/support/pems/client/cert.pem +37 -0
  203. data/spec/support/pems/client/foo-cert.pem +39 -0
  204. data/spec/support/pems/client/foo-key.pem +51 -0
  205. data/spec/support/pems/client/key.pem +51 -0
  206. data/spec/support/pems/server/cert.pem +37 -0
  207. data/spec/support/pems/server/key.pem +51 -0
  208. data/spec/support/reports/placeholder +0 -0
  209. data/spec/support/shared/application.rb +10 -0
  210. data/spec/support/shared/component.rb +31 -0
  211. data/spec/support/shared/component/options/base.rb +187 -0
  212. data/spec/support/shared/option_group.rb +98 -0
  213. data/spec/support/shared/support/cache.rb +419 -0
  214. data/spec/support/shared/support/filter.rb +143 -0
  215. data/spec/support/shared/system/platforms/base.rb +25 -0
  216. data/spec/support/shared/system/platforms/mixins/unix.rb +37 -0
  217. data/spec/support/snapshots/placeholder +0 -0
  218. metadata +566 -21
  219. data/.gitignore +0 -8
  220. data/bin/console +0 -15
  221. data/bin/setup +0 -8
@@ -0,0 +1,39 @@
1
+ module Cuboid::OptionGroups
2
+
3
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
4
+ class Report < Cuboid::OptionGroup
5
+
6
+ # @return [String]
7
+ # Directory or file path where to store the scan report.
8
+ attr_accessor :path
9
+
10
+ def initialize
11
+ @default_path = self.path = default_path
12
+ end
13
+
14
+ def path=( path )
15
+ return @path = @default_path if !path
16
+
17
+ if path.end_with?( '/' ) && !File.exist?( path )
18
+ raise ArgumentError,
19
+ "Snapshot location does not exist: #{path}"
20
+ end
21
+
22
+ path = File.expand_path( path )
23
+ if File.directory? path
24
+ path += '/' if !path.end_with? '/'
25
+ end
26
+
27
+ @path = path
28
+ end
29
+
30
+ def default_path
31
+ Paths.config['reports']
32
+ end
33
+
34
+ def defaults
35
+ { path: default_path }
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,105 @@
1
+ module Cuboid::OptionGroups
2
+
3
+ # Holds {Engine::RPC::Client} and {Engine::RPC::Server} options.
4
+ #
5
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
6
+ class RPC < Cuboid::OptionGroup
7
+
8
+ # @return [String]
9
+ # Path to the UNIX socket to use for RPC communication.
10
+ #
11
+ # @see RPC::Server::Base
12
+ attr_accessor :server_socket
13
+
14
+ # @return [String]
15
+ # Hostname or IP address for the RPC server.
16
+ #
17
+ # @see RPC::Server::Base
18
+ attr_accessor :server_address
19
+
20
+ # @return [String]
21
+ # External (hostname or IP) address for the RPC server to advertise.
22
+ attr_accessor :server_external_address
23
+
24
+ # @return [Integer]
25
+ # RPC server port.
26
+ #
27
+ # @see RPC::Server::Base
28
+ attr_accessor :server_port
29
+
30
+ # @return [String]
31
+ # Path to an SSL certificate authority file in PEM format.
32
+ #
33
+ # @see RPC::Server::Base
34
+ # @see RPC::Client::Base
35
+ attr_accessor :ssl_ca
36
+
37
+ # @return [String]
38
+ # Path to a server SSL private key in PEM format.
39
+ #
40
+ # @see RPC::Server::Base
41
+ attr_accessor :server_ssl_private_key
42
+
43
+ # @return [String]
44
+ # Path to server SSL certificate in PEM format.
45
+ #
46
+ # @see RPC::Server::Base
47
+ attr_accessor :server_ssl_certificate
48
+
49
+ # @return [String]
50
+ # Path to a client SSL private key in PEM format.
51
+ #
52
+ # @see RPC::Client::Base
53
+ attr_accessor :client_ssl_private_key
54
+
55
+ # @return [String]
56
+ # Path to client SSL certificate in PEM format.
57
+ #
58
+ # @see RPC::Client::Base
59
+ attr_accessor :client_ssl_certificate
60
+
61
+ # @return [Integer]
62
+ # Maximum retries for failed RPC calls.
63
+ #
64
+ # @see RPC::Client::Base
65
+ attr_accessor :client_max_retries
66
+
67
+ # @note This should be permanently set to `1`, otherwise it will cause issues
68
+ # with the scheduling of the workload distribution of multi-Instance scans.
69
+ #
70
+ # @return [Integer]
71
+ # Amount of concurrently open connections for each RPC client.
72
+ #
73
+ # @see RPC::Client::Base
74
+ attr_accessor :connection_pool_size
75
+
76
+ set_defaults(
77
+ connection_pool_size: 1,
78
+ server_address: '127.0.0.1',
79
+ server_port: 7331
80
+ )
81
+
82
+ def to_client_options
83
+ {
84
+ connection_pool_size: connection_pool_size,
85
+ max_retries: client_max_retries,
86
+ ssl_ca: ssl_ca,
87
+ ssl_pkey: client_ssl_private_key,
88
+ ssl_cert: client_ssl_certificate
89
+ }
90
+ end
91
+
92
+ def to_server_options
93
+ {
94
+ host: server_address,
95
+ external_address: server_external_address,
96
+ port: server_port,
97
+ socket: server_socket,
98
+ ssl_ca: ssl_ca,
99
+ ssl_pkey: server_ssl_private_key,
100
+ ssl_cert: server_ssl_certificate
101
+ }
102
+ end
103
+
104
+ end
105
+ end
@@ -0,0 +1,27 @@
1
+ module Cuboid::OptionGroups
2
+
3
+ # Holds options for {RPC::Server::Scheduler} servers.
4
+ #
5
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
6
+ class Scheduler < Cuboid::OptionGroup
7
+
8
+ # @return [String]
9
+ # URL of a {RPC::Server::Scheduler}.
10
+ attr_accessor :url
11
+
12
+ # @return [Array<Integer>]
13
+ # Range of ports to use when spawning instances, first entry should be
14
+ # the lowest port number, last the max port number.
15
+ attr_accessor :instance_port_range
16
+
17
+ # @return [Float]
18
+ # How regularly to check for scan statuses.
19
+ attr_accessor :ping_interval
20
+
21
+ set_defaults(
22
+ ping_interval: 5.0,
23
+ instance_port_range: [1025, 65535]
24
+ )
25
+
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ require_relative 'report'
2
+
3
+ module Cuboid::OptionGroups
4
+
5
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
6
+ class Snapshot < Report
7
+
8
+ def default_path
9
+ Paths.config['snapshots']
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ module Cuboid::OptionGroups
2
+
3
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
4
+ class System < Cuboid::OptionGroup
5
+
6
+ # @return [Integer]
7
+ attr_accessor :max_slots
8
+
9
+ end
10
+ end
@@ -0,0 +1,254 @@
1
+ require 'yaml'
2
+ require 'singleton'
3
+
4
+ require_relative 'error'
5
+ require_relative 'utilities'
6
+
7
+ module Cuboid
8
+
9
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
10
+ # @see OptionGroups
11
+ class Options
12
+ include Singleton
13
+
14
+ def self.attr_accessor(*vars)
15
+ @attr_accessors ||= []
16
+ @attr_accessors |= vars
17
+ super( *vars )
18
+ end
19
+
20
+ def self.attr_accessors
21
+ @attr_accessors
22
+ end
23
+
24
+ def attr_accessors
25
+ self.class.attr_accessors
26
+ end
27
+
28
+ # {Options} error namespace.
29
+ #
30
+ # All {Options} errors inherit from and live under it.
31
+ #
32
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
33
+ class Error < Cuboid::Error
34
+ end
35
+
36
+ class <<self
37
+
38
+ def method_missing( sym, *args, &block )
39
+ if instance.respond_to?( sym )
40
+ instance.send( sym, *args, &block )
41
+ else
42
+ super( sym, *args, &block )
43
+ end
44
+ end
45
+
46
+ def respond_to?( *args )
47
+ super || instance.respond_to?( *args )
48
+ end
49
+
50
+ # Ruby 2.0 doesn't like my class-level method_missing for some reason.
51
+ # @private
52
+ public :allocate
53
+
54
+ # @return [Hash<Symbol,OptionGroup>]
55
+ # {OptionGroups Option group} classes by name.
56
+ def group_classes
57
+ @group_classes ||= {}
58
+ end
59
+
60
+ # Should be called by {OptionGroup.inherited}.
61
+ # @private
62
+ def register_group( group )
63
+ name = Utilities.caller_name
64
+
65
+ # Prepare an attribute reader for this group...
66
+ attr_reader name
67
+
68
+ # ... and initialize it.
69
+ instance_variable_set "@#{name}".to_sym, group.new
70
+
71
+ group_classes[name.to_sym] = group
72
+ end
73
+ end
74
+
75
+ # Load all {OptionGroups}.
76
+ require_relative 'option_groups'
77
+
78
+ TO_RPC_IGNORE = Set.new([
79
+ :instance, :rpc, :dispatcher, :queue, :paths,
80
+ :snapshot, :report, :output, :system
81
+ ])
82
+
83
+ TO_HASH_IGNORE = Set.new([ :instance ])
84
+
85
+
86
+ # @return [String]
87
+ # E-mail address of the person that authorized the run.
88
+ #
89
+ # @see HTTP::Client#headers
90
+ attr_accessor :authorized_by
91
+
92
+ attr_accessor :application
93
+
94
+ def initialize
95
+ reset
96
+ end
97
+
98
+ # Restores everything to their default values.
99
+ #
100
+ # @return [Options] `self`
101
+ def reset
102
+ # nil everything out.
103
+ instance_variables.each { |var| instance_variable_set( var.to_s, nil ) }
104
+
105
+ # Set fresh option groups.
106
+ group_classes.each do |name, klass|
107
+ instance_variable_set "@#{name}".to_sym, klass.new
108
+ end
109
+
110
+ @authorized_by = nil
111
+
112
+ self
113
+ end
114
+
115
+
116
+ # Configures options via a Hash object.
117
+ #
118
+ # @param [Hash] options
119
+ # If the key refers to a class attribute, the attribute will be assigned
120
+ # the given value, if it refers to one of the {OptionGroups} the value
121
+ # should be a hash with data to update that {OptionGroup group} using
122
+ # {OptionGroup#update}.
123
+ #
124
+ # @return [Options]
125
+ #
126
+ # @see OptionGroups
127
+ def update( options )
128
+ options.each do |k, v|
129
+ k = k.to_sym
130
+ if group_classes.include? k
131
+ send( k ).update v
132
+ else
133
+ send( "#{k.to_s}=", v )
134
+ end
135
+ end
136
+
137
+ self
138
+ end
139
+ alias :set :update
140
+
141
+ # @return [Hash]
142
+ # Hash of errors with the name of the invalid options/groups as the keys.
143
+ def validate
144
+ errors = {}
145
+ group_classes.keys.each do |name|
146
+ next if (group_errors = send(name).validate).empty?
147
+ errors[name] = group_errors
148
+ end
149
+ errors
150
+ end
151
+
152
+ # @param [String] file
153
+ # Saves `self` to `file` using YAML.
154
+ def save( file )
155
+ File.open( file, 'w' ) do |f|
156
+ f.write to_save_data
157
+ f.path
158
+ end
159
+ end
160
+
161
+ def to_save_data
162
+ to_rpc_data.to_yaml
163
+ end
164
+
165
+ def to_save_data_without_defaults
166
+ to_rpc_data_without_defaults.to_yaml
167
+ end
168
+
169
+ # Loads a file created by {#save}.
170
+ #
171
+ # @param [String] filepath
172
+ # Path to the file created by {#save}.
173
+ #
174
+ # @return [Cuboid::Options]
175
+ def load( filepath )
176
+ update( YAML.load_file( filepath ) )
177
+ end
178
+
179
+ # @return [Hash]
180
+ # `self` converted to a Hash suitable for RPC transmission.
181
+ def to_rpc_data
182
+ hash = {}
183
+ instance_variables.each do |var|
184
+ val = instance_variable_get( var )
185
+ var = normalize_name( var )
186
+
187
+ next if TO_RPC_IGNORE.include?( var )
188
+
189
+ hash[var.to_s] = (val.is_a? OptionGroup) ? val.to_rpc_data : val
190
+ end
191
+ hash.deep_clone
192
+ end
193
+
194
+ def to_rpc_data_without_defaults
195
+ defaults = self.class.allocate.reset.to_rpc_data
196
+ to_rpc_data.reject { |k, v| defaults[k] == v }
197
+ end
198
+
199
+ # @return [Hash]
200
+ # `self` converted to a Hash.
201
+ def to_hash
202
+ hash = {}
203
+ instance_variables.each do |var|
204
+ val = instance_variable_get( var )
205
+ var = normalize_name( var )
206
+
207
+ next if TO_HASH_IGNORE.include?( var )
208
+
209
+ hash[var] = (val.is_a? OptionGroup) ? val.to_h : val
210
+ end
211
+
212
+ hash.deep_clone
213
+ end
214
+ alias :to_h :to_hash
215
+
216
+ # @param [Hash] hash
217
+ # Hash to convert into {#to_hash} format.
218
+ #
219
+ # @return [Hash]
220
+ # `hash` in {#to_hash} format.
221
+ def rpc_data_to_hash( hash )
222
+ self.class.allocate.reset.update( hash ).to_hash.
223
+ reject { |k| TO_RPC_IGNORE.include? k }
224
+ end
225
+
226
+ # @param [Hash] hash
227
+ # Hash to convert into {#to_rpc_data} format.
228
+ #
229
+ # @return [Hash]
230
+ # `hash` in {#to_rpc_data} format.
231
+ def hash_to_rpc_data( hash )
232
+ self.class.allocate.reset.update( hash ).to_rpc_data
233
+ end
234
+
235
+ def hash_to_save_data( hash )
236
+ self.class.allocate.reset.update( hash ).to_save_data
237
+ end
238
+
239
+ def dup
240
+ self.class.allocate.reset.update( self.to_h )
241
+ end
242
+
243
+ private
244
+
245
+ def group_classes
246
+ self.class.group_classes
247
+ end
248
+
249
+ def normalize_name( name )
250
+ name.to_s.gsub( '@', '' ).to_sym
251
+ end
252
+
253
+ end
254
+ end