parsanol 3.0.0

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.

Potentially problematic release.


This version of parsanol might be problematic. Click here for more details.

Files changed (336) hide show
  1. checksums.yaml +7 -0
  2. data/HISTORY.txt +25 -0
  3. data/LICENSE +23 -0
  4. data/README.adoc +643 -0
  5. data/Rakefile +189 -0
  6. data/example/balanced-parens/basic.rb +42 -0
  7. data/example/balanced-parens/basic.rb.md +86 -0
  8. data/example/balanced-parens/parens.rb +42 -0
  9. data/example/balanced-parens/ruby_transform.rb +162 -0
  10. data/example/big.erb +73 -0
  11. data/example/boolean-algebra/basic.rb +70 -0
  12. data/example/boolean-algebra/basic.rb.md +108 -0
  13. data/example/boolean-algebra/ruby_transform.rb +263 -0
  14. data/example/calculator/basic.rb +153 -0
  15. data/example/calculator/basic.rb.md +120 -0
  16. data/example/calculator/pattern.rb +153 -0
  17. data/example/calculator/ruby_transform.rb +156 -0
  18. data/example/calculator/ruby_transform.rb.md +32 -0
  19. data/example/calculator/serialized.rb +257 -0
  20. data/example/calculator/serialized.rb.md +32 -0
  21. data/example/calculator/transform.rb +153 -0
  22. data/example/calculator/zero_copy.rb +269 -0
  23. data/example/calculator/zero_copy.rb.md +36 -0
  24. data/example/capture/basic.rb +49 -0
  25. data/example/capture/basic.rb.md +106 -0
  26. data/example/capture/example.json +39 -0
  27. data/example/comments/basic.rb +35 -0
  28. data/example/comments/basic.rb.md +110 -0
  29. data/example/csv/ruby_transform.rb +148 -0
  30. data/example/csv/ruby_transform.rb.md +131 -0
  31. data/example/csv/serialized.rb +201 -0
  32. data/example/csv/serialized.rb.md +31 -0
  33. data/example/csv/zero_copy.rb +276 -0
  34. data/example/csv/zero_copy.rb.md +36 -0
  35. data/example/custom_atoms/indent_atom.rb +79 -0
  36. data/example/deepest-errors/basic.rb +131 -0
  37. data/example/deepest-errors/basic.rb.md +152 -0
  38. data/example/documentation/basic.rb +18 -0
  39. data/example/documentation/basic.rb.md +97 -0
  40. data/example/email/basic.rb +55 -0
  41. data/example/email/basic.rb.md +102 -0
  42. data/example/email/ruby_transform.rb +106 -0
  43. data/example/empty/basic.rb +13 -0
  44. data/example/empty/basic.rb.md +73 -0
  45. data/example/empty/example.json +38 -0
  46. data/example/erb/basic.rb +47 -0
  47. data/example/erb/basic.rb.md +103 -0
  48. data/example/erb/optimized.rb +42 -0
  49. data/example/error-reporting/basic.rb +132 -0
  50. data/example/error-reporting/basic.rb.md +122 -0
  51. data/example/expression-evaluator/basic.rb +284 -0
  52. data/example/expression-evaluator/basic.rb.md +138 -0
  53. data/example/ini/basic.rb +154 -0
  54. data/example/ini/basic.rb.md +129 -0
  55. data/example/ini/ruby_transform.rb +154 -0
  56. data/example/ip-address/basic.rb +125 -0
  57. data/example/ip-address/basic.rb.md +139 -0
  58. data/example/iso-6709/basic.rb +231 -0
  59. data/example/iso-6709/basic.rb.md +143 -0
  60. data/example/iso-8601/basic.rb +275 -0
  61. data/example/iso-8601/basic.rb.md +149 -0
  62. data/example/json/basic.rb +128 -0
  63. data/example/json/basic.rb.md +121 -0
  64. data/example/json/pattern.rb +128 -0
  65. data/example/json/ruby_transform.rb +200 -0
  66. data/example/json/ruby_transform.rb.md +32 -0
  67. data/example/json/serialized.rb +233 -0
  68. data/example/json/serialized.rb.md +31 -0
  69. data/example/json/transform.rb +128 -0
  70. data/example/json/zero_copy.rb +316 -0
  71. data/example/json/zero_copy.rb.md +36 -0
  72. data/example/local/basic.rb +34 -0
  73. data/example/local/basic.rb.md +91 -0
  74. data/example/local/example.json +38 -0
  75. data/example/markdown/basic.rb +287 -0
  76. data/example/markdown/basic.rb.md +160 -0
  77. data/example/markup/basic.rb +173 -0
  78. data/example/markup/basic.rb.md +118 -0
  79. data/example/mathn/basic.rb +47 -0
  80. data/example/mathn/basic.rb.md +96 -0
  81. data/example/mathn/example.json +39 -0
  82. data/example/minilisp/basic.rb +94 -0
  83. data/example/minilisp/basic.rb.md +133 -0
  84. data/example/modularity/basic.rb +47 -0
  85. data/example/modularity/basic.rb.md +152 -0
  86. data/example/nested-errors/basic.rb +132 -0
  87. data/example/nested-errors/basic.rb.md +157 -0
  88. data/example/output/boolean_algebra.out +4 -0
  89. data/example/output/calc.out +1 -0
  90. data/example/output/capture.out +3 -0
  91. data/example/output/comments.out +8 -0
  92. data/example/output/deepest_errors.out +54 -0
  93. data/example/output/documentation.err +4 -0
  94. data/example/output/documentation.out +1 -0
  95. data/example/output/email_parser.out +2 -0
  96. data/example/output/empty.err +1 -0
  97. data/example/output/erb.out +7 -0
  98. data/example/output/ignore.out +1 -0
  99. data/example/output/ignore_whitespace.out +1 -0
  100. data/example/output/ip_address.out +9 -0
  101. data/example/output/json.out +5 -0
  102. data/example/output/local.out +3 -0
  103. data/example/output/mathn.out +4 -0
  104. data/example/output/minilisp.out +5 -0
  105. data/example/output/modularity.out +0 -0
  106. data/example/output/nested_errors.out +54 -0
  107. data/example/output/optimized_erb.out +1 -0
  108. data/example/output/parens.out +8 -0
  109. data/example/output/prec_calc.out +5 -0
  110. data/example/output/readme.out +1 -0
  111. data/example/output/scopes.out +1 -0
  112. data/example/output/seasons.out +28 -0
  113. data/example/output/sentence.out +1 -0
  114. data/example/output/simple_xml.out +2 -0
  115. data/example/output/string_parser.out +3 -0
  116. data/example/prec-calc/basic.rb +71 -0
  117. data/example/prec-calc/basic.rb.md +114 -0
  118. data/example/readme/basic.rb +30 -0
  119. data/example/readme/basic.rb.md +80 -0
  120. data/example/scopes/basic.rb +15 -0
  121. data/example/scopes/basic.rb.md +73 -0
  122. data/example/scopes/example.json +38 -0
  123. data/example/seasons/basic.rb +46 -0
  124. data/example/seasons/basic.rb.md +117 -0
  125. data/example/seasons/example.json +40 -0
  126. data/example/sentence/basic.rb +36 -0
  127. data/example/sentence/basic.rb.md +81 -0
  128. data/example/sexp/ruby_transform.rb +180 -0
  129. data/example/sexp/ruby_transform.rb.md +143 -0
  130. data/example/simple-xml/basic.rb +54 -0
  131. data/example/simple-xml/basic.rb.md +125 -0
  132. data/example/simple.lit +3 -0
  133. data/example/string-literal/basic.rb +77 -0
  134. data/example/string-literal/basic.rb.md +128 -0
  135. data/example/test.lit +4 -0
  136. data/example/toml/basic.rb +226 -0
  137. data/example/toml/basic.rb.md +173 -0
  138. data/example/url/basic.rb +219 -0
  139. data/example/url/basic.rb.md +142 -0
  140. data/example/url/ruby_transform.rb +219 -0
  141. data/example/yaml/basic.rb +216 -0
  142. data/example/yaml/basic.rb.md +148 -0
  143. data/ext/parsanol_native/extconf.rb +4 -0
  144. data/lib/parsanol/accelerator/application.rb +62 -0
  145. data/lib/parsanol/accelerator/engine.rb +112 -0
  146. data/lib/parsanol/accelerator.rb +162 -0
  147. data/lib/parsanol/ast_visitor.rb +122 -0
  148. data/lib/parsanol/atoms/alternative.rb +97 -0
  149. data/lib/parsanol/atoms/base.rb +214 -0
  150. data/lib/parsanol/atoms/can_flatten.rb +192 -0
  151. data/lib/parsanol/atoms/capture.rb +41 -0
  152. data/lib/parsanol/atoms/context.rb +351 -0
  153. data/lib/parsanol/atoms/context_optimized.rb +42 -0
  154. data/lib/parsanol/atoms/custom.rb +110 -0
  155. data/lib/parsanol/atoms/cut.rb +62 -0
  156. data/lib/parsanol/atoms/dsl.rb +130 -0
  157. data/lib/parsanol/atoms/dynamic.rb +33 -0
  158. data/lib/parsanol/atoms/entity.rb +55 -0
  159. data/lib/parsanol/atoms/ignored.rb +28 -0
  160. data/lib/parsanol/atoms/infix.rb +121 -0
  161. data/lib/parsanol/atoms/lookahead.rb +64 -0
  162. data/lib/parsanol/atoms/named.rb +50 -0
  163. data/lib/parsanol/atoms/re.rb +61 -0
  164. data/lib/parsanol/atoms/repetition.rb +241 -0
  165. data/lib/parsanol/atoms/scope.rb +28 -0
  166. data/lib/parsanol/atoms/sequence.rb +157 -0
  167. data/lib/parsanol/atoms/str.rb +90 -0
  168. data/lib/parsanol/atoms/visitor.rb +91 -0
  169. data/lib/parsanol/atoms.rb +36 -0
  170. data/lib/parsanol/buffer.rb +130 -0
  171. data/lib/parsanol/builder_callbacks.rb +353 -0
  172. data/lib/parsanol/cause.rb +101 -0
  173. data/lib/parsanol/context.rb +23 -0
  174. data/lib/parsanol/convenience.rb +35 -0
  175. data/lib/parsanol/edit_tracker.rb +107 -0
  176. data/lib/parsanol/error_reporter/contextual.rb +122 -0
  177. data/lib/parsanol/error_reporter/deepest.rb +106 -0
  178. data/lib/parsanol/error_reporter/tree.rb +68 -0
  179. data/lib/parsanol/error_reporter.rb +98 -0
  180. data/lib/parsanol/export.rb +163 -0
  181. data/lib/parsanol/expression/treetop.rb +94 -0
  182. data/lib/parsanol/expression.rb +51 -0
  183. data/lib/parsanol/fast_mode.rb +145 -0
  184. data/lib/parsanol/first_set.rb +75 -0
  185. data/lib/parsanol/grammar_builder.rb +177 -0
  186. data/lib/parsanol/graphviz.rb +97 -0
  187. data/lib/parsanol/incremental_parser.rb +179 -0
  188. data/lib/parsanol/interval_tree.rb +215 -0
  189. data/lib/parsanol/lazy_result.rb +178 -0
  190. data/lib/parsanol/lexer.rb +146 -0
  191. data/lib/parsanol/native/parser.rb +630 -0
  192. data/lib/parsanol/native/serializer.rb +245 -0
  193. data/lib/parsanol/native/transformer.rb +438 -0
  194. data/lib/parsanol/native/types.rb +41 -0
  195. data/lib/parsanol/native.rb +217 -0
  196. data/lib/parsanol/optimizer.rb +86 -0
  197. data/lib/parsanol/optimizers/choice_optimizer.rb +78 -0
  198. data/lib/parsanol/optimizers/cut_inserter.rb +175 -0
  199. data/lib/parsanol/optimizers/lookahead_optimizer.rb +58 -0
  200. data/lib/parsanol/optimizers/quantifier_optimizer.rb +62 -0
  201. data/lib/parsanol/optimizers/sequence_optimizer.rb +97 -0
  202. data/lib/parsanol/options/ruby_transform.rb +109 -0
  203. data/lib/parsanol/options/serialized.rb +94 -0
  204. data/lib/parsanol/options/zero_copy.rb +130 -0
  205. data/lib/parsanol/options.rb +20 -0
  206. data/lib/parsanol/parallel.rb +133 -0
  207. data/lib/parsanol/parsanol_native.bundle +0 -0
  208. data/lib/parsanol/parser.rb +151 -0
  209. data/lib/parsanol/parslet.rb +148 -0
  210. data/lib/parsanol/parslet_native.bundle +0 -0
  211. data/lib/parsanol/pattern/binding.rb +49 -0
  212. data/lib/parsanol/pattern.rb +115 -0
  213. data/lib/parsanol/pool.rb +220 -0
  214. data/lib/parsanol/pools/array_pool.rb +75 -0
  215. data/lib/parsanol/pools/buffer_pool.rb +173 -0
  216. data/lib/parsanol/pools/position_pool.rb +92 -0
  217. data/lib/parsanol/pools/slice_pool.rb +64 -0
  218. data/lib/parsanol/position.rb +89 -0
  219. data/lib/parsanol/result.rb +44 -0
  220. data/lib/parsanol/result_builder.rb +208 -0
  221. data/lib/parsanol/result_stream.rb +262 -0
  222. data/lib/parsanol/rig/rspec.rb +52 -0
  223. data/lib/parsanol/rope.rb +78 -0
  224. data/lib/parsanol/scope.rb +42 -0
  225. data/lib/parsanol/slice.rb +172 -0
  226. data/lib/parsanol/source/line_cache.rb +99 -0
  227. data/lib/parsanol/source.rb +171 -0
  228. data/lib/parsanol/source_location.rb +164 -0
  229. data/lib/parsanol/streaming_parser.rb +124 -0
  230. data/lib/parsanol/string_view.rb +192 -0
  231. data/lib/parsanol/transform.rb +267 -0
  232. data/lib/parsanol/version.rb +5 -0
  233. data/lib/parsanol/wasm/README.md +80 -0
  234. data/lib/parsanol/wasm/package.json +51 -0
  235. data/lib/parsanol/wasm/parsanol.js +252 -0
  236. data/lib/parsanol/wasm/parslet.d.ts +129 -0
  237. data/lib/parsanol/wasm_parser.rb +239 -0
  238. data/lib/parsanol.rb +408 -0
  239. data/parsanol-ruby.gemspec +56 -0
  240. data/spec/acceptance/examples_spec.rb +96 -0
  241. data/spec/acceptance/infix_parser_spec.rb +145 -0
  242. data/spec/acceptance/mixing_parsers_spec.rb +74 -0
  243. data/spec/acceptance/regression_spec.rb +329 -0
  244. data/spec/acceptance/repetition_and_maybe_spec.rb +44 -0
  245. data/spec/acceptance/unconsumed_input_spec.rb +21 -0
  246. data/spec/benchmark/comparative/runner_spec.rb +105 -0
  247. data/spec/integration/array_pooling_spec.rb +193 -0
  248. data/spec/integration/buffer_allocation_spec.rb +324 -0
  249. data/spec/integration/position_pooling_spec.rb +184 -0
  250. data/spec/integration/result_builder_spec.rb +282 -0
  251. data/spec/integration/rope_stringview_integration_spec.rb +188 -0
  252. data/spec/integration/slice_pooling_spec.rb +63 -0
  253. data/spec/integration/string_view_integration_spec.rb +125 -0
  254. data/spec/lexer_spec.rb +231 -0
  255. data/spec/parsanol/atom_results_spec.rb +39 -0
  256. data/spec/parsanol/atoms/alternative_spec.rb +26 -0
  257. data/spec/parsanol/atoms/base_spec.rb +127 -0
  258. data/spec/parsanol/atoms/capture_spec.rb +21 -0
  259. data/spec/parsanol/atoms/combinations_spec.rb +5 -0
  260. data/spec/parsanol/atoms/custom_spec.rb +79 -0
  261. data/spec/parsanol/atoms/dsl_spec.rb +7 -0
  262. data/spec/parsanol/atoms/entity_spec.rb +77 -0
  263. data/spec/parsanol/atoms/ignored_spec.rb +15 -0
  264. data/spec/parsanol/atoms/infix_spec.rb +5 -0
  265. data/spec/parsanol/atoms/lookahead_spec.rb +22 -0
  266. data/spec/parsanol/atoms/named_spec.rb +4 -0
  267. data/spec/parsanol/atoms/re_spec.rb +14 -0
  268. data/spec/parsanol/atoms/repetition_spec.rb +24 -0
  269. data/spec/parsanol/atoms/scope_spec.rb +26 -0
  270. data/spec/parsanol/atoms/sequence_spec.rb +28 -0
  271. data/spec/parsanol/atoms/str_spec.rb +15 -0
  272. data/spec/parsanol/atoms/visitor_spec.rb +101 -0
  273. data/spec/parsanol/atoms_spec.rb +488 -0
  274. data/spec/parsanol/auto_optimize_spec.rb +334 -0
  275. data/spec/parsanol/buffer_spec.rb +219 -0
  276. data/spec/parsanol/builder_callbacks_spec.rb +377 -0
  277. data/spec/parsanol/choice_optimizer_spec.rb +231 -0
  278. data/spec/parsanol/convenience_spec.rb +54 -0
  279. data/spec/parsanol/cut_inserter_spec.rb +248 -0
  280. data/spec/parsanol/cut_spec.rb +66 -0
  281. data/spec/parsanol/edit_tracker_spec.rb +218 -0
  282. data/spec/parsanol/error_reporter/contextual_spec.rb +122 -0
  283. data/spec/parsanol/error_reporter/deepest_spec.rb +82 -0
  284. data/spec/parsanol/error_reporter/tree_spec.rb +7 -0
  285. data/spec/parsanol/export_spec.rb +67 -0
  286. data/spec/parsanol/expression/treetop_spec.rb +75 -0
  287. data/spec/parsanol/first_set_spec.rb +298 -0
  288. data/spec/parsanol/interval_tree_spec.rb +205 -0
  289. data/spec/parsanol/lazy_result_spec.rb +288 -0
  290. data/spec/parsanol/lookahead_optimizer_spec.rb +252 -0
  291. data/spec/parsanol/minilisp.citrus +29 -0
  292. data/spec/parsanol/minilisp.tt +29 -0
  293. data/spec/parsanol/optimizer_spec.rb +459 -0
  294. data/spec/parsanol/options/parslet_compat_spec.rb +166 -0
  295. data/spec/parsanol/options/ruby_transform_spec.rb +70 -0
  296. data/spec/parsanol/options/serialized_spec.rb +69 -0
  297. data/spec/parsanol/options/zero_copy_spec.rb +230 -0
  298. data/spec/parsanol/parser_spec.rb +36 -0
  299. data/spec/parsanol/parslet_spec.rb +38 -0
  300. data/spec/parsanol/pattern_spec.rb +272 -0
  301. data/spec/parsanol/pool_spec.rb +392 -0
  302. data/spec/parsanol/pools/array_pool_spec.rb +356 -0
  303. data/spec/parsanol/pools/buffer_pool_spec.rb +365 -0
  304. data/spec/parsanol/pools/position_pool_spec.rb +118 -0
  305. data/spec/parsanol/pools/slice_pool_spec.rb +262 -0
  306. data/spec/parsanol/position_spec.rb +14 -0
  307. data/spec/parsanol/result_builder_spec.rb +391 -0
  308. data/spec/parsanol/rig/rspec_spec.rb +54 -0
  309. data/spec/parsanol/rope_spec.rb +207 -0
  310. data/spec/parsanol/scope_spec.rb +45 -0
  311. data/spec/parsanol/slice_spec.rb +249 -0
  312. data/spec/parsanol/source/line_cache_spec.rb +74 -0
  313. data/spec/parsanol/source_spec.rb +207 -0
  314. data/spec/parsanol/string_view_spec.rb +345 -0
  315. data/spec/parsanol/transform/context_spec.rb +56 -0
  316. data/spec/parsanol/transform_spec.rb +183 -0
  317. data/spec/parsanol/tree_memoization_spec.rb +149 -0
  318. data/spec/parslet_compatibility/expressir_edge_cases_spec.rb +153 -0
  319. data/spec/parslet_compatibility/minimal_reproduction.rb +199 -0
  320. data/spec/parslet_compatibility_spec.rb +399 -0
  321. data/spec/parslet_imported/atom_spec.rb +93 -0
  322. data/spec/parslet_imported/combinator_spec.rb +161 -0
  323. data/spec/parslet_imported/spec_helper.rb +73 -0
  324. data/spec/performance/batch_parsing_benchmark.rb +129 -0
  325. data/spec/performance/complete_optimization_summary.rb +143 -0
  326. data/spec/performance/grammar_caching_analysis.rb +121 -0
  327. data/spec/performance/grammar_caching_benchmark.rb +80 -0
  328. data/spec/performance/native_benchmark_spec.rb +230 -0
  329. data/spec/performance/phase5_benchmark.rb +144 -0
  330. data/spec/performance/profiling_benchmark.rb +131 -0
  331. data/spec/performance/ruby_improvements_benchmark.rb +171 -0
  332. data/spec/performance_spec.rb +374 -0
  333. data/spec/spec_helper.rb +79 -0
  334. data/spec/support/opal.rb +8 -0
  335. data/spec/support/opal.rb.erb +14 -0
  336. metadata +485 -0
@@ -0,0 +1,392 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ # Test class that supports reset protocol
6
+ class TestPoolable
7
+ attr_accessor :value
8
+
9
+ def initialize
10
+ @value = nil
11
+ end
12
+
13
+ def reset!
14
+ @value = nil
15
+ end
16
+ end
17
+
18
+ # Test class that does NOT support reset protocol
19
+ class TestNonResettable
20
+ attr_accessor :value
21
+
22
+ def initialize
23
+ @value = nil
24
+ end
25
+ end
26
+
27
+ # Test class that requires arguments (cannot be pre-allocated)
28
+ class TestWithArgs
29
+ attr_accessor :value
30
+
31
+ def initialize(value)
32
+ @value = value
33
+ end
34
+
35
+ def reset!
36
+ @value = nil
37
+ end
38
+ end
39
+
40
+ describe Parsanol::ObjectPool do
41
+ describe '#initialize' do
42
+ it 'creates a pool with specified size' do
43
+ pool = described_class.new(Array, size: 500)
44
+ expect(pool.size).to eq(500)
45
+ end
46
+
47
+ it 'creates a pool with default size of 1000' do
48
+ pool = described_class.new(Array)
49
+ expect(pool.size).to eq(1000)
50
+ end
51
+
52
+ it 'pre-allocates objects when preallocate is true (default)' do
53
+ pool = described_class.new(Array, size: 10, preallocate: true)
54
+ stats = pool.statistics
55
+ expect(stats[:available]).to eq(10)
56
+ expect(stats[:released]).to eq(10)
57
+ end
58
+
59
+ it 'does not pre-allocate when preallocate is false' do
60
+ pool = described_class.new(Array, size: 10, preallocate: false)
61
+ stats = pool.statistics
62
+ expect(stats[:available]).to eq(0)
63
+ expect(stats[:released]).to eq(0)
64
+ end
65
+
66
+ it 'handles classes that require arguments gracefully' do
67
+ # Should not raise error, just skip pre-allocation
68
+ pool = described_class.new(TestWithArgs, size: 10, preallocate: true)
69
+ stats = pool.statistics
70
+ expect(stats[:available]).to eq(0)
71
+ end
72
+ end
73
+
74
+ describe '#acquire' do
75
+ context 'with empty pool' do
76
+ it 'creates a new object' do
77
+ pool = described_class.new(Array, size: 10, preallocate: false)
78
+ obj = pool.acquire
79
+ expect(obj).to be_a(Array)
80
+ end
81
+
82
+ it 'increments created counter' do
83
+ pool = described_class.new(Array, size: 10, preallocate: false)
84
+ pool.acquire
85
+ stats = pool.statistics
86
+ expect(stats[:created]).to eq(1)
87
+ end
88
+
89
+ it 'does not increment reused counter' do
90
+ pool = described_class.new(Array, size: 10, preallocate: false)
91
+ pool.acquire
92
+ stats = pool.statistics
93
+ expect(stats[:reused]).to eq(0)
94
+ end
95
+ end
96
+
97
+ context 'with available objects' do
98
+ it 'returns an object from the pool' do
99
+ pool = described_class.new(Array, size: 10, preallocate: true)
100
+ obj = pool.acquire
101
+ expect(obj).to be_a(Array)
102
+ end
103
+
104
+ it 'increments reused counter' do
105
+ pool = described_class.new(Array, size: 10, preallocate: true)
106
+ pool.acquire
107
+ stats = pool.statistics
108
+ expect(stats[:reused]).to eq(1)
109
+ end
110
+
111
+ it 'does not increment created counter' do
112
+ pool = described_class.new(Array, size: 10, preallocate: true)
113
+ initial_created = pool.statistics[:created]
114
+ pool.acquire
115
+ stats = pool.statistics
116
+ expect(stats[:created]).to eq(initial_created)
117
+ end
118
+
119
+ it 'decreases available count' do
120
+ pool = described_class.new(Array, size: 10, preallocate: true)
121
+ initial_available = pool.statistics[:available]
122
+ pool.acquire
123
+ stats = pool.statistics
124
+ expect(stats[:available]).to eq(initial_available - 1)
125
+ end
126
+ end
127
+
128
+ context 'multiple acquisitions' do
129
+ it 'can acquire multiple objects' do
130
+ pool = described_class.new(Array, size: 5, preallocate: true)
131
+ objects = 3.times.map { pool.acquire }
132
+ expect(objects.size).to eq(3)
133
+ # With pre-allocation, objects come from the pool and are reused
134
+ # So they will be different instances (different object_ids)
135
+ expect(objects.map(&:object_id).uniq.size).to eq(3)
136
+ end
137
+
138
+ it 'creates new objects when pool is exhausted' do
139
+ pool = described_class.new(Array, size: 2, preallocate: true)
140
+ # Acquire all pre-allocated objects
141
+ pool.acquire
142
+ pool.acquire
143
+ # This should create a new one
144
+ obj = pool.acquire
145
+ expect(obj).to be_a(Array)
146
+ stats = pool.statistics
147
+ expect(stats[:created]).to eq(1)
148
+ expect(stats[:reused]).to eq(2)
149
+ end
150
+ end
151
+ end
152
+
153
+ describe '#release' do
154
+ context 'with objects that support reset!' do
155
+ it 'calls reset! on the object' do
156
+ pool = described_class.new(TestPoolable, size: 10, preallocate: false)
157
+ obj = pool.acquire
158
+ obj.value = 'test'
159
+ pool.release(obj)
160
+ # Acquire the same object again
161
+ reused = pool.acquire
162
+ expect(reused.value).to be_nil
163
+ end
164
+ end
165
+
166
+ context 'with objects that do not support reset!' do
167
+ it 'pools the object without calling reset!' do
168
+ pool = described_class.new(TestNonResettable, size: 10, preallocate: false)
169
+ obj = pool.acquire
170
+ obj.value = 'test'
171
+ pool.release(obj)
172
+ # Value should still be set (no reset called)
173
+ reused = pool.acquire
174
+ expect(reused.value).to eq('test')
175
+ end
176
+ end
177
+
178
+ context 'pool capacity management' do
179
+ it 'returns object to pool when under capacity' do
180
+ pool = described_class.new(Array, size: 10, preallocate: false)
181
+ obj = pool.acquire
182
+ result = pool.release(obj)
183
+ expect(result).to be true
184
+ stats = pool.statistics
185
+ expect(stats[:available]).to eq(1)
186
+ expect(stats[:released]).to eq(1)
187
+ end
188
+
189
+ it 'discards object when pool is at capacity' do
190
+ pool = described_class.new(Array, size: 2, preallocate: true)
191
+ # Try to release one more than capacity
192
+ obj = Array.new
193
+ result = pool.release(obj)
194
+ expect(result).to be false
195
+ stats = pool.statistics
196
+ expect(stats[:available]).to eq(2) # Still at capacity
197
+ expect(stats[:discarded]).to eq(1)
198
+ end
199
+
200
+ it 'increments discarded counter when capacity exceeded' do
201
+ pool = described_class.new(Array, size: 1, preallocate: true)
202
+ obj1 = Array.new
203
+ obj2 = Array.new
204
+ pool.release(obj1)
205
+ pool.release(obj2)
206
+ stats = pool.statistics
207
+ expect(stats[:discarded]).to eq(2)
208
+ end
209
+ end
210
+
211
+ context 'statistics tracking' do
212
+ it 'increments released counter' do
213
+ pool = described_class.new(Array, size: 10, preallocate: false)
214
+ obj = pool.acquire
215
+ pool.release(obj)
216
+ stats = pool.statistics
217
+ expect(stats[:released]).to eq(1)
218
+ end
219
+
220
+ it 'tracks multiple releases' do
221
+ pool = described_class.new(Array, size: 10, preallocate: false)
222
+ 3.times do
223
+ obj = pool.acquire
224
+ pool.release(obj)
225
+ end
226
+ stats = pool.statistics
227
+ expect(stats[:released]).to eq(3)
228
+ end
229
+ end
230
+ end
231
+
232
+ describe '#statistics' do
233
+ it 'returns hash with all statistics' do
234
+ pool = described_class.new(Array, size: 10, preallocate: false)
235
+ stats = pool.statistics
236
+ expect(stats).to include(
237
+ :size, :available, :created, :reused, :released, :discarded, :utilization
238
+ )
239
+ end
240
+
241
+ it 'calculates utilization percentage correctly' do
242
+ pool = described_class.new(Array, size: 10, preallocate: false)
243
+ # Create 3 objects first (created=3)
244
+ objs = 3.times.map { pool.acquire }
245
+ # Release them back to pool
246
+ objs.each { |obj| pool.release(obj) }
247
+ # Acquire 5 more: 3 from pool (reused=3), 2 new (created=5 total)
248
+ # Total acquires = 3 + 5 = 8, reused = 3, utilization = 3/8 = 37.5%
249
+ 5.times { pool.acquire }
250
+ stats = pool.statistics
251
+ expect(stats[:utilization]).to eq(37.5)
252
+ end
253
+
254
+ it 'handles zero acquires without division by zero' do
255
+ pool = described_class.new(Array, size: 10, preallocate: false)
256
+ stats = pool.statistics
257
+ expect(stats[:utilization]).to eq(0.0)
258
+ end
259
+
260
+ it 'tracks available count correctly' do
261
+ pool = described_class.new(Array, size: 10, preallocate: true)
262
+ pool.acquire
263
+ pool.acquire
264
+ stats = pool.statistics
265
+ expect(stats[:available]).to eq(8)
266
+ end
267
+ end
268
+
269
+ describe '#clear!' do
270
+ it 'removes all objects from pool' do
271
+ pool = described_class.new(Array, size: 10, preallocate: true)
272
+ pool.clear!
273
+ stats = pool.statistics
274
+ expect(stats[:available]).to eq(0)
275
+ end
276
+
277
+ it 'resets all statistics' do
278
+ pool = described_class.new(Array, size: 10, preallocate: true)
279
+ 3.times { pool.acquire }
280
+ pool.clear!
281
+ stats = pool.statistics
282
+ expect(stats[:created]).to eq(0)
283
+ expect(stats[:reused]).to eq(0)
284
+ expect(stats[:released]).to eq(0)
285
+ expect(stats[:discarded]).to eq(0)
286
+ end
287
+ end
288
+
289
+ describe 'object reuse cycle' do
290
+ it 'successfully reuses objects through multiple cycles' do
291
+ pool = described_class.new(TestPoolable, size: 5, preallocate: false)
292
+
293
+ # Cycle 1: Acquire and release
294
+ obj1 = pool.acquire
295
+ obj1.value = 'cycle1'
296
+ pool.release(obj1)
297
+
298
+ # Cycle 2: Reuse same object
299
+ obj2 = pool.acquire
300
+ expect(obj2.value).to be_nil # Should be reset
301
+ obj2.value = 'cycle2'
302
+ pool.release(obj2)
303
+
304
+ # Cycle 3: Reuse again
305
+ obj3 = pool.acquire
306
+ expect(obj3.value).to be_nil # Should be reset again
307
+
308
+ # All cycles should use the same object
309
+ expect(obj1.object_id).to eq(obj2.object_id)
310
+ expect(obj2.object_id).to eq(obj3.object_id)
311
+ end
312
+
313
+ it 'maintains object count correctly through cycles' do
314
+ pool = described_class.new(Array, size: 3, preallocate: false)
315
+
316
+ # Create 3 objects
317
+ obj1 = pool.acquire
318
+ obj2 = pool.acquire
319
+ obj3 = pool.acquire
320
+
321
+ # Release all
322
+ pool.release(obj1)
323
+ pool.release(obj2)
324
+ pool.release(obj3)
325
+
326
+ stats = pool.statistics
327
+ expect(stats[:available]).to eq(3)
328
+ expect(stats[:created]).to eq(3)
329
+
330
+ # Reuse all 3
331
+ pool.acquire
332
+ pool.acquire
333
+ pool.acquire
334
+
335
+ stats = pool.statistics
336
+ expect(stats[:available]).to eq(0)
337
+ expect(stats[:reused]).to eq(3)
338
+ expect(stats[:created]).to eq(3) # No new creations
339
+ end
340
+ end
341
+
342
+ describe 'edge cases' do
343
+ it 'handles pool size of 0 gracefully' do
344
+ pool = described_class.new(Array, size: 0, preallocate: false)
345
+ obj = pool.acquire
346
+ result = pool.release(obj)
347
+ expect(result).to be false # Always discarded
348
+ expect(pool.statistics[:discarded]).to eq(1)
349
+ end
350
+
351
+ it 'handles pool size of 1' do
352
+ pool = described_class.new(Array, size: 1, preallocate: true)
353
+ obj = pool.acquire
354
+ expect(pool.statistics[:available]).to eq(0)
355
+ pool.release(obj)
356
+ expect(pool.statistics[:available]).to eq(1)
357
+ end
358
+
359
+ it 'handles large pool sizes' do
360
+ pool = described_class.new(Array, size: 10000, preallocate: true)
361
+ expect(pool.statistics[:available]).to eq(10000)
362
+ end
363
+ end
364
+
365
+ describe 'pool overhead' do
366
+ it 'has minimal overhead for acquire operations' do
367
+ pool = described_class.new(Array, size: 1000, preallocate: true)
368
+
369
+ # Measure 1000 acquires from pre-allocated pool
370
+ start_time = Time.now
371
+ 1000.times { pool.acquire }
372
+ elapsed = Time.now - start_time
373
+
374
+ # Pool acquires should be very fast (< 10ms for 1000 operations)
375
+ # This validates that pool overhead is minimal
376
+ expect(elapsed).to be < 0.01
377
+ end
378
+
379
+ it 'has minimal overhead for release operations' do
380
+ pool = described_class.new(TestPoolable, size: 1000, preallocate: false)
381
+ objects = 100.times.map { pool.acquire }
382
+
383
+ # Measure 100 releases with reset
384
+ start_time = Time.now
385
+ objects.each { |obj| pool.release(obj) }
386
+ elapsed = Time.now - start_time
387
+
388
+ # Releases with reset should be fast (< 10ms for 100 operations)
389
+ expect(elapsed).to be < 0.01
390
+ end
391
+ end
392
+ end