activecypher 0.0.0 → 0.2.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.
- checksums.yaml +4 -4
- data/lib/active_cypher/associations/collection_proxy.rb +144 -0
- data/lib/active_cypher/associations.rb +537 -0
- data/lib/active_cypher/base.rb +47 -0
- data/lib/active_cypher/bolt/connection.rb +525 -0
- data/lib/active_cypher/bolt/driver.rb +144 -0
- data/lib/active_cypher/bolt/handlers.rb +10 -0
- data/lib/active_cypher/bolt/message_reader.rb +100 -0
- data/lib/active_cypher/bolt/message_writer.rb +53 -0
- data/lib/active_cypher/bolt/messaging.rb +307 -0
- data/lib/active_cypher/bolt/packstream.rb +319 -0
- data/lib/active_cypher/bolt/result.rb +82 -0
- data/lib/active_cypher/bolt/session.rb +201 -0
- data/lib/active_cypher/bolt/transaction.rb +211 -0
- data/lib/active_cypher/bolt/version_encoding.rb +41 -0
- data/lib/active_cypher/bolt.rb +7 -0
- data/lib/active_cypher/connection_adapters/abstract_adapter.rb +75 -0
- data/lib/active_cypher/connection_adapters/abstract_bolt_adapter.rb +178 -0
- data/lib/active_cypher/connection_adapters/memgraph_adapter.rb +44 -0
- data/lib/active_cypher/connection_adapters/neo4j_adapter.rb +58 -0
- data/lib/active_cypher/connection_factory.rb +130 -0
- data/lib/active_cypher/connection_handler.rb +9 -0
- data/lib/active_cypher/connection_pool.rb +123 -0
- data/lib/active_cypher/connection_url_resolver.rb +137 -0
- data/lib/active_cypher/cypher_config.rb +50 -0
- data/lib/active_cypher/generators/install_generator.rb +23 -0
- data/lib/active_cypher/generators/node_generator.rb +32 -0
- data/lib/active_cypher/generators/relationship_generator.rb +33 -0
- data/lib/active_cypher/generators/templates/application_graph_node.rb +5 -0
- data/lib/active_cypher/generators/templates/application_graph_relationship.rb +5 -0
- data/lib/active_cypher/generators/templates/cypher_databases.yml +28 -0
- data/lib/active_cypher/generators/templates/node.rb.erb +10 -0
- data/lib/active_cypher/generators/templates/relationship.rb.erb +11 -0
- data/lib/active_cypher/logging.rb +44 -0
- data/lib/active_cypher/model/abstract.rb +87 -0
- data/lib/active_cypher/model/attributes.rb +24 -0
- data/lib/active_cypher/model/callbacks.rb +44 -0
- data/lib/active_cypher/model/connection_handling.rb +76 -0
- data/lib/active_cypher/model/connection_owner.rb +50 -0
- data/lib/active_cypher/model/core.rb +45 -0
- data/lib/active_cypher/model/countable.rb +30 -0
- data/lib/active_cypher/model/destruction.rb +49 -0
- data/lib/active_cypher/model/inspectable.rb +28 -0
- data/lib/active_cypher/model/persistence.rb +182 -0
- data/lib/active_cypher/model/querying.rb +67 -0
- data/lib/active_cypher/railtie.rb +34 -0
- data/lib/active_cypher/relation.rb +190 -0
- data/lib/active_cypher/relationship.rb +233 -0
- data/lib/active_cypher/runtime_registry.rb +8 -0
- data/lib/active_cypher/scoping.rb +97 -0
- data/lib/active_cypher/utils/logger.rb +100 -0
- data/lib/active_cypher/version.rb +5 -0
- data/lib/activecypher.rb +108 -0
- data/lib/cyrel/call_procedure.rb +29 -0
- data/lib/cyrel/clause/call.rb +46 -0
- data/lib/cyrel/clause/call_subquery.rb +40 -0
- data/lib/cyrel/clause/create.rb +33 -0
- data/lib/cyrel/clause/delete.rb +41 -0
- data/lib/cyrel/clause/limit.rb +33 -0
- data/lib/cyrel/clause/match.rb +40 -0
- data/lib/cyrel/clause/merge.rb +34 -0
- data/lib/cyrel/clause/order_by.rb +78 -0
- data/lib/cyrel/clause/remove.rb +75 -0
- data/lib/cyrel/clause/return.rb +90 -0
- data/lib/cyrel/clause/set.rb +97 -0
- data/lib/cyrel/clause/skip.rb +34 -0
- data/lib/cyrel/clause/where.rb +42 -0
- data/lib/cyrel/clause/with.rb +94 -0
- data/lib/cyrel/clause.rb +25 -0
- data/lib/cyrel/direction.rb +18 -0
- data/lib/cyrel/expression/alias.rb +27 -0
- data/lib/cyrel/expression/base.rb +101 -0
- data/lib/cyrel/expression/case.rb +45 -0
- data/lib/cyrel/expression/comparison.rb +60 -0
- data/lib/cyrel/expression/exists.rb +42 -0
- data/lib/cyrel/expression/function_call.rb +57 -0
- data/lib/cyrel/expression/literal.rb +33 -0
- data/lib/cyrel/expression/logical.rb +38 -0
- data/lib/cyrel/expression/operator.rb +27 -0
- data/lib/cyrel/expression/pattern_comprehension.rb +44 -0
- data/lib/cyrel/expression/property_access.rb +25 -0
- data/lib/cyrel/expression.rb +56 -0
- data/lib/cyrel/functions.rb +116 -0
- data/lib/cyrel/node.rb +397 -0
- data/lib/cyrel/parameterizable.rb +20 -0
- data/lib/cyrel/pattern/node.rb +66 -0
- data/lib/cyrel/pattern/path.rb +41 -0
- data/lib/cyrel/pattern/relationship.rb +74 -0
- data/lib/cyrel/pattern.rb +8 -0
- data/lib/cyrel/query.rb +497 -0
- data/lib/cyrel/return_only.rb +26 -0
- data/lib/cyrel/types/hash_type.rb +22 -0
- data/lib/cyrel/types/symbol_type.rb +13 -0
- data/lib/cyrel.rb +72 -0
- data/lib/tasks/active_cypher_tasks.rake +6 -0
- data/sig/activecypher.rbs +4 -0
- metadata +173 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b78e44e773d760db977af2feadbb8da2feae14d361e530a9ce0c300b3488316e
|
4
|
+
data.tar.gz: 867b1beeed8d8f2a42d49b61c33be85fbc97e431b1a5d212ffdd676b9fc5cde3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ab032c06695810f847c679dcadddec8af51c4c4a6c53425f04561cf92f2d558876b775cd40d535e40c66c654e33474752d7438eeb91fa5d2f3450b1fe46fd53
|
7
|
+
data.tar.gz: 14120d94c9f0fb83765631ab06efe2da72cf2939ea6d24a2fade76911384b4df0cc992f828aa55e10326730e3388b2703746647dcfc577d49c9245be70dfeedd
|
@@ -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
|