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,27 @@
1
+ module Cuboid
2
+ class State
3
+
4
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
5
+ class Options
6
+
7
+ def statistics
8
+ {}
9
+ end
10
+
11
+ def dump( directory )
12
+ FileUtils.mkdir_p( directory )
13
+ Cuboid::Options.save( "#{directory}/options" )
14
+ end
15
+
16
+ def self.load( directory )
17
+ Cuboid::Options.load( "#{directory}/options" )
18
+ new
19
+ end
20
+
21
+ def clear
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ module Cuboid::Support
2
+ end
3
+
4
+ lib = Cuboid::Options.paths.support
5
+ require lib + 'mixins'
6
+ require lib + 'buffer'
7
+ require lib + 'cache'
8
+ require lib + 'crypto'
9
+ require lib + 'database'
10
+ require lib + 'filter'
11
+ require lib + 'glob'
@@ -0,0 +1,3 @@
1
+ buffers = Cuboid::Options.paths.support + 'buffer/'
2
+ require buffers + 'base'
3
+ require buffers + 'autoflush'
@@ -0,0 +1,61 @@
1
+ module Cuboid
2
+ module Support::Buffer
3
+
4
+ # A buffer implementation which flushes itself when it gets full or a number
5
+ # of push attempts is reached between flushes.
6
+ #
7
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
8
+ class AutoFlush < Base
9
+
10
+ attr_reader :max_pushes
11
+
12
+ # @param [Integer] max_size
13
+ # Maximum buffer size -- a flush will be triggered when that limit is
14
+ # reached.
15
+ # @param [Integer] max_pushes
16
+ # Maximum number of pushes between flushes.
17
+ # @param [#<<, #|, #clear, #size, #empty?] type
18
+ # Internal storage class to use.
19
+ def initialize( max_size = nil, max_pushes = nil, type = Array )
20
+ super( max_size, type )
21
+
22
+ @max_pushes = max_pushes
23
+ @pushes = 0
24
+ end
25
+
26
+ def <<( *args )
27
+ super( *args )
28
+ ensure
29
+ handle_push
30
+ end
31
+
32
+ def batch_push( *args )
33
+ super( *args )
34
+ ensure
35
+ handle_push
36
+ end
37
+
38
+ def flush
39
+ super
40
+ ensure
41
+ @pushes = 0
42
+ end
43
+
44
+ private
45
+
46
+ def handle_push
47
+ @pushes += 1
48
+ flush if flush?
49
+ end
50
+
51
+ def flush?
52
+ !!(full? || push_limit_reached?)
53
+ end
54
+
55
+ def push_limit_reached?
56
+ max_pushes && @pushes >= max_pushes
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,91 @@
1
+ module Cuboid
2
+ module Support::Buffer
3
+
4
+ #
5
+ # Base buffer class to be extended by more specialised implementation.
6
+ #
7
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
8
+ #
9
+ class Base
10
+ include Support::Mixins::Observable
11
+
12
+ # @!method on_push( &block )
13
+ # @param [Block] block block to call on {#push}
14
+ advertise :on_push
15
+
16
+ # @!method on_batch_push( &block )
17
+ # @param [Block] block block to call on {#batch_push}
18
+ advertise :on_batch_push
19
+
20
+ # @!method on_flush( &block )
21
+ # @param [Block] block block to call on {#flush}
22
+ advertise :on_flush
23
+
24
+ # @return [Integer] Maximum buffer size.
25
+ attr_reader :max_size
26
+
27
+ # @param [Integer] max_size
28
+ # Maximum buffer size -- won't be enforced.
29
+ # @param [#<<, #|, #clear, #size, #empty?] type
30
+ # Internal storage class to use.
31
+ def initialize( max_size = nil, type = Array )
32
+ super()
33
+ @buffer = type.new
34
+ @max_size = max_size
35
+ end
36
+
37
+ # @note Calls {#on_push} blocks with the given object and pushes an object
38
+ # to the buffer.
39
+ #
40
+ # @param [Object] obj
41
+ # Object to push.
42
+ def <<( obj )
43
+ notify_on_push obj
44
+ @buffer << obj
45
+ self
46
+ end
47
+ alias :push :<<
48
+
49
+ # @note Calls {#on_batch_push} blocks with the given list and merges the
50
+ # buffer with the contents of a list.
51
+ #
52
+ # @param [#|] list
53
+ # List of objects
54
+ def batch_push( list )
55
+ notify_on_batch_push list
56
+ @buffer |= list
57
+ self
58
+ end
59
+
60
+ # @return [Integer]
61
+ # Number of object in the buffer.
62
+ def size
63
+ @buffer.size
64
+ end
65
+
66
+ # @return [Bool]
67
+ # `true` if the buffer is empty, `false` otherwise.
68
+ def empty?
69
+ @buffer.empty?
70
+ end
71
+
72
+ # @return [Bool]
73
+ # `true` if the buffer is full, `false` otherwise.
74
+ def full?
75
+ !!(max_size && size >= max_size)
76
+ end
77
+
78
+ # @note Calls {#on_flush} blocks with the buffer and then empties it.
79
+ #
80
+ # @return current buffer
81
+ def flush
82
+ buffer = @buffer.dup
83
+ notify_on_flush buffer
84
+ buffer
85
+ ensure
86
+ @buffer.clear
87
+ end
88
+
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,7 @@
1
+ lib = Cuboid::Options.paths.support + 'cache/'
2
+ require lib + 'base'
3
+ require lib + 'least_recently_pushed'
4
+ require lib + 'least_recently_used'
5
+ require lib + 'random_replacement'
6
+ require lib + 'least_cost_replacement'
7
+ require lib + 'preference'
@@ -0,0 +1,226 @@
1
+ module Cuboid
2
+ module Support::Cache
3
+
4
+ # Base cache implementation -- stores, retrieves and removes entries.
5
+ #
6
+ # The cache will be pruned (call {#prune}) upon storage operations, removing
7
+ # old entries to make room for new ones.
8
+ #
9
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
10
+ # @abstract
11
+ class Base
12
+ include Support::Mixins::Profiler
13
+
14
+ # @return [Integer]
15
+ # Maximum cache size.
16
+ attr_reader :max_size
17
+
18
+ # @param [Hash] options
19
+ # @option options [Integer, nil] :size
20
+ # Maximum size of the cache (must be > 0, `nil` means unlimited).
21
+ # Once the size of the cache is about to exceed `max_size`, the pruning
22
+ # phase will be initiated.
23
+ # # @option options [true, false] :freeze (true)
24
+ # Whether or not to freeze stored items.
25
+ def initialize( options = {} )
26
+ super()
27
+
28
+ @options = options
29
+ @freeze = @options[:freeze].nil? ? true : @options[:freeze]
30
+ self.max_size = @options[:size]
31
+
32
+ @cache = {}
33
+ @hits = 0
34
+ @misses = 0
35
+ @prunings = 0
36
+ end
37
+
38
+ def statistics
39
+ lookups = @hits + @misses
40
+
41
+ {
42
+ lookups: lookups,
43
+ hits: @hits,
44
+ hit_ratio: @hits == 0 ? 0.0 : @hits / Float( lookups ),
45
+ misses: @misses,
46
+ miss_ratio: @misses == 0 ? 0.0 : @misses / Float( lookups ),
47
+ prunings: @prunings,
48
+ size: size,
49
+ max_size: max_size
50
+ }
51
+ end
52
+
53
+ def max_size=( max )
54
+ @max_size = if !max
55
+ nil
56
+ else
57
+ fail( 'Maximum size must be greater than 0.' ) if max <= 0
58
+ max
59
+ end
60
+ end
61
+
62
+ # @return [Bool]
63
+ # `true` is there is no size limit, `false` otherwise
64
+ def uncapped?
65
+ !capped?
66
+ end
67
+
68
+ # @return [Bool]
69
+ # `true` is there is a size limit, `false`` otherwise
70
+ def capped?
71
+ !!max_size
72
+ end
73
+
74
+ # Uncaps the cache {#max_size} limit
75
+ def uncap
76
+ @max_size = nil
77
+ end
78
+
79
+ # @return [Integer]
80
+ # Number of entries in the cache.
81
+ def size
82
+ @cache.size
83
+ end
84
+
85
+ # Storage method.
86
+ #
87
+ # @param [Object] k
88
+ # Entry key.
89
+ # @param [Object] v
90
+ # Object to store.
91
+ #
92
+ # @return [Object] `v`
93
+ def store( k, v )
94
+ store_with_internal_key( make_key( k ), v )
95
+ end
96
+
97
+ # @see #store
98
+ def []=( k, v )
99
+ store( k, v )
100
+ end
101
+
102
+ # Retrieving method.
103
+ #
104
+ # @param [Object] k
105
+ # Entry key.
106
+ #
107
+ # @return [Object, nil]
108
+ # Value for key `k`, `nil` if there is no key `k`.
109
+ def []( k )
110
+ get_with_internal_key( make_key( k ) )
111
+ end
112
+
113
+ # @note If key `k` exists, its corresponding value will be returned.
114
+ # If not, the return value of `block` will be assigned to key `k` and that
115
+ # value will be returned.
116
+ #
117
+ # @param [Object] k
118
+ # Entry key.
119
+ #
120
+ # @return [Object]
121
+ # Value for key `k` or `block.call` if key `k` does not exist.
122
+ def fetch( k, &block )
123
+ k = make_key( k )
124
+
125
+ if @cache.include?( k )
126
+ get_with_internal_key( k )
127
+ else
128
+ @misses += 1
129
+ store_with_internal_key( k, profile_proc( &block ) )
130
+ end
131
+ end
132
+
133
+ # @return [Bool]
134
+ # `true` if cache includes an entry for key `k`, false otherwise.
135
+ def include?( k )
136
+ @cache.include?( make_key( k ) )
137
+ end
138
+
139
+ # @return [Bool]
140
+ # `true` if cache is empty, false otherwise.
141
+ def empty?
142
+ @cache.empty?
143
+ end
144
+
145
+ # @return [Bool]
146
+ # `true` if cache is not empty, `false` otherwise.
147
+ def any?
148
+ !empty?
149
+ end
150
+
151
+ # Removes entry with key `k` from the cache.
152
+ #
153
+ # @param [Object] k
154
+ # Key.
155
+ #
156
+ # @return [Object, nil]
157
+ # Value for key `k`, `nil` if there is no key `k`.
158
+ def delete( k )
159
+ @cache.delete( make_key( k ) )
160
+ end
161
+
162
+ # Clears/empties the cache.
163
+ def clear
164
+ @cache.clear
165
+ end
166
+
167
+ def ==( other )
168
+ hash == other.hash
169
+ end
170
+
171
+ def hash
172
+ @cache.hash
173
+ end
174
+
175
+ def dup
176
+ self.class.new( @options.dup ).tap { |h| h.cache = @cache.dup }
177
+ end
178
+
179
+ protected
180
+
181
+ def cache=( c )
182
+ @cache = c
183
+ end
184
+
185
+ private
186
+
187
+ def store_with_internal_key( k, v )
188
+ while capped? && (size > max_size - 1)
189
+ prune
190
+ @prunings += 1
191
+ end
192
+
193
+ _store( k, v )
194
+ end
195
+
196
+ def _store( k, v )
197
+ @cache[k] = @freeze ? v.freeze : v
198
+ end
199
+
200
+ def get_with_internal_key( k )
201
+ if (r = @cache[k])
202
+ @hits += 1
203
+ else
204
+ @misses += 1
205
+ end
206
+ r
207
+ end
208
+
209
+ def make_key( k )
210
+ k.hash
211
+ end
212
+
213
+ def cache
214
+ @cache
215
+ end
216
+
217
+ # Called to make room when the cache is about to reach its maximum size.
218
+ #
219
+ # @abstract
220
+ def prune
221
+ fail NotImplementedError
222
+ end
223
+
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,77 @@
1
+ module Cuboid
2
+ module Support::Cache
3
+
4
+ # Least Cost Replacement cache implementation.
5
+ #
6
+ # Maintains 3 cost classes (low, medium, high) ) and discards entries from the
7
+ # lowest cost classes in order to make room for new ones.
8
+ #
9
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
10
+ class LeastCostReplacement < Base
11
+
12
+ VALID_COSTS = [ :low, :medium, :high ]
13
+
14
+ # @see Cuboid::Cache::Base#initialize
15
+ def initialize( * )
16
+ super
17
+ reset_costs
18
+ end
19
+
20
+ # Storage method
21
+ #
22
+ # @param [Object] k
23
+ # Entry key.
24
+ # @param [Object] v
25
+ # Object to store.
26
+ # @param [Symbol] cost
27
+ #
28
+ # @return [Object] `v`
29
+ #
30
+ # @see VALID_COSTS
31
+ def store( k, v, cost = :low )
32
+ fail( "invalid cost: #{cost}" ) if !valid_cost?( cost )
33
+
34
+ super( k, v )
35
+ ensure
36
+ @costs[cost] << k
37
+ end
38
+
39
+ # @see Cuboid::Cache::Base#clear
40
+ def clear
41
+ super
42
+ ensure
43
+ reset_costs
44
+ end
45
+
46
+ private
47
+
48
+ def reset_costs
49
+ @costs = {}
50
+ VALID_COSTS.each { |c| @costs[c] = [] }
51
+ end
52
+
53
+ def valid_cost?( cost )
54
+ VALID_COSTS.include?( cost )
55
+ end
56
+
57
+ def candidate_from_cost_class( cost_class )
58
+ return if (costs = @costs[cost_class]).empty?
59
+ costs.delete_at( rand( costs.size ) )
60
+ end
61
+
62
+ def prune_candidate
63
+ VALID_COSTS.each do |cost|
64
+ if c = candidate_from_cost_class( cost )
65
+ return c
66
+ end
67
+ end
68
+ end
69
+
70
+ def prune
71
+ delete( prune_candidate )
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+ end