neo4j-ruby-driver 6.0.3.alpha.0 → 6.2.1.beta.2

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 (348) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +113 -411
  3. data/{ruby → lib}/neo4j/driver/access_mode.rb +2 -1
  4. data/lib/neo4j/driver/auth_token_managers.rb +31 -0
  5. data/lib/neo4j/driver/auth_tokens.rb +41 -0
  6. data/lib/neo4j/driver/auto_commit_retries_mode.rb +18 -0
  7. data/lib/neo4j/driver/bolt/bolt_version.rb +67 -0
  8. data/lib/neo4j/driver/bolt/connection.rb +1091 -0
  9. data/lib/neo4j/driver/bolt/handshake.rb +236 -0
  10. data/lib/neo4j/driver/bolt/message/failure.rb +139 -0
  11. data/lib/neo4j/driver/bolt/message/ignored.rb +27 -0
  12. data/lib/neo4j/driver/bolt/message/record.rb +30 -0
  13. data/lib/neo4j/driver/bolt/message/success.rb +34 -0
  14. data/lib/neo4j/driver/bolt/message.rb +112 -0
  15. data/lib/neo4j/driver/bolt/pool.rb +255 -0
  16. data/lib/neo4j/driver/bolt/protocol/base.rb +146 -0
  17. data/lib/neo4j/driver/bolt/protocol/v3.rb +43 -0
  18. data/lib/neo4j/driver/bolt/protocol/v4.rb +24 -0
  19. data/lib/neo4j/driver/bolt/protocol/v43.rb +28 -0
  20. data/lib/neo4j/driver/bolt/protocol/v44.rb +23 -0
  21. data/lib/neo4j/driver/bolt/protocol/v5.rb +27 -0
  22. data/lib/neo4j/driver/bolt/protocol/v51.rb +24 -0
  23. data/lib/neo4j/driver/bolt/protocol/v52.rb +28 -0
  24. data/lib/neo4j/driver/bolt/protocol/v53.rb +27 -0
  25. data/lib/neo4j/driver/bolt/protocol/v54.rb +14 -0
  26. data/lib/neo4j/driver/bolt/protocol/v55.rb +17 -0
  27. data/lib/neo4j/driver/bolt/protocol/v56.rb +14 -0
  28. data/lib/neo4j/driver/bolt/protocol/v57.rb +31 -0
  29. data/lib/neo4j/driver/bolt/protocol/v58.rb +15 -0
  30. data/lib/neo4j/driver/bolt/protocol/v6.rb +43 -0
  31. data/lib/neo4j/driver/bolt/protocol/v61.rb +21 -0
  32. data/lib/neo4j/driver/bolt/protocol_version_handler.rb +39 -0
  33. data/lib/neo4j/driver/bolt/record_buffer.rb +143 -0
  34. data/lib/neo4j/driver/bolt/stream_handler.rb +53 -0
  35. data/lib/neo4j/driver/bolt/tls_config.rb +129 -0
  36. data/lib/neo4j/driver/bolt/wire.rb +321 -0
  37. data/lib/neo4j/driver/bookmark.rb +15 -0
  38. data/lib/neo4j/driver/bookmark_managers.rb +39 -0
  39. data/lib/neo4j/driver/client_certificate.rb +19 -0
  40. data/lib/neo4j/driver/client_certificate_manager.rb +37 -0
  41. data/lib/neo4j/driver/client_certificate_managers.rb +15 -0
  42. data/lib/neo4j/driver/client_certificates.rb +14 -0
  43. data/lib/neo4j/driver/direct/connection_provider.rb +262 -0
  44. data/lib/neo4j/driver/driver.rb +268 -0
  45. data/lib/neo4j/driver/eager_result.rb +10 -0
  46. data/lib/neo4j/driver/exceptions/neo4j_exception.rb +16 -7
  47. data/lib/neo4j/driver/exceptions/no_such_record_exception.rb +1 -1
  48. data/lib/neo4j/driver/exceptions/protocol_exception.rb +0 -5
  49. data/lib/neo4j/driver/exceptions/security_retryable_exception.rb +14 -0
  50. data/lib/neo4j/driver/exceptions/transaction_terminated_exception.rb +13 -0
  51. data/lib/neo4j/driver/graph_database.rb +29 -0
  52. data/lib/neo4j/driver/internal/clock.rb +20 -0
  53. data/lib/neo4j/driver/internal/clock_adapter.rb +21 -0
  54. data/lib/neo4j/driver/internal/default_bookmark_manager.rb +54 -0
  55. data/lib/neo4j/driver/internal/deprecator.rb +9 -5
  56. data/lib/neo4j/driver/internal/driver_factory.rb +128 -0
  57. data/lib/neo4j/driver/internal/duration_normalizer.rb +14 -39
  58. data/lib/neo4j/driver/internal/extras.rb +23 -0
  59. data/lib/neo4j/driver/internal/home_db_cache.rb +57 -0
  60. data/lib/neo4j/driver/internal/internal_auth_token_manager.rb +37 -0
  61. data/lib/neo4j/driver/internal/metrics.rb +31 -0
  62. data/lib/neo4j/driver/internal/security/static_auth_token_manager.rb +25 -0
  63. data/lib/neo4j/driver/internal/validator.rb +16 -2
  64. data/lib/neo4j/driver/net.rb +29 -0
  65. data/lib/neo4j/driver/packstream/markers.rb +49 -0
  66. data/lib/neo4j/driver/packstream/packer.rb +335 -0
  67. data/lib/neo4j/driver/packstream/structure.rb +27 -0
  68. data/lib/neo4j/driver/packstream/unpacker.rb +170 -0
  69. data/lib/neo4j/driver/record.rb +43 -0
  70. data/lib/neo4j/driver/result.rb +282 -0
  71. data/lib/neo4j/driver/routing/load_balancer.rb +706 -0
  72. data/lib/neo4j/driver/routing/routed_connection.rb +132 -0
  73. data/lib/neo4j/driver/routing/routing_table.rb +127 -0
  74. data/lib/neo4j/driver/routing/server_address.rb +25 -0
  75. data/lib/neo4j/driver/routing_control.rb +13 -0
  76. data/lib/neo4j/driver/session.rb +577 -0
  77. data/lib/neo4j/driver/summary/database_info.rb +25 -0
  78. data/lib/neo4j/driver/summary/gql_notification.rb +38 -0
  79. data/lib/neo4j/driver/summary/gql_status_object.rb +35 -0
  80. data/lib/neo4j/driver/summary/notification.rb +46 -0
  81. data/lib/neo4j/driver/summary/plan.rb +23 -0
  82. data/lib/neo4j/driver/summary/profile.rb +28 -0
  83. data/lib/neo4j/driver/summary/query.rb +19 -0
  84. data/lib/neo4j/driver/summary/query_type.rb +5 -1
  85. data/lib/neo4j/driver/summary/result_summary.rb +215 -0
  86. data/lib/neo4j/driver/summary/server_info.rb +33 -0
  87. data/lib/neo4j/driver/summary/summary_counters.rb +72 -0
  88. data/lib/neo4j/driver/transaction.rb +344 -0
  89. data/lib/neo4j/driver/types/duration.rb +76 -0
  90. data/lib/neo4j/driver/types/entity.rb +40 -0
  91. data/lib/neo4j/driver/types/local_date_time.rb +77 -8
  92. data/lib/neo4j/driver/types/local_time.rb +50 -7
  93. data/lib/neo4j/driver/types/node.rb +18 -0
  94. data/lib/neo4j/driver/types/offset_time.rb +78 -7
  95. data/lib/neo4j/driver/types/path.rb +68 -0
  96. data/lib/neo4j/driver/types/point.rb +38 -18
  97. data/lib/neo4j/driver/types/relationship.rb +32 -0
  98. data/lib/neo4j/driver/types/temporal_value.rb +44 -0
  99. data/lib/neo4j/driver/types/unbound_relationship.rb +24 -0
  100. data/lib/neo4j/driver/types/unresolvable_zoned_date_time.rb +38 -0
  101. data/lib/neo4j/driver/types/unsupported_type.rb +25 -0
  102. data/lib/neo4j/driver/types/uuid.rb +29 -0
  103. data/{ruby → lib}/neo4j/driver/version.rb +1 -1
  104. data/lib/neo4j/driver.rb +15 -0
  105. data/lib/neo4j-ruby-driver_loader.rb +34 -20
  106. metadata +115 -441
  107. data/lib/neo4j/driver/internal/bolt_server_address.rb +0 -97
  108. data/lib/neo4j/driver/synchronizable.rb +0 -23
  109. data/lib/neo4j/driver/types/time.rb +0 -45
  110. data/ruby/neo4j/driver/auth_tokens.rb +0 -34
  111. data/ruby/neo4j/driver/bookmark.rb +0 -21
  112. data/ruby/neo4j/driver/config.rb +0 -89
  113. data/ruby/neo4j/driver/graph_database.rb +0 -78
  114. data/ruby/neo4j/driver/internal/async/connection/bolt_protocol_util.rb +0 -51
  115. data/ruby/neo4j/driver/internal/async/connection/bootstrap_factory.rb +0 -22
  116. data/ruby/neo4j/driver/internal/async/connection/channel_attributes.rb +0 -31
  117. data/ruby/neo4j/driver/internal/async/connection/channel_connected_listener.rb +0 -32
  118. data/ruby/neo4j/driver/internal/async/connection/channel_connector_impl.rb +0 -83
  119. data/ruby/neo4j/driver/internal/async/connection/channel_pipeline_builder_impl.rb +0 -22
  120. data/ruby/neo4j/driver/internal/async/connection/direct_connection.rb +0 -30
  121. data/ruby/neo4j/driver/internal/async/connection/event_loop_group_factory.rb +0 -83
  122. data/ruby/neo4j/driver/internal/async/connection/handshake_completed_listener.rb +0 -27
  123. data/ruby/neo4j/driver/internal/async/connection/handshake_handler.rb +0 -113
  124. data/ruby/neo4j/driver/internal/async/connection/netty_channel_initializer.rb +0 -57
  125. data/ruby/neo4j/driver/internal/async/connection/netty_domain_name_resolver.rb +0 -26
  126. data/ruby/neo4j/driver/internal/async/connection/netty_domain_name_resolver_group.rb +0 -19
  127. data/ruby/neo4j/driver/internal/async/connection/routing_connection.rb +0 -36
  128. data/ruby/neo4j/driver/internal/async/connection/stream.rb +0 -12
  129. data/ruby/neo4j/driver/internal/async/connection/stream_reader.rb +0 -16
  130. data/ruby/neo4j/driver/internal/async/connection_context.rb +0 -10
  131. data/ruby/neo4j/driver/internal/async/immutable_connection_context.rb +0 -24
  132. data/ruby/neo4j/driver/internal/async/inbound/byte_buf_input.rb +0 -30
  133. data/ruby/neo4j/driver/internal/async/inbound/channel_error_handler.rb +0 -77
  134. data/ruby/neo4j/driver/internal/async/inbound/chunk_decoder.rb +0 -41
  135. data/ruby/neo4j/driver/internal/async/inbound/connect_timeout_handler.rb +0 -32
  136. data/ruby/neo4j/driver/internal/async/inbound/connection_read_timeout_handler.rb +0 -17
  137. data/ruby/neo4j/driver/internal/async/inbound/inbound_message_dispatcher.rb +0 -171
  138. data/ruby/neo4j/driver/internal/async/inbound/inbound_message_handler.rb +0 -42
  139. data/ruby/neo4j/driver/internal/async/inbound/message_decoder.rb +0 -51
  140. data/ruby/neo4j/driver/internal/async/internal_async_session.rb +0 -92
  141. data/ruby/neo4j/driver/internal/async/internal_async_transaction.rb +0 -13
  142. data/ruby/neo4j/driver/internal/async/leak_logging_network_session.rb +0 -34
  143. data/ruby/neo4j/driver/internal/async/network_connection.rb +0 -194
  144. data/ruby/neo4j/driver/internal/async/network_session.rb +0 -150
  145. data/ruby/neo4j/driver/internal/async/outbound/chunk_aware_byte_buf_output.rb +0 -110
  146. data/ruby/neo4j/driver/internal/async/outbound/outbound_message_handler.rb +0 -39
  147. data/ruby/neo4j/driver/internal/async/pool/channel.rb +0 -66
  148. data/ruby/neo4j/driver/internal/async/pool/channel_pool.rb +0 -31
  149. data/ruby/neo4j/driver/internal/async/pool/channel_tracker.rb +0 -135
  150. data/ruby/neo4j/driver/internal/async/pool/connection_pool_impl.rb +0 -156
  151. data/ruby/neo4j/driver/internal/async/pool/netty_channel_health_checker.rb +0 -87
  152. data/ruby/neo4j/driver/internal/async/pool/netty_channel_pool.rb +0 -52
  153. data/ruby/neo4j/driver/internal/async/pool/network_connection_factory.rb +0 -21
  154. data/ruby/neo4j/driver/internal/async/pool/pool_settings.rb +0 -34
  155. data/ruby/neo4j/driver/internal/async/pool/timed_stack.rb +0 -15
  156. data/ruby/neo4j/driver/internal/async/result_cursors_holder.rb +0 -17
  157. data/ruby/neo4j/driver/internal/async/unmanaged_transaction.rb +0 -212
  158. data/ruby/neo4j/driver/internal/bookmark_holder.rb +0 -9
  159. data/ruby/neo4j/driver/internal/cluster/cluster_composition.rb +0 -48
  160. data/ruby/neo4j/driver/internal/cluster/cluster_composition_lookup_result.rb +0 -14
  161. data/ruby/neo4j/driver/internal/cluster/cluster_routing_table.rb +0 -122
  162. data/ruby/neo4j/driver/internal/cluster/identity_resolver.rb +0 -10
  163. data/ruby/neo4j/driver/internal/cluster/loadbalancing/least_connected_load_balancing_strategy.rb +0 -68
  164. data/ruby/neo4j/driver/internal/cluster/loadbalancing/load_balancer.rb +0 -125
  165. data/ruby/neo4j/driver/internal/cluster/loadbalancing/round_robin_array_index.rb +0 -13
  166. data/ruby/neo4j/driver/internal/cluster/multi_databases_routing_procedure_runner.rb +0 -31
  167. data/ruby/neo4j/driver/internal/cluster/rediscovery_impl.rb +0 -147
  168. data/ruby/neo4j/driver/internal/cluster/route_message_routing_procedure_runner.rb +0 -43
  169. data/ruby/neo4j/driver/internal/cluster/routing_context.rb +0 -77
  170. data/ruby/neo4j/driver/internal/cluster/routing_procedure_cluster_composition_provider.rb +0 -60
  171. data/ruby/neo4j/driver/internal/cluster/routing_procedure_response.rb +0 -35
  172. data/ruby/neo4j/driver/internal/cluster/routing_settings.rb +0 -24
  173. data/ruby/neo4j/driver/internal/cluster/routing_table_handler_impl.rb +0 -95
  174. data/ruby/neo4j/driver/internal/cluster/routing_table_registry_impl.rb +0 -121
  175. data/ruby/neo4j/driver/internal/cluster/single_database_routing_procedure_runner.rb +0 -73
  176. data/ruby/neo4j/driver/internal/connection_settings.rb +0 -16
  177. data/ruby/neo4j/driver/internal/cursor/async_result_cursor_impl.rb +0 -55
  178. data/ruby/neo4j/driver/internal/cursor/async_result_cursor_only_factory.rb +0 -24
  179. data/ruby/neo4j/driver/internal/cursor/disposable_async_result_cursor.rb +0 -61
  180. data/ruby/neo4j/driver/internal/cursor/result_cursor_factory_impl.rb +0 -24
  181. data/ruby/neo4j/driver/internal/cursor/rx_result_cursor_impl.rb +0 -110
  182. data/ruby/neo4j/driver/internal/database_name_util.rb +0 -37
  183. data/ruby/neo4j/driver/internal/default_bookmark_holder.rb +0 -9
  184. data/ruby/neo4j/driver/internal/default_domain_name_resolver.rb +0 -11
  185. data/ruby/neo4j/driver/internal/direct_connection_provider.rb +0 -40
  186. data/ruby/neo4j/driver/internal/driver_factory.rb +0 -126
  187. data/ruby/neo4j/driver/internal/eager_result_value.rb +0 -5
  188. data/ruby/neo4j/driver/internal/handlers/begin_tx_response_handler.rb +0 -20
  189. data/ruby/neo4j/driver/internal/handlers/channel_releasing_reset_response_handler.rb +0 -30
  190. data/ruby/neo4j/driver/internal/handlers/commit_tx_response_handler.rb +0 -23
  191. data/ruby/neo4j/driver/internal/handlers/hello_response_handler.rb +0 -65
  192. data/ruby/neo4j/driver/internal/handlers/init_response_handler.rb +0 -34
  193. data/ruby/neo4j/driver/internal/handlers/legacy_pull_all_response_handler.rb +0 -199
  194. data/ruby/neo4j/driver/internal/handlers/no_op_response_handler.rb +0 -16
  195. data/ruby/neo4j/driver/internal/handlers/ping_response_handler.rb +0 -29
  196. data/ruby/neo4j/driver/internal/handlers/pull_handlers.rb +0 -32
  197. data/ruby/neo4j/driver/internal/handlers/pulln/auto_pull_response_handler.rb +0 -169
  198. data/ruby/neo4j/driver/internal/handlers/pulln/basic_pull_response_handler.rb +0 -298
  199. data/ruby/neo4j/driver/internal/handlers/pulln/fetch_size_util.rb +0 -20
  200. data/ruby/neo4j/driver/internal/handlers/reset_response_handler.rb +0 -34
  201. data/ruby/neo4j/driver/internal/handlers/rollback_tx_response_handler.rb +0 -25
  202. data/ruby/neo4j/driver/internal/handlers/route_message_response_handler.rb +0 -21
  203. data/ruby/neo4j/driver/internal/handlers/routing_response_handler.rb +0 -79
  204. data/ruby/neo4j/driver/internal/handlers/run_response_handler.rb +0 -38
  205. data/ruby/neo4j/driver/internal/handlers/session_pull_response_completion_listener.rb +0 -34
  206. data/ruby/neo4j/driver/internal/handlers/transaction_pull_response_completion_listener.rb +0 -20
  207. data/ruby/neo4j/driver/internal/impersonation_util.rb +0 -22
  208. data/ruby/neo4j/driver/internal/internal_bookmark.rb +0 -9
  209. data/ruby/neo4j/driver/internal/internal_database_name.rb +0 -9
  210. data/ruby/neo4j/driver/internal/internal_driver.rb +0 -83
  211. data/ruby/neo4j/driver/internal/internal_entity.rb +0 -21
  212. data/ruby/neo4j/driver/internal/internal_node.rb +0 -21
  213. data/ruby/neo4j/driver/internal/internal_pair.rb +0 -9
  214. data/ruby/neo4j/driver/internal/internal_path.rb +0 -35
  215. data/ruby/neo4j/driver/internal/internal_point2_d.rb +0 -9
  216. data/ruby/neo4j/driver/internal/internal_point3_d.rb +0 -6
  217. data/ruby/neo4j/driver/internal/internal_record.rb +0 -27
  218. data/ruby/neo4j/driver/internal/internal_relationship.rb +0 -27
  219. data/ruby/neo4j/driver/internal/internal_result.rb +0 -49
  220. data/ruby/neo4j/driver/internal/internal_session.rb +0 -91
  221. data/ruby/neo4j/driver/internal/internal_transaction.rb +0 -48
  222. data/ruby/neo4j/driver/internal/logging/channel_activity_logger.rb +0 -29
  223. data/ruby/neo4j/driver/internal/logging/channel_error_logger.rb +0 -17
  224. data/ruby/neo4j/driver/internal/logging/prefixed_logger.rb +0 -19
  225. data/ruby/neo4j/driver/internal/logging/reformatted_logger.rb +0 -17
  226. data/ruby/neo4j/driver/internal/messaging/abstract_message_writer.rb +0 -23
  227. data/ruby/neo4j/driver/internal/messaging/bolt_protocol.rb +0 -32
  228. data/ruby/neo4j/driver/internal/messaging/bolt_protocol_version.rb +0 -48
  229. data/ruby/neo4j/driver/internal/messaging/common/common_message_reader.rb +0 -51
  230. data/ruby/neo4j/driver/internal/messaging/common/common_value.rb +0 -33
  231. data/ruby/neo4j/driver/internal/messaging/common/common_value_packer.rb +0 -104
  232. data/ruby/neo4j/driver/internal/messaging/common/common_value_unpacker.rb +0 -256
  233. data/ruby/neo4j/driver/internal/messaging/encode/begin_message_encoder.rb +0 -15
  234. data/ruby/neo4j/driver/internal/messaging/encode/commit_message_encoder.rb +0 -14
  235. data/ruby/neo4j/driver/internal/messaging/encode/discard_all_message_encoder.rb +0 -14
  236. data/ruby/neo4j/driver/internal/messaging/encode/discard_message_encoder.rb +0 -15
  237. data/ruby/neo4j/driver/internal/messaging/encode/goodbye_message_encoder.rb +0 -14
  238. data/ruby/neo4j/driver/internal/messaging/encode/hello_message_encoder.rb +0 -15
  239. data/ruby/neo4j/driver/internal/messaging/encode/init_message_encoder.rb +0 -16
  240. data/ruby/neo4j/driver/internal/messaging/encode/logoff_message_encoder.rb +0 -14
  241. data/ruby/neo4j/driver/internal/messaging/encode/logon_message_encoder.rb +0 -15
  242. data/ruby/neo4j/driver/internal/messaging/encode/pull_all_message_encoder.rb +0 -14
  243. data/ruby/neo4j/driver/internal/messaging/encode/pull_message_encoder.rb +0 -15
  244. data/ruby/neo4j/driver/internal/messaging/encode/reset_message_encoder.rb +0 -14
  245. data/ruby/neo4j/driver/internal/messaging/encode/rollback_message_encoder.rb +0 -14
  246. data/ruby/neo4j/driver/internal/messaging/encode/route_message_encoder.rb +0 -24
  247. data/ruby/neo4j/driver/internal/messaging/encode/route_v44_message_encoder.rb +0 -22
  248. data/ruby/neo4j/driver/internal/messaging/encode/run_message_encoder.rb +0 -16
  249. data/ruby/neo4j/driver/internal/messaging/encode/run_with_metadata_message_encoder.rb +0 -17
  250. data/ruby/neo4j/driver/internal/messaging/request/abstract_streaming_message.rb +0 -25
  251. data/ruby/neo4j/driver/internal/messaging/request/begin_message.rb +0 -25
  252. data/ruby/neo4j/driver/internal/messaging/request/commit_message.rb +0 -20
  253. data/ruby/neo4j/driver/internal/messaging/request/discard_all_message.rb +0 -20
  254. data/ruby/neo4j/driver/internal/messaging/request/discard_message.rb +0 -23
  255. data/ruby/neo4j/driver/internal/messaging/request/goodbye_message.rb +0 -20
  256. data/ruby/neo4j/driver/internal/messaging/request/hello_message.rb +0 -30
  257. data/ruby/neo4j/driver/internal/messaging/request/init_message.rb +0 -19
  258. data/ruby/neo4j/driver/internal/messaging/request/logoff_message.rb +0 -13
  259. data/ruby/neo4j/driver/internal/messaging/request/logon_message.rb +0 -13
  260. data/ruby/neo4j/driver/internal/messaging/request/message_with_metadata.rb +0 -17
  261. data/ruby/neo4j/driver/internal/messaging/request/multi_database_util.rb +0 -26
  262. data/ruby/neo4j/driver/internal/messaging/request/pull_all_message.rb +0 -23
  263. data/ruby/neo4j/driver/internal/messaging/request/pull_message.rb +0 -22
  264. data/ruby/neo4j/driver/internal/messaging/request/reset_message.rb +0 -32
  265. data/ruby/neo4j/driver/internal/messaging/request/rollback_message.rb +0 -20
  266. data/ruby/neo4j/driver/internal/messaging/request/route_message.rb +0 -28
  267. data/ruby/neo4j/driver/internal/messaging/request/run_message.rb +0 -23
  268. data/ruby/neo4j/driver/internal/messaging/request/run_with_metadata_message.rb +0 -49
  269. data/ruby/neo4j/driver/internal/messaging/request/transaction_metadata_builder.rb +0 -24
  270. data/ruby/neo4j/driver/internal/messaging/response/failure_message.rb +0 -40
  271. data/ruby/neo4j/driver/internal/messaging/response/ignored_message.rb +0 -29
  272. data/ruby/neo4j/driver/internal/messaging/response/record_message.rb +0 -33
  273. data/ruby/neo4j/driver/internal/messaging/response/success_message.rb +0 -34
  274. data/ruby/neo4j/driver/internal/messaging/v3/bolt_protocol_v3.rb +0 -82
  275. data/ruby/neo4j/driver/internal/messaging/v3/message_format_v3.rb +0 -17
  276. data/ruby/neo4j/driver/internal/messaging/v3/message_writer_v3.rb +0 -27
  277. data/ruby/neo4j/driver/internal/messaging/v4/bolt_protocol_v4.rb +0 -29
  278. data/ruby/neo4j/driver/internal/messaging/v4/message_format_v4.rb +0 -13
  279. data/ruby/neo4j/driver/internal/messaging/v4/message_writer_v4.rb +0 -17
  280. data/ruby/neo4j/driver/internal/messaging/v41/bolt_protocol_v41.rb +0 -25
  281. data/ruby/neo4j/driver/internal/messaging/v42/bolt_protocol_v42.rb +0 -13
  282. data/ruby/neo4j/driver/internal/messaging/v43/bolt_protocol_v43.rb +0 -19
  283. data/ruby/neo4j/driver/internal/messaging/v43/message_format_v43.rb +0 -14
  284. data/ruby/neo4j/driver/internal/messaging/v43/message_writer_v43.rb +0 -20
  285. data/ruby/neo4j/driver/internal/messaging/v44/bolt_protocol_v44.rb +0 -17
  286. data/ruby/neo4j/driver/internal/messaging/v44/message_format_v44.rb +0 -14
  287. data/ruby/neo4j/driver/internal/messaging/v44/message_writer_v44.rb +0 -15
  288. data/ruby/neo4j/driver/internal/messaging/v5/bolt_protocol_v5.rb +0 -21
  289. data/ruby/neo4j/driver/internal/messaging/v5/message_format_v5.rb +0 -18
  290. data/ruby/neo4j/driver/internal/messaging/v5/value_unpacker_v5.rb +0 -17
  291. data/ruby/neo4j/driver/internal/messaging/v51/bolt_protocol_v51.rb +0 -40
  292. data/ruby/neo4j/driver/internal/messaging/v51/message_format_v51.rb +0 -14
  293. data/ruby/neo4j/driver/internal/messaging/v51/message_writer_v51.rb +0 -17
  294. data/ruby/neo4j/driver/internal/metrics/connection_pool_metrics_listener.rb +0 -34
  295. data/ruby/neo4j/driver/internal/metrics/internal_abstract_metrics.rb +0 -46
  296. data/ruby/neo4j/driver/internal/metrics/internal_connection_pool_metrics.rb +0 -105
  297. data/ruby/neo4j/driver/internal/metrics/internal_metrics.rb +0 -82
  298. data/ruby/neo4j/driver/internal/metrics/internal_metrics_provider.rb +0 -18
  299. data/ruby/neo4j/driver/internal/metrics/listener_event.rb +0 -17
  300. data/ruby/neo4j/driver/internal/metrics/metrics_provider.rb +0 -24
  301. data/ruby/neo4j/driver/internal/metrics/time_recorder_listener_event.rb +0 -15
  302. data/ruby/neo4j/driver/internal/packstream/byte_array_incompatible_packer.rb +0 -12
  303. data/ruby/neo4j/driver/internal/packstream/pack_input.rb +0 -47
  304. data/ruby/neo4j/driver/internal/packstream/pack_output.rb +0 -39
  305. data/ruby/neo4j/driver/internal/packstream/pack_stream.rb +0 -324
  306. data/ruby/neo4j/driver/internal/packstream/pack_type.rb +0 -17
  307. data/ruby/neo4j/driver/internal/read_only_bookmark_holder.rb +0 -13
  308. data/ruby/neo4j/driver/internal/resolved_bolt_server_address.rb +0 -35
  309. data/ruby/neo4j/driver/internal/retry/exponential_backoff_retry_logic.rb +0 -150
  310. data/ruby/neo4j/driver/internal/revocation_strategy.rb +0 -19
  311. data/ruby/neo4j/driver/internal/scheme.rb +0 -32
  312. data/ruby/neo4j/driver/internal/security/internal_auth_token.rb +0 -15
  313. data/ruby/neo4j/driver/internal/security/security_plan_impl.rb +0 -48
  314. data/ruby/neo4j/driver/internal/security_setting.rb +0 -66
  315. data/ruby/neo4j/driver/internal/session_factory_impl.rb +0 -32
  316. data/ruby/neo4j/driver/internal/spi/connection.rb +0 -19
  317. data/ruby/neo4j/driver/internal/spi/connection_pool.rb +0 -9
  318. data/ruby/neo4j/driver/internal/spi/response_handler.rb +0 -23
  319. data/ruby/neo4j/driver/internal/summary/internal_database_info.rb +0 -7
  320. data/ruby/neo4j/driver/internal/summary/internal_input_position.rb +0 -11
  321. data/ruby/neo4j/driver/internal/summary/internal_notification.rb +0 -16
  322. data/ruby/neo4j/driver/internal/summary/internal_plan.rb +0 -41
  323. data/ruby/neo4j/driver/internal/summary/internal_profiled_plan.rb +0 -32
  324. data/ruby/neo4j/driver/internal/summary/internal_result_summary.rb +0 -33
  325. data/ruby/neo4j/driver/internal/summary/internal_server_info.rb +0 -6
  326. data/ruby/neo4j/driver/internal/summary/internal_summary_counters.rb +0 -18
  327. data/ruby/neo4j/driver/internal/svm/netty_substitutions.rb +0 -196
  328. data/ruby/neo4j/driver/internal/svm/z_lib_substitutions.rb +0 -21
  329. data/ruby/neo4j/driver/internal/util/certificate_tool.rb +0 -65
  330. data/ruby/neo4j/driver/internal/util/clock.rb +0 -29
  331. data/ruby/neo4j/driver/internal/util/error_util.rb +0 -104
  332. data/ruby/neo4j/driver/internal/util/extract.rb +0 -123
  333. data/ruby/neo4j/driver/internal/util/format.rb +0 -39
  334. data/ruby/neo4j/driver/internal/util/futures.rb +0 -99
  335. data/ruby/neo4j/driver/internal/util/iterables.rb +0 -35
  336. data/ruby/neo4j/driver/internal/util/lock_util.rb +0 -23
  337. data/ruby/neo4j/driver/internal/util/metadata_extractor.rb +0 -101
  338. data/ruby/neo4j/driver/internal/util/mutex.rb +0 -9
  339. data/ruby/neo4j/driver/internal/util/preconditions.rb +0 -16
  340. data/ruby/neo4j/driver/internal/util/result_holder.rb +0 -72
  341. data/ruby/neo4j/driver/internal/util/server_version.rb +0 -60
  342. data/ruby/neo4j/driver/logging1.rb +0 -51
  343. data/ruby/neo4j/driver/net/server_address.rb +0 -9
  344. data/ruby/neo4j/driver/query.rb +0 -48
  345. data/ruby/neo4j/driver/records.rb +0 -13
  346. data/ruby/neo4j/driver/transaction_config.rb +0 -50
  347. data/ruby/neo4j/driver/values.rb +0 -26
  348. data/ruby/neo4j/driver.rb +0 -30
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Summary
6
+ # Server-emitted notification (e.g. "this query has a missing index").
7
+ # Mirrors org.neo4j.driver.summary.Notification.
8
+ class Notification
9
+ attr_reader :code, :title, :description, :severity_level, :severity,
10
+ :raw_severity_level, :category, :raw_category, :position
11
+
12
+ # Severity mapping matches the Java driver's NotificationSeverity:
13
+ # WARNING/INFORMATION/OFF/UNKNOWN. Anything else stays as nil on
14
+ # severity_level but is preserved verbatim in raw_severity_level.
15
+ KNOWN_SEVERITIES = %w[WARNING INFORMATION OFF UNKNOWN].freeze
16
+ KNOWN_CATEGORIES = %w[HINT UNRECOGNIZED UNSUPPORTED PERFORMANCE
17
+ DEPRECATION GENERIC SECURITY TOPOLOGY SCHEMA].freeze
18
+
19
+ def initialize(data)
20
+ @code = data[:code]
21
+ @title = data[:title]
22
+ @description = data[:description]
23
+ # Old wire format used :severity, newer uses :severityLevel.
24
+ @raw_severity_level = data[:severityLevel] || data[:severity]
25
+ @severity_level = KNOWN_SEVERITIES.include?(@raw_severity_level) ? @raw_severity_level : 'UNKNOWN'
26
+ @severity = @raw_severity_level
27
+ @raw_category = data[:category]
28
+ @category = KNOWN_CATEGORIES.include?(@raw_category) ? @raw_category : 'UNKNOWN'
29
+ @position = data[:position] && Position.new(data[:position])
30
+ end
31
+
32
+ # Inner type — character offset / line / column where the
33
+ # notification applies in the query text.
34
+ class Position
35
+ attr_reader :offset, :line, :column
36
+
37
+ def initialize(position_data)
38
+ @offset = position_data[:offset]
39
+ @line = position_data[:line]
40
+ @column = position_data[:column]
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Summary
6
+ # Query execution plan tree. Mirrors org.neo4j.driver.summary.Plan.
7
+ class Plan
8
+ attr_reader :operator_type, :identifiers, :arguments, :children
9
+
10
+ def initialize(plan_data)
11
+ @operator_type = plan_data[:operatorType]
12
+ @identifiers = plan_data[:identifiers] || []
13
+ @arguments = plan_data[:args] || {}
14
+ @children = (plan_data[:children] || []).map { |child| self.class.new(child) }
15
+ end
16
+
17
+ # Alias args → arguments to match JRuby's InternalPlan#args helper,
18
+ # so cross-flavour callers can use either name.
19
+ alias args arguments
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Summary
6
+ # Plan plus per-step execution stats. Mirrors the driver-6.2 public
7
+ # QueryProfile, whose getters are all optional: an absent stat stays
8
+ # nil (distinguishable from a real 0) rather than collapsing to 0, so
9
+ # the backend can omit it — Feature:API:Summary:Profile:OptionalStats.
10
+ # The JRuby flavor gets the same nil-vs-0 distinction from
11
+ # Ext::Internal::Summary::InternalQueryProfile.
12
+ class Profile < Plan
13
+ attr_reader :db_hits, :records, :page_cache_hits, :page_cache_misses,
14
+ :page_cache_hit_ratio, :time
15
+
16
+ def initialize(profile_data)
17
+ super
18
+ @db_hits = profile_data[:dbHits]
19
+ @records = profile_data[:rows] || profile_data[:records]
20
+ @page_cache_hits = profile_data[:pageCacheHits]
21
+ @page_cache_misses = profile_data[:pageCacheMisses]
22
+ @page_cache_hit_ratio = profile_data[:pageCacheHitRatio]
23
+ @time = profile_data[:time]
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Summary
6
+ # The query (text + parameters) the summary describes. Mirrors
7
+ # org.neo4j.driver.Query but lives under Summary:: for the same
8
+ # reason as the rest — the user reaches it via Summary#query.
9
+ class Query
10
+ attr_reader :text, :parameters
11
+
12
+ def initialize(text, parameters = {})
13
+ @text = text
14
+ @parameters = parameters
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,10 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Neo4j
2
4
  module Driver
3
5
  module Summary
6
+ # Mirrors org.neo4j.driver.summary.QueryType — coarse classification
7
+ # of what a query did (read, write, both, or schema mutation).
4
8
  module QueryType
5
9
  READ_ONLY = 'r'
6
- READ_WRITE = 'rw'
7
10
  WRITE_ONLY = 'w'
11
+ READ_WRITE = 'rw'
8
12
  SCHEMA_WRITE = 's'
9
13
  end
10
14
  end
@@ -0,0 +1,215 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Summary
6
+ # Summary of query execution. Mirrors Java's
7
+ # org.neo4j.driver.summary.ResultSummary — same name and place in
8
+ # the namespace; no public `metadata` accessor, no Bolt-wire-format
9
+ # leakage.
10
+ class ResultSummary
11
+ def initialize(metadata, query_text = nil, parameters = {}, connection = nil, had_record: false)
12
+ @metadata = metadata
13
+ @query_text = query_text
14
+ @parameters = parameters
15
+ @connection = connection
16
+ @had_record = had_record
17
+ end
18
+
19
+ def query
20
+ Query.new(@query_text, @parameters)
21
+ end
22
+
23
+ # Mirrors Java's ResultSummary#queryType — returns nil only when the
24
+ # server omitted the :type field (testkit relies on this). A field
25
+ # that is present but holds anything other than a known code — an
26
+ # unrecognised string, or an explicit null — is a protocol
27
+ # violation, so we raise (Java's extractQueryType chokes on those
28
+ # too) rather than silently returning nil.
29
+ def query_type
30
+ return nil unless @metadata.key?(:type)
31
+
32
+ case @metadata[:type]
33
+ when 'r' then QueryType::READ_ONLY
34
+ when 'w' then QueryType::WRITE_ONLY
35
+ when 'rw' then QueryType::READ_WRITE
36
+ when 's' then QueryType::SCHEMA_WRITE
37
+ else
38
+ raise Exceptions::ProtocolException, "Unexpected query type: #{@metadata[:type].inspect}"
39
+ end
40
+ end
41
+
42
+ def counters
43
+ @counters ||= SummaryCounters.new(@metadata[:stats] || {})
44
+ end
45
+
46
+ def server
47
+ @server_info ||=
48
+ if @metadata[:server]
49
+ ServerInfo.new(@metadata[:server])
50
+ elsif @connection
51
+ ServerInfo.new(
52
+ address: @connection.address,
53
+ agent: @connection.server_agent,
54
+ protocol_version: @connection.protocol&.version
55
+ )
56
+ else
57
+ ServerInfo.new(nil)
58
+ end
59
+ end
60
+
61
+ def database
62
+ @database_info ||= DatabaseInfo.new(@metadata[:db])
63
+ end
64
+
65
+ # Time until results are available (milliseconds). 't_first' on the wire.
66
+ def result_available_after
67
+ @metadata[:t_first]
68
+ end
69
+
70
+ # Time to consume results (milliseconds). 't_last' on the wire.
71
+ def result_consumed_after
72
+ @metadata[:t_last]
73
+ end
74
+
75
+ def plan
76
+ return @plan if defined?(@plan)
77
+
78
+ @plan =
79
+ if @metadata[:plan]
80
+ Plan.new(@metadata[:plan])
81
+ elsif @metadata[:profile]
82
+ # A profiled query exposes both: `plan` is the plan-only view
83
+ # (no stats), `profile` adds them. Distinct objects, matching
84
+ # the Java driver 6.2 model.
85
+ Plan.new(@metadata[:profile])
86
+ end
87
+ end
88
+
89
+ def profile
90
+ return @profile if defined?(@profile)
91
+
92
+ @profile = @metadata[:profile] ? Profile.new(@metadata[:profile]) : nil
93
+ end
94
+
95
+ # MRI has a single profile concept; the modern (JRuby 6.2) query_profile
96
+ # accessor maps to it. MRI doesn't advertise OptionalStats, so absent
97
+ # stats still default to 0 (testkit uses its strict-driver path).
98
+ def query_profile = profile
99
+
100
+ def has_plan?
101
+ !@metadata[:plan].nil? || !@metadata[:profile].nil?
102
+ end
103
+
104
+ def has_profile?
105
+ !@metadata[:profile].nil?
106
+ end
107
+
108
+ # Bolt 5.5+ servers report query info via `statuses` (GQL status
109
+ # objects) rather than the legacy `notifications` list. Statuses
110
+ # without a `neo4j_code` are pure-GQL info (e.g. "successful
111
+ # completion") and have no legacy-notification equivalent, so we
112
+ # drop them; the rest are reshaped to look like `notifications`.
113
+ def notifications
114
+ (@metadata[:notifications] || statuses_as_notifications)
115
+ .map { |n| Notification.new(n) }
116
+ end
117
+
118
+ # GQL status objects. Bolt 5.6+ servers report them natively in the
119
+ # summary `statuses` list, which we preserve verbatim (server order —
120
+ # the driver must not reorder them). Older servers (or any SUCCESS
121
+ # without `statuses`) don't, so we synthesise the list from the legacy
122
+ # `notifications` plus a mandatory outcome status — the pre-5.6
123
+ # backfill. A status carrying a `neo4j_code` is also a notification
124
+ # (GqlNotification); the rest are plain status objects.
125
+ def gql_status_objects
126
+ (@metadata[:statuses] || polyfilled_statuses).map do |status|
127
+ (status[:neo4j_code] ? GqlNotification : GqlStatusObject).new(status)
128
+ end
129
+ end
130
+
131
+ private
132
+
133
+ # Synthesise `statuses` from the legacy `notifications` list plus a
134
+ # single outcome status, mirroring the Java driver's MetadataExtractor.
135
+ # The whole set is ordered by GQL status class (02 no-data, 01 warning,
136
+ # 00 success, 03 info, then everything else); the sort is stable so
137
+ # same-class notifications keep their server order.
138
+ def polyfilled_statuses
139
+ statuses = (@metadata[:notifications] || []).map { notification_as_status(it) }
140
+ statuses << outcome_status
141
+ statuses.each_with_index.sort_by { |status, i| [gql_status_class(status), i] }.map(&:first)
142
+ end
143
+
144
+ # The mandatory outcome status. The driver reached the summary having
145
+ # streamed the whole result (eager pull), so it knows whether a record
146
+ # arrived: yes -> successful completion; no -> "no data", or "omitted
147
+ # result" when the query produced no fields at all (e.g. a write).
148
+ def outcome_status
149
+ if @had_record
150
+ { gql_status: '00000', status_description: 'note: successful completion' }
151
+ elsif Array(@metadata[:fields]).empty?
152
+ { gql_status: '00001', status_description: 'note: successful completion - omitted result' }
153
+ else
154
+ { gql_status: '02000', status_description: 'note: no data' }
155
+ end
156
+ end
157
+
158
+ # Reshape one legacy notification into a `statuses`-list entry so it
159
+ # flows through GqlNotification unchanged. WARNING -> 01N42, everything
160
+ # else -> 03N42; the notification facets move into the diagnostic
161
+ # record under `_severity` / `_classification` / `_position`.
162
+ def notification_as_status(notification)
163
+ # Legacy notifications carry severity under :severity — the only key
164
+ # the Java driver's MetadataExtractor reads here; :severityLevel never
165
+ # appears on this wire structure.
166
+ severity = notification[:severity]
167
+ warning = severity == 'WARNING'
168
+ description = notification[:description] unless notification[:description] == 'null'
169
+ {
170
+ gql_status: warning ? '01N42' : '03N42',
171
+ status_description: description || (warning ? 'warn: unknown warning' : 'info: unknown notification'),
172
+ neo4j_code: notification[:code],
173
+ title: notification[:title],
174
+ diagnostic_record: { _severity: severity, _classification: notification[:category],
175
+ _position: notification[:position] }.compact
176
+ }
177
+ end
178
+
179
+ # Sort key from the GQL status class (first two chars of the code).
180
+ def gql_status_class(status)
181
+ case status[:gql_status]
182
+ when /\A02/ then 0
183
+ when /\A01/ then 1
184
+ when /\A00/ then 2
185
+ when /\A03/ then 3
186
+ else 4
187
+ end
188
+ end
189
+
190
+ def statuses_as_notifications
191
+ (@metadata[:statuses] || []).filter_map do |s|
192
+ next unless s[:neo4j_code]
193
+
194
+ dr = s[:diagnostic_record] || {}
195
+ {
196
+ code: s[:neo4j_code],
197
+ title: s[:title],
198
+ description: s[:description],
199
+ severity: dr[:_severity],
200
+ category: dr[:_classification],
201
+ position: dr[:_position]
202
+ }.compact
203
+ end
204
+ end
205
+
206
+ # Internal-only accessor. NOT part of the public API — Java's
207
+ # ResultSummary doesn't expose the raw wire metadata either. The
208
+ # one in-tree consumer is Session#harvest_auto_commit_bookmark
209
+ # (which reaches in via send) because the auto-commit bookmark
210
+ # only ever lives on the wire SUCCESS metadata.
211
+ attr_reader :metadata
212
+ end
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Summary
6
+ # Server metadata reported in the SUCCESS response.
7
+ class ServerInfo
8
+ attr_reader :address, :agent, :protocol_version
9
+
10
+ def initialize(server_data = nil, address: nil, agent: nil, protocol_version: nil)
11
+ case server_data
12
+ when String
13
+ @agent = server_data
14
+ @address = address
15
+ @protocol_version = protocol_version
16
+ when Hash
17
+ @address = server_data[:address] || address
18
+ @agent = server_data[:agent] || agent
19
+ @protocol_version = server_data[:protocol_version] || protocol_version
20
+ else
21
+ @address = address
22
+ @agent = agent
23
+ @protocol_version = protocol_version
24
+ end
25
+ end
26
+
27
+ def to_s
28
+ @agent || 'Unknown'
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Summary
6
+ # Per-operation counts produced by the query.
7
+ # Mirrors org.neo4j.driver.summary.SummaryCounters.
8
+ class SummaryCounters
9
+ attr_reader :nodes_created, :nodes_deleted,
10
+ :relationships_created, :relationships_deleted,
11
+ :properties_set,
12
+ :labels_added, :labels_removed,
13
+ :indexes_added, :indexes_removed,
14
+ :constraints_added, :constraints_removed,
15
+ :system_updates
16
+
17
+ # Mapping from server metadata keys (kebab-case strings sent on the
18
+ # wire as Symbols) to Ruby attribute names.
19
+ COUNTER_KEYS = {
20
+ 'nodes-created': :nodes_created,
21
+ 'nodes-deleted': :nodes_deleted,
22
+ 'relationships-created': :relationships_created,
23
+ 'relationships-deleted': :relationships_deleted,
24
+ 'properties-set': :properties_set,
25
+ 'labels-added': :labels_added,
26
+ 'labels-removed': :labels_removed,
27
+ 'indexes-added': :indexes_added,
28
+ 'indexes-removed': :indexes_removed,
29
+ 'constraints-added': :constraints_added,
30
+ 'constraints-removed': :constraints_removed,
31
+ 'system-updates': :system_updates
32
+ }.freeze
33
+
34
+ def initialize(stats)
35
+ # Initialize all counters to 0 — server only reports nonzero ones.
36
+ COUNTER_KEYS.each_value { |attr| instance_variable_set("@#{attr}", 0) }
37
+
38
+ stats.each do |key, value|
39
+ attr_name = COUNTER_KEYS[key]
40
+ instance_variable_set("@#{attr_name}", value.to_i) if attr_name
41
+ end
42
+ end
43
+
44
+ def contains_updates?
45
+ nodes_created.positive? ||
46
+ nodes_deleted.positive? ||
47
+ relationships_created.positive? ||
48
+ relationships_deleted.positive? ||
49
+ properties_set.positive? ||
50
+ labels_added.positive? ||
51
+ labels_removed.positive? ||
52
+ indexes_added.positive? ||
53
+ indexes_removed.positive? ||
54
+ constraints_added.positive? ||
55
+ constraints_removed.positive?
56
+ end
57
+
58
+ def contains_system_updates?
59
+ system_updates.positive?
60
+ end
61
+
62
+ def to_h
63
+ COUNTER_KEYS.values.to_h { |attr| [attr, instance_variable_get("@#{attr}")] }
64
+ end
65
+
66
+ def to_s
67
+ to_h.select { |_, v| v.positive? }.map { |k, v| "#{k}: #{v}" }.join(', ')
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end