Module: ActiveCypher::Fixtures
- Defined in:
- lib/active_cypher/fixtures.rb,
lib/active_cypher/fixtures/parser.rb,
lib/active_cypher/fixtures/registry.rb,
lib/active_cypher/fixtures/evaluator.rb,
lib/active_cypher/fixtures/dsl_context.rb,
lib/active_cypher/fixtures/rel_builder.rb,
lib/active_cypher/fixtures/node_builder.rb
Defined Under Namespace
Classes: DSLContext, Evaluator, FixtureError, FixtureNotFoundError, NodeBuilder, Parser, Registry, RelBuilder
Class Method Summary collapse
-
.[](ref) ⇒ Object
Fetch a node by logical ref.
-
.clear_all ⇒ void
Clear all nodes in all known connections.
-
.conn_details_for(klass, cache) ⇒ Hash
Memoized connection-details lookup for a model class.
-
.connection_details(conn) ⇒ Hash
Build a comparable connection fingerprint for cross-database detection.
-
.load(profile: nil) ⇒ void
Load a graph fixture profile.
-
.validate_relationships(relationships) ⇒ Object
Validates relationships for cross-DB issues.
-
.wipe_connections(connections, context) ⇒ void
Detach-delete every node across the given connections, logging per-connection failures.
Class Method Details
.[](ref) ⇒ Object
Fetch a node by logical ref.
158 159 160 |
# File 'lib/active_cypher/fixtures.rb', line 158 def self.[](ref) Registry[ref] end |
.clear_all ⇒ void
This method returns an undefined value.
Clear all nodes in all known connections.
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/active_cypher/fixtures.rb', line 62 def self.clear_all # Find all concrete (non-abstract) model classes inheriting from ActiveCypher::Base model_classes = [] ObjectSpace.each_object(Class) do |klass| next unless klass < ActiveCypher::Base next if klass.respond_to?(:abstract_class?) && klass.abstract_class? model_classes << klass end # Gather unique connections from all model classes connections = model_classes.map(&:connection).compact.uniq # Wipe all nodes in each connection wipe_connections(connections, 'clear_all') true end |
.conn_details_for(klass, cache) ⇒ Hash
Memoized connection-details lookup for a model class.
95 96 97 |
# File 'lib/active_cypher/fixtures.rb', line 95 def self.conn_details_for(klass, cache) cache[klass] ||= connection_details(klass.connection) end |
.connection_details(conn) ⇒ Hash
Build a comparable connection fingerprint for cross-database detection.
83 84 85 86 87 88 89 |
# File 'lib/active_cypher/fixtures.rb', line 83 def self.connection_details(conn) { adapter: conn.class.name, config: conn.instance_variable_get(:@config), object_id: conn.object_id } end |
.load(profile: nil) ⇒ void
This method returns an undefined value.
Load a graph fixture profile.
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/active_cypher/fixtures.rb', line 16 def self.load(profile: nil) # 1. Resolve file profile_name = (profile || :default).to_s fixtures_dir = File.('test/fixtures/graph', Dir.pwd) file = File.join(fixtures_dir, "#{profile_name}.rb") raise FixtureNotFoundError, "Fixture profile not found: #{profile_name} (#{file})" unless File.exist?(file) # 2. Reset registry Registry.reset! # 3. Parse the profile file (to discover which models are referenced) parser = Parser.new(file) dsl_context = parser.parse # 4. Validate relationships upfront (cross-DB) validate_relationships(dsl_context.relationships) # 5. Gather unique connections for all model classes referenced in this profile model_classes = dsl_context.nodes.map { |node| node[:model_class] }.uniq connections = model_classes.map(&:connection).compact.uniq # 6. Wipe all nodes in each relevant connection wipe_connections(connections, 'load') # 7. Evaluate nodes and relationships (batched if large) if dsl_context.nodes.size > 100 || dsl_context.relationships.size > 200 NodeBuilder.bulk_build(dsl_context.nodes) # Create all nodes first, then validate relationships again with populated Registry validate_relationships(dsl_context.relationships) RelBuilder.bulk_build(dsl_context.relationships) else dsl_context.nodes.each do |node| NodeBuilder.build(node[:ref], node[:model_class], node[:props]) end rel_builder = RelBuilder.new dsl_context.relationships.each do |rel| rel_builder.build(rel[:ref], rel[:from_ref], rel[:type], rel[:to_ref], rel[:props]) end end # 8. Return registry for convenience Registry end |
.validate_relationships(relationships) ⇒ Object
Validates relationships for cross-DB issues
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/active_cypher/fixtures.rb', line 114 def self.validate_relationships(relationships) model_connections = {} # First build a mapping of model class => connection details ObjectSpace.each_object(Class) do |klass| next unless klass < ActiveCypher::Base next if klass.respond_to?(:abstract_class?) && klass.abstract_class? # Store connection details for comparison model_connections[klass] = connection_details(klass.connection) end relationships.each do |rel| from_ref = rel[:from_ref] to_ref = rel[:to_ref] # Get node classes from DSL context # In real data, nodes have already been created by this point from_node = Registry.get(from_ref) to_node = Registry.get(to_ref) # Skip if we can't find both nodes yet (will be caught later) next unless from_node && to_node from_class = from_node.class to_class = to_node.class # Look up connection details for each class, refreshing the cache on miss from_conn_details = conn_details_for(from_class, model_connections) to_conn_details = conn_details_for(to_class, model_connections) # Compare connection details next unless from_conn_details[:object_id] != to_conn_details[:object_id] || from_conn_details[:adapter] != to_conn_details[:adapter] || from_conn_details[:config][:database] != to_conn_details[:config][:database] raise FixtureError, 'Cross-database relationship? Sorry, your data has commitment issues. ' \ "Nodes #{from_ref} (#{from_class}) and #{to_ref} (#{to_class}) use different databases." end end |
.wipe_connections(connections, context) ⇒ void
This method returns an undefined value.
Detach-delete every node across the given connections, logging per-connection failures.
103 104 105 106 107 108 109 |
# File 'lib/active_cypher/fixtures.rb', line 103 def self.wipe_connections(connections, context) connections.each do |conn| conn.execute_cypher('MATCH (n) DETACH DELETE n') rescue StandardError => e warn "[ActiveCypher::Fixtures.#{context}] Failed to clear connection #{conn.inspect}: #{e.class}: #{e.}" end end |