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.
-
.load(profile: nil) ⇒ void
Load a graph fixture profile.
-
.validate_relationships(relationships) ⇒ Object
Validates relationships for cross-DB issues.
Class Method Details
.[](ref) ⇒ Object
Fetch a node by logical ref.
161 162 163 |
# File 'lib/active_cypher/fixtures.rb', line 161 def self.[](ref) Registry[ref] end |
.clear_all ⇒ void
This method returns an undefined value.
Clear all nodes in all known connections.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/active_cypher/fixtures.rb', line 66 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 connections.each do |conn| conn.execute_cypher('MATCH (n) DETACH DELETE n') rescue StandardError => e warn "[ActiveCypher::Fixtures.clear_all] Failed to clear connection #{conn.inspect}: #{e.class}: #{e.}" end true 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 59 60 61 62 |
# 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 connections.each do |conn| conn.execute_cypher('MATCH (n) DETACH DELETE n') rescue StandardError => e warn "[ActiveCypher::Fixtures.load] Failed to clear connection #{conn.inspect}: #{e.class}: #{e.}" end # 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
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 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 154 155 156 |
# File 'lib/active_cypher/fixtures.rb', line 91 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? conn = klass.connection # Store connection details for comparison model_connections[klass] = { adapter: conn.class.name, config: conn.instance_variable_get(:@config), object_id: conn.object_id } 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 from_conn_details = model_connections[from_class] to_conn_details = model_connections[to_class] # If either class isn't in our mapping, refresh it unless from_conn_details conn = from_class.connection from_conn_details = { adapter: conn.class.name, config: conn.instance_variable_get(:@config), object_id: conn.object_id } model_connections[from_class] = from_conn_details end unless to_conn_details conn = to_class.connection to_conn_details = { adapter: conn.class.name, config: conn.instance_variable_get(:@config), object_id: conn.object_id } model_connections[to_class] = to_conn_details end # 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 |