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,4 @@
1
+ lib = Cuboid::Options.paths.lib
2
+ require lib + 'ruby/object'
3
+ require lib + 'ruby/hash'
4
+ require lib + 'ruby/array'
@@ -0,0 +1,17 @@
1
+ class Array
2
+
3
+ # @param [#to_s, Array<#to_s>] tags
4
+ #
5
+ # @return [Bool]
6
+ # `true` if `self` contains any of the `tags` when objects of both `self`
7
+ # and `tags` are converted to `String`.
8
+ def includes_tags?( tags )
9
+ return false if !tags
10
+
11
+ tags = [tags].flatten.compact.map( &:to_s )
12
+ return false if tags.empty?
13
+
14
+ (self.flatten.compact.map( &:to_s ) & tags).any?
15
+ end
16
+
17
+ end
@@ -0,0 +1,41 @@
1
+ class Hash
2
+
3
+ if !method_defined?( :to_h )
4
+ alias :to_h :to_hash
5
+ end
6
+
7
+ # Converts the hash keys to strings.
8
+ #
9
+ # @param [Boolean] recursively
10
+ # Go through the Hash recursively?
11
+ #
12
+ # @return [Hash]
13
+ # Hash with +self+'s keys recursively converted to strings.
14
+ def my_stringify_keys( recursively = true )
15
+ stringified = {}
16
+ each do |k, v|
17
+ stringified[k.to_s] = (recursively && v.is_a?( Hash ) ?
18
+ v.my_stringify_keys : v)
19
+ end
20
+ stringified
21
+ end
22
+
23
+ # Converts the hash keys to symbols.
24
+ #
25
+ # @param [Boolean] recursively
26
+ # Go through the Hash recursively?
27
+ #
28
+ # @return [Hash]
29
+ # Hash with +self+'s keys recursively converted to symbols.
30
+ def my_symbolize_keys( recursively = true )
31
+ symbolize = {}
32
+ each do |k, v|
33
+ k = k.respond_to?(:to_sym) ? k.to_sym : k
34
+
35
+ symbolize[k] = (recursively && v.is_a?( Hash ) ?
36
+ v.my_symbolize_keys : v)
37
+ end
38
+ symbolize
39
+ end
40
+
41
+ end
@@ -0,0 +1,32 @@
1
+ # Overloads the {Object} class providing a {#deep_clone} method.
2
+ #
3
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
4
+ class Object
5
+
6
+ # Deep-clones self using a Marshal dump-load.
7
+ #
8
+ # @return [Object]
9
+ # Duplicate of self.
10
+ def deep_clone
11
+ Marshal.load( Marshal.dump( self ) )
12
+ end
13
+
14
+ def rpc_clone
15
+ if self.class.respond_to?( :from_rpc_data )
16
+ self.class.from_rpc_data(
17
+ Cuboid::RPC::Serializer.serializer.load(
18
+ Cuboid::RPC::Serializer.serializer.dump( to_rpc_data )
19
+ )
20
+ )
21
+ else
22
+ Cuboid::RPC::Serializer.serializer.load(
23
+ Cuboid::RPC::Serializer.serializer.dump( self )
24
+ )
25
+ end
26
+ end
27
+
28
+ def to_rpc_data_or_self
29
+ respond_to?( :to_rpc_data ) ? to_rpc_data : self
30
+ end
31
+
32
+ end
@@ -0,0 +1,186 @@
1
+ require 'zip'
2
+ require 'fileutils'
3
+
4
+ require_relative 'data'
5
+ require_relative 'state'
6
+
7
+ module Cuboid
8
+
9
+ # Stores and provides access to the state of the system.
10
+ #
11
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
12
+ class Snapshot
13
+
14
+ # {Snapshot} error namespace.
15
+ #
16
+ # All {Snapshot} errors inherit from and live under it.
17
+ #
18
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
19
+ class Error < Cuboid::Error
20
+
21
+ # Raised when trying to read an invalid snapshot file.
22
+ #
23
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
24
+ class InvalidFile < Error
25
+ end
26
+ end
27
+
28
+ EXTENSION = 'csf'
29
+
30
+ class <<self
31
+
32
+ # @return [Hash]
33
+ # Metadata associated with the {.load loaded} snapshot.
34
+ attr_accessor :metadata
35
+
36
+ # @return [String]
37
+ # Location of the {.load loaded} snapshot.
38
+ attr_accessor :location
39
+
40
+ def reset
41
+ @metadata = nil
42
+ @location = nil
43
+ end
44
+
45
+ # @return [Bool]
46
+ # `true` if this is a restored snapshot, `false` otherwise.
47
+ def restored?
48
+ !!location
49
+ end
50
+
51
+ # @return [Hash]
52
+ # Snapshot summary information.
53
+ def summary
54
+ {
55
+ data: Data.statistics,
56
+ state: State.statistics
57
+ }
58
+ end
59
+
60
+ # @param [String] location
61
+ # Location of the snapshot.
62
+ #
63
+ # @return [String]
64
+ # Location of the snapshot.
65
+ def dump( location )
66
+ FileUtils.rm_rf( location )
67
+
68
+ directory = get_temporary_directory
69
+
70
+ FileUtils.rm_rf( directory )
71
+ FileUtils.mkdir_p( directory )
72
+
73
+ begin
74
+ Data.dump( "#{directory}/data/" )
75
+ State.dump( "#{directory}/state/" )
76
+
77
+ compress directory, location
78
+
79
+ # Append metadata to the end of the file.
80
+ metadata = Marshal.dump( prepare_metadata )
81
+ File.open( location, 'ab' ) do |f|
82
+ f.write [metadata, metadata.size].pack( 'a*N' )
83
+ end
84
+
85
+ location
86
+ ensure
87
+ FileUtils.rm_rf( directory )
88
+ end
89
+ end
90
+
91
+ # @param [String] snapshot
92
+ # Location of the snapshot to load.
93
+ #
94
+ # @return [Snapshot]
95
+ # `self`
96
+ #
97
+ # @raise [Error::InvalidFile]
98
+ # When trying to read an invalid file.
99
+ def load( snapshot )
100
+ directory = get_temporary_directory
101
+
102
+ @location = snapshot
103
+ @metadata = read_metadata( snapshot )
104
+
105
+ extract( snapshot, directory )
106
+
107
+ Data.load( "#{directory}/data/" )
108
+ State.load( "#{directory}/state/" )
109
+
110
+ self
111
+ ensure
112
+
113
+ # Don't delete the directory immediately because there are disk DBs that
114
+ # use those files.
115
+ Kernel.at_exit do
116
+ FileUtils.rm_rf( directory )
117
+ end
118
+ end
119
+
120
+ # @param [String] snapshot
121
+ # Location of the snapshot.
122
+ #
123
+ # @return [Hash]
124
+ # Metadata associated with the given snapshot.
125
+ #
126
+ # @raise [Error::InvalidFile]
127
+ # When trying to read an invalid file.
128
+ def read_metadata( snapshot )
129
+ File.open( snapshot, 'rb' ) do |f|
130
+ f.seek -4, IO::SEEK_END
131
+ metadata_size = f.read( 4 ).unpack( 'N' ).first
132
+
133
+ f.seek -metadata_size-4, IO::SEEK_END
134
+ Marshal.load( f.read( metadata_size ) )
135
+ end
136
+ rescue => e
137
+ ne = Error::InvalidFile.new( "Invalid snapshot: #{snapshot} (#{e})" )
138
+ ne.set_backtrace e.backtrace
139
+ raise ne
140
+ end
141
+
142
+ private
143
+
144
+ def prepare_metadata
145
+ {
146
+ timestamp: Time.now,
147
+ version: Cuboid::VERSION,
148
+ summary: summary
149
+ }
150
+ end
151
+
152
+ def get_temporary_directory
153
+ "#{Options.paths.tmpdir}/Cuboid_Snapshot_#{Utilities.generate_token}/"
154
+ end
155
+
156
+ def extract( archive, directory )
157
+ Zip::File.open( archive ) do |zip_file|
158
+ zip_file.each do |f|
159
+ f_path = File.join( directory, f.name )
160
+ FileUtils.mkdir_p( File.dirname( f_path ) )
161
+ zip_file.extract( f, f_path ) unless File.exist?( f_path )
162
+ end
163
+ end
164
+
165
+ directory
166
+ end
167
+
168
+ def compress( directory, archive )
169
+ # Globs on Windows don't accept \ as a separator since it's an escape character.
170
+ directory = directory.gsub( '\\', '/' ) + '/'
171
+ directory.gsub!( /\/+/, '/' )
172
+
173
+ Zip::File.open( archive, Zip::File::CREATE ) do |zipfile|
174
+ Dir[directory + '**/**'].each do |file|
175
+ zipfile.add( file.sub( directory, '' ), file )
176
+ end
177
+ end
178
+
179
+ archive
180
+ end
181
+
182
+ end
183
+
184
+ reset
185
+ end
186
+ end
@@ -0,0 +1,94 @@
1
+ module Cuboid
2
+
3
+ # Stores and provides access to the state of the system.
4
+ #
5
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
6
+ class State
7
+
8
+ # {State} error namespace.
9
+ #
10
+ # All {State} errors inherit from and live under it.
11
+ #
12
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
13
+ class Error < Cuboid::Error
14
+ end
15
+
16
+ require_relative 'state/options'
17
+ require_relative 'state/application'
18
+
19
+ class <<self
20
+
21
+ # @return [Options]
22
+ attr_accessor :options
23
+
24
+ # @return [Framework]
25
+ attr_accessor :application
26
+
27
+ def reset
28
+ @options = Options.new
29
+ @application = Application.new
30
+ end
31
+
32
+ def statistics
33
+ stats = {}
34
+ each do |attribute|
35
+ stats[attribute] = send(attribute).statistics
36
+ end
37
+ stats
38
+ end
39
+
40
+ # @param [String] directory
41
+ # Location of the dump directory.
42
+ #
43
+ # @return [String]
44
+ # Location of the directory.
45
+ def dump( directory )
46
+ FileUtils.mkdir_p( directory )
47
+
48
+ each do |name, state|
49
+ state.dump( "#{directory}/#{name}/" )
50
+ end
51
+
52
+ directory
53
+ end
54
+
55
+ # @param [String] directory
56
+ # Location of the dump directory.
57
+ #
58
+ # @return [State]
59
+ # `self`
60
+ def load( directory )
61
+ each do |name, state|
62
+ send( "#{name}=", state.class.load( "#{directory}/#{name}/" ) )
63
+ end
64
+
65
+ self
66
+ end
67
+
68
+ # Clears all states.
69
+ def clear
70
+ each { |_, state| state.clear }
71
+ self
72
+ end
73
+
74
+ private
75
+
76
+ def each( &block )
77
+ accessors.each do |attr|
78
+ block.call attr, send( attr )
79
+ end
80
+ end
81
+
82
+ def accessors
83
+ instance_variables.map do |ivar|
84
+ attribute = "#{ivar.to_s.gsub('@','')}"
85
+ next if !methods.include?( :"#{attribute}=" )
86
+ attribute
87
+ end.compact.map(&:to_sym)
88
+ end
89
+
90
+ end
91
+
92
+ reset
93
+ end
94
+ end
@@ -0,0 +1,309 @@
1
+ module Cuboid
2
+ class State
3
+
4
+ # State information for {Cuboid::Framework}.
5
+ #
6
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
7
+ class Application
8
+
9
+ # {Framework} error namespace.
10
+ #
11
+ # All {Framework} errors inherit from and live under it.
12
+ #
13
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
14
+ class Error < State::Error
15
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
16
+ class StateNotSuspendable < Error
17
+ end
18
+
19
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
20
+ class StateNotAbortable < Error
21
+ end
22
+
23
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
24
+ class InvalidStatusMessage < Error
25
+ end
26
+ end
27
+
28
+ # @return [Symbol]
29
+ attr_accessor :status
30
+
31
+ # @return [Bool]
32
+ attr_accessor :running
33
+
34
+ # @return [Array<String>]
35
+ attr_reader :status_messages
36
+
37
+ attr_accessor :runtime
38
+
39
+ def initialize
40
+ @running = false
41
+ @pre_pause_status = nil
42
+
43
+ @pause_signals = Set.new
44
+
45
+ @status_messages = []
46
+ end
47
+
48
+ def statistics
49
+ {
50
+ runtime: !!@runtime
51
+ }
52
+ end
53
+
54
+ # @return [Hash{Symbol=>String}]
55
+ # All possible {#status_messages} by type.
56
+ def available_status_messages
57
+ {
58
+ suspending: 'Will suspend as soon as the current page is audited.',
59
+ saving_snapshot: 'Saving snapshot at: %s',
60
+ snapshot_location: 'Snapshot location: %s',
61
+ aborting: 'Aborting the scan.',
62
+ timed_out: 'Scan timed out.'
63
+ }
64
+ end
65
+
66
+ # Sets a message as {#status_messages}.
67
+ #
68
+ # @param (see #add_status_message)
69
+ # @return (see #add_status_message)
70
+ def set_status_message( *args )
71
+ clear_status_messages
72
+ add_status_message( *args )
73
+ end
74
+
75
+ # Pushes a message to {#status_messages}.
76
+ #
77
+ # @param [String, Symbol] message
78
+ # Status message. If `Symbol`, it will be grabbed from
79
+ # {#available_status_messages}.
80
+ # @param [String, Numeric] sprintf
81
+ # `sprintf` arguments.
82
+ def add_status_message( message, *sprintf )
83
+ if message.is_a? Symbol
84
+ if !available_status_messages.include?( message )
85
+ fail Error::InvalidStatusMessage,
86
+ "Could not find status message for: '#{message}'"
87
+ end
88
+
89
+ message = available_status_messages[message] % sprintf
90
+ end
91
+
92
+ @status_messages << message.to_s
93
+ end
94
+
95
+ # Clears {#status_messages}.
96
+ def clear_status_messages
97
+ @status_messages.clear
98
+ end
99
+
100
+ def running?
101
+ !!@running
102
+ end
103
+
104
+ def timed_out
105
+ @status = :timed_out
106
+ nil
107
+ end
108
+
109
+ def timed_out?
110
+ @status == :timed_out
111
+ end
112
+
113
+ # @param [Bool] block
114
+ # `true` if the method should block until an abortion has completed,
115
+ # `false` otherwise.
116
+ #
117
+ # @return [Bool]
118
+ # `true` if the abort request was successful, `false` if the system is
119
+ # already {#suspended?} or is {#suspending?}.
120
+ #
121
+ # @raise [StateNotAbortable]
122
+ # When not {#running?}.
123
+ def abort
124
+ return false if aborting? || aborted?
125
+
126
+ if !running?
127
+ fail Error::StateNotAbortable, "Cannot abort idle state: #{status}"
128
+ end
129
+
130
+ set_status_message :aborting
131
+ @status = :aborting
132
+ @abort = true
133
+
134
+ true
135
+ end
136
+
137
+ # @return [Bool]
138
+ # `true` if a {#abort} signal is in place , `false` otherwise.
139
+ def abort?
140
+ !!@abort
141
+ end
142
+
143
+ # Signals a completed abort operation.
144
+ def aborted
145
+ @abort = false
146
+ @status = :aborted
147
+ nil
148
+ end
149
+
150
+ # @return [Bool]
151
+ # `true` if the system has been aborted, `false` otherwise.
152
+ def aborted?
153
+ @status == :aborted
154
+ end
155
+
156
+ # @return [Bool]
157
+ # `true` if the system is being aborted, `false` otherwise.
158
+ def aborting?
159
+ @status == :aborting
160
+ end
161
+
162
+ # @return [Bool]
163
+ # `true` if the system has completed successfully, `false` otherwise.
164
+ def done?
165
+ @status == :done
166
+ end
167
+
168
+ # @param [Bool] block
169
+ # `true` if the method should block until a suspend has completed,
170
+ # `false` otherwise.
171
+ #
172
+ # @return [Bool]
173
+ # `true` if the suspend request was successful, `false` if the system is
174
+ # already {#suspended?} or is {#suspending?}.
175
+ #
176
+ # @raise [StateNotSuspendable]
177
+ # When {#paused?} or {#pausing?}.
178
+ def suspend
179
+ return false if suspending? || suspended?
180
+
181
+ if paused? || pausing?
182
+ fail Error::StateNotSuspendable, 'Cannot suspend a paused state.'
183
+ end
184
+
185
+ if !running?
186
+ fail Error::StateNotSuspendable, "Cannot suspend idle state: #{status}"
187
+ end
188
+
189
+ set_status_message :suspending
190
+ @status = :suspending
191
+ @suspend = true
192
+
193
+ true
194
+ end
195
+
196
+ # @return [Bool]
197
+ # `true` if an {#abort} signal is in place , `false` otherwise.
198
+ def suspend?
199
+ !!@suspend
200
+ end
201
+
202
+ # Signals a completed suspension.
203
+ def suspended
204
+ @suspend = false
205
+ @status = :suspended
206
+ nil
207
+ end
208
+
209
+ # @return [Bool]
210
+ # `true` if the system has been suspended, `false` otherwise.
211
+ def suspended?
212
+ @status == :suspended
213
+ end
214
+
215
+ # @return [Bool]
216
+ # `true` if the system is being suspended, `false` otherwise.
217
+ def suspending?
218
+ @status == :suspending
219
+ end
220
+
221
+ # @param [Bool] block
222
+ # `true` if the method should block until the pause has completed,
223
+ # `false` otherwise.
224
+ #
225
+ # @return [TrueClass]
226
+ # Pauses the framework on a best effort basis, might take a while to take
227
+ # effect.
228
+ def pause
229
+ @pre_pause_status ||= @status if !paused? && !pausing?
230
+
231
+ if !paused?
232
+ @status = :pausing
233
+ end
234
+
235
+ @pause_signals << :nil
236
+
237
+ paused if !running?
238
+ true
239
+ end
240
+
241
+ # Signals that the system has been paused..
242
+ def paused
243
+ clear_status_messages
244
+ @status = :paused
245
+ end
246
+
247
+ # @return [Bool]
248
+ # `true` if the framework is paused.
249
+ def paused?
250
+ @status == :paused
251
+ end
252
+
253
+ # @return [Bool]
254
+ # `true` if the system is being paused, `false` otherwise.
255
+ def pausing?
256
+ @status == :pausing
257
+ end
258
+
259
+ # @return [Bool]
260
+ # `true` if the framework should pause, `false` otherwise.
261
+ def pause?
262
+ @pause_signals.any?
263
+ end
264
+
265
+ # Resumes a paused system
266
+ #
267
+ # @return [Bool]
268
+ # `true` if the system is resumed, `false` if there are more {#pause}
269
+ # signals pending.
270
+ def resume
271
+ @status = :resuming
272
+ @pause_signals.clear
273
+
274
+ true
275
+ end
276
+
277
+ def resumed
278
+ @status = @pre_pause_status
279
+ @pre_pause_status = nil
280
+
281
+ true
282
+ end
283
+
284
+ def dump( directory )
285
+ FileUtils.mkdir_p( directory )
286
+
287
+ d = Cuboid::Application.serializer.dump( @runtime )
288
+ IO.binwrite( "#{directory}/runtime", d )
289
+ end
290
+
291
+ def self.load( directory )
292
+ application = new
293
+ application.runtime = Cuboid::Application.serializer.load( IO.binread( "#{directory}/runtime" ) )
294
+ application
295
+ end
296
+
297
+ def clear
298
+ @pause_signals.clear
299
+
300
+ @running = false
301
+ @pre_pause_status = nil
302
+
303
+ @runtime = nil
304
+ end
305
+
306
+ end
307
+
308
+ end
309
+ end