activecypher 0.0.0 → 0.3.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.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/lib/active_cypher/associations/collection_proxy.rb +144 -0
  3. data/lib/active_cypher/associations.rb +537 -0
  4. data/lib/active_cypher/base.rb +47 -0
  5. data/lib/active_cypher/bolt/connection.rb +525 -0
  6. data/lib/active_cypher/bolt/driver.rb +144 -0
  7. data/lib/active_cypher/bolt/handlers.rb +10 -0
  8. data/lib/active_cypher/bolt/message_reader.rb +100 -0
  9. data/lib/active_cypher/bolt/message_writer.rb +53 -0
  10. data/lib/active_cypher/bolt/messaging.rb +307 -0
  11. data/lib/active_cypher/bolt/packstream.rb +319 -0
  12. data/lib/active_cypher/bolt/result.rb +82 -0
  13. data/lib/active_cypher/bolt/session.rb +201 -0
  14. data/lib/active_cypher/bolt/transaction.rb +211 -0
  15. data/lib/active_cypher/bolt/version_encoding.rb +41 -0
  16. data/lib/active_cypher/bolt.rb +7 -0
  17. data/lib/active_cypher/connection_adapters/abstract_adapter.rb +75 -0
  18. data/lib/active_cypher/connection_adapters/abstract_bolt_adapter.rb +178 -0
  19. data/lib/active_cypher/connection_adapters/memgraph_adapter.rb +44 -0
  20. data/lib/active_cypher/connection_adapters/neo4j_adapter.rb +58 -0
  21. data/lib/active_cypher/connection_factory.rb +130 -0
  22. data/lib/active_cypher/connection_handler.rb +9 -0
  23. data/lib/active_cypher/connection_pool.rb +123 -0
  24. data/lib/active_cypher/connection_url_resolver.rb +137 -0
  25. data/lib/active_cypher/cypher_config.rb +61 -0
  26. data/lib/active_cypher/generators/install_generator.rb +23 -0
  27. data/lib/active_cypher/generators/node_generator.rb +32 -0
  28. data/lib/active_cypher/generators/relationship_generator.rb +33 -0
  29. data/lib/active_cypher/generators/templates/application_graph_node.rb +5 -0
  30. data/lib/active_cypher/generators/templates/application_graph_relationship.rb +5 -0
  31. data/lib/active_cypher/generators/templates/cypher_databases.yml +16 -0
  32. data/lib/active_cypher/generators/templates/node.rb.erb +10 -0
  33. data/lib/active_cypher/generators/templates/relationship.rb.erb +11 -0
  34. data/lib/active_cypher/logging.rb +44 -0
  35. data/lib/active_cypher/model/abstract.rb +87 -0
  36. data/lib/active_cypher/model/attributes.rb +24 -0
  37. data/lib/active_cypher/model/callbacks.rb +44 -0
  38. data/lib/active_cypher/model/connection_handling.rb +76 -0
  39. data/lib/active_cypher/model/connection_owner.rb +50 -0
  40. data/lib/active_cypher/model/core.rb +45 -0
  41. data/lib/active_cypher/model/countable.rb +30 -0
  42. data/lib/active_cypher/model/destruction.rb +49 -0
  43. data/lib/active_cypher/model/inspectable.rb +28 -0
  44. data/lib/active_cypher/model/persistence.rb +182 -0
  45. data/lib/active_cypher/model/querying.rb +67 -0
  46. data/lib/active_cypher/railtie.rb +34 -0
  47. data/lib/active_cypher/relation.rb +190 -0
  48. data/lib/active_cypher/relationship.rb +233 -0
  49. data/lib/active_cypher/runtime_registry.rb +8 -0
  50. data/lib/active_cypher/scoping.rb +97 -0
  51. data/lib/active_cypher/utils/logger.rb +100 -0
  52. data/lib/active_cypher/version.rb +5 -0
  53. data/lib/activecypher.rb +108 -0
  54. data/lib/cyrel/call_procedure.rb +29 -0
  55. data/lib/cyrel/clause/call.rb +46 -0
  56. data/lib/cyrel/clause/call_subquery.rb +40 -0
  57. data/lib/cyrel/clause/create.rb +33 -0
  58. data/lib/cyrel/clause/delete.rb +41 -0
  59. data/lib/cyrel/clause/limit.rb +33 -0
  60. data/lib/cyrel/clause/match.rb +40 -0
  61. data/lib/cyrel/clause/merge.rb +34 -0
  62. data/lib/cyrel/clause/order_by.rb +78 -0
  63. data/lib/cyrel/clause/remove.rb +75 -0
  64. data/lib/cyrel/clause/return.rb +90 -0
  65. data/lib/cyrel/clause/set.rb +97 -0
  66. data/lib/cyrel/clause/skip.rb +34 -0
  67. data/lib/cyrel/clause/where.rb +42 -0
  68. data/lib/cyrel/clause/with.rb +94 -0
  69. data/lib/cyrel/clause.rb +25 -0
  70. data/lib/cyrel/direction.rb +18 -0
  71. data/lib/cyrel/expression/alias.rb +27 -0
  72. data/lib/cyrel/expression/base.rb +101 -0
  73. data/lib/cyrel/expression/case.rb +45 -0
  74. data/lib/cyrel/expression/comparison.rb +60 -0
  75. data/lib/cyrel/expression/exists.rb +42 -0
  76. data/lib/cyrel/expression/function_call.rb +57 -0
  77. data/lib/cyrel/expression/literal.rb +33 -0
  78. data/lib/cyrel/expression/logical.rb +38 -0
  79. data/lib/cyrel/expression/operator.rb +27 -0
  80. data/lib/cyrel/expression/pattern_comprehension.rb +44 -0
  81. data/lib/cyrel/expression/property_access.rb +25 -0
  82. data/lib/cyrel/expression.rb +56 -0
  83. data/lib/cyrel/functions.rb +116 -0
  84. data/lib/cyrel/node.rb +397 -0
  85. data/lib/cyrel/parameterizable.rb +20 -0
  86. data/lib/cyrel/pattern/node.rb +66 -0
  87. data/lib/cyrel/pattern/path.rb +41 -0
  88. data/lib/cyrel/pattern/relationship.rb +74 -0
  89. data/lib/cyrel/pattern.rb +8 -0
  90. data/lib/cyrel/query.rb +497 -0
  91. data/lib/cyrel/return_only.rb +26 -0
  92. data/lib/cyrel/types/hash_type.rb +22 -0
  93. data/lib/cyrel/types/symbol_type.rb +13 -0
  94. data/lib/cyrel.rb +72 -0
  95. data/sig/activecypher.rbs +4 -0
  96. metadata +172 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f0e2c604c5ec3fe484276994955b2d0be34105662c5cfbce0e52c9113a50eea9
4
- data.tar.gz: 5431aa5e0d2b069e262bdc797200ad9366358b041e98cec136568e90e40b9726
3
+ metadata.gz: 56e09b9d8193d2c4c745c1973bb420ba83854cbe0b578d5d37d4e215c96c7687
4
+ data.tar.gz: a53e722376ff97ae59c6fc0735c16eb15b9e2fe89fe45cda6d42c5d45237a97f
5
5
  SHA512:
6
- metadata.gz: 9fbb120bc4ab351bc91df701bea81d6fd50d23ef5e6e3f55f76618648b5c1cb481124a16bf1a8c8a1c7be24bf778250698394df6c159d5a8234a62629220f823
7
- data.tar.gz: 8e9dfe628538982b7971a9f6fb70e2ee6da0a167ccc582b1081e5e013cd3d1b7bd29b7b782d9b72c8d41edf80da25c34f305585e1243d12ef6f7422b709bd948
6
+ metadata.gz: bd26475b4774f41f37b047c558707c7fe5282718c8970f1a24148c3f6ad57186ad7b0823051e06d420d80373605f8d28721b7f934dd64e0110b05050af6af7f9
7
+ data.tar.gz: 20b688e0788fe8dd84a51471ee2125d8ce8082304d4e9db724d4970b0e27da638fd4e541030982b582716d5ad1c1f86d1e88c3fbb3605437bab05c96a5e2b832
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveCypher
4
+ module Associations
5
+ # @!visibility public
6
+ # CollectionProxy wraps association collections, providing lazy loading and mutation helpers.
7
+ # Because what’s one more layer between you and your data?
8
+ class CollectionProxy < Relation
9
+ attr_reader :owner, :reflection
10
+
11
+ # ------------------------------------------------------------
12
+ # construction
13
+ # ------------------------------------------------------------
14
+
15
+ # Initializes the proxy, because direct access would be too easy.
16
+ #
17
+ # @param owner [Object] The owning record
18
+ # @param reflection [Hash] The association reflection
19
+ # @param base_relation [Relation] The base relation for the association
20
+ def initialize(owner, reflection, base_relation)
21
+ super(reflection[:class_name].constantize, base_relation.cyrel_query)
22
+
23
+ @owner = owner
24
+ @reflection = reflection
25
+ @records = nil # lazy – load on first enumeration
26
+ end
27
+
28
+ # ------------------------------------------------------------
29
+ # enumeration helpers
30
+ # ------------------------------------------------------------
31
+
32
+ # Iterates over the records in the association.
33
+ # Because Rubyists love pretending their database is just an array.
34
+ #
35
+ # @yield [record] Yields each record in the collection
36
+ def each(&)
37
+ load_target unless @records
38
+ @records.each(&)
39
+ end
40
+ alias to_a each
41
+
42
+ # Returns the size, because counting things is the only certainty in life.
43
+ #
44
+ # @return [Integer] The number of records in the collection
45
+ def size = load_target.size
46
+ alias length size
47
+
48
+ # Fully refresh from the database.
49
+ # For when you want to relive the disappointment of your data changing.
50
+ #
51
+ # @return [self]
52
+ def reload
53
+ @records = nil
54
+ load_target
55
+ self
56
+ end
57
+
58
+ # ------------------------------------------------------------
59
+ # Mutation helpers ( <<, build, create … )
60
+ # ------------------------------------------------------------
61
+
62
+ #
63
+ # hobby.people << bob
64
+ #
65
+ # * persists the edge (via the relationship model if supplied)
66
+ # * updates the in‑memory collection, so `include?` etc. work
67
+ #
68
+ # Because shoveling objects into a collection is the pinnacle of ORM magic.
69
+ #
70
+ # @param records [Array<Object>] Records to add
71
+ # @return [self]
72
+ def <<(*records)
73
+ unless owner.persisted?
74
+ raise ActiveCypher::PersistenceError,
75
+ 'Cannot modify associations on a new record'
76
+ end
77
+
78
+ records.flatten.each { |rec| add_record(rec) }
79
+ self
80
+ end
81
+
82
+ # Convenient `ids` reader ─ used by a few specs.
83
+ # Because sometimes you just want the IDs and none of the commitment.
84
+ #
85
+ # @return [Array<String>] The internal IDs of the associated records
86
+ def ids
87
+ map(&:internal_id)
88
+ end
89
+
90
+ private
91
+
92
+ # ------------------------------------------------------------------
93
+ # helpers
94
+ # ------------------------------------------------------------------
95
+
96
+ # Loads the target records, because lazy loading is the only exercise this code gets.
97
+ #
98
+ # @return [Array<Object>] The loaded records
99
+ def load_target
100
+ @records = load_records
101
+ end
102
+
103
+ # Adds a record to the association, with all the ceremony of a royal wedding.
104
+ #
105
+ # @param record [Object] The record to add
106
+ # @raise [ArgumentError] if the record is not of the correct class
107
+ # @raise [RuntimeError] if the record is not persisted
108
+ def add_record(record)
109
+ klass = reflection[:class_name].constantize
110
+ raise ArgumentError, "Expected #{klass}, got #{record.class}" unless record.is_a?(klass)
111
+ raise "Associated object #{record.inspect} must be persisted" unless record.persisted?
112
+
113
+ # Persist the edge ------------------------------------------------
114
+ rel_klass = reflection[:relationship_class]&.constantize
115
+ dir = reflection[:direction]
116
+
117
+ from_node, to_node =
118
+ case dir
119
+ when :out then [owner, record]
120
+ when :in then [record, owner]
121
+ when :both then [owner, record]
122
+ else raise ArgumentError, "Direction '#{dir}' not supported"
123
+ end
124
+
125
+ if rel_klass
126
+ rel_klass.create({}, from_node: from_node, to_node: to_node)
127
+ else
128
+ arrow = (dir == :both ? :'--' : :out)
129
+ Cyrel
130
+ .match(Cyrel.node(from_node.class.label_name).as(:a)
131
+ .where(Cyrel.id(:a).eq(from_node.internal_id)))
132
+ .match(Cyrel.node(to_node.class.label_name).as(:b)
133
+ .where(Cyrel.id(:b).eq(to_node.internal_id)))
134
+ .create(Cyrel.node(:a).rel(arrow, reflection[:relationship]).to(:b))
135
+ .tap { |qry| owner.class.connection.execute_cypher(*qry.to_cypher, 'Create Association') }
136
+ end
137
+
138
+ # Keep the in‑memory collection in sync --------------------------
139
+ load_target unless @records # force initial load if needed
140
+ @records << record unless @records.include?(record)
141
+ end
142
+ end
143
+ end
144
+ end