activecypher 0.6.3 → 0.7.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6586ae8148cfb3d3f1e6ce182fc556a36790d84d0559e95455a57e1534afe4b2
4
- data.tar.gz: 634ef2b25e6ba61bb38baf955c30e48fca9c9c6c71e885c5e5a5174d2105a947
3
+ metadata.gz: e5ccd00478564eeb903b76a423867e21973268d6c23af232de7329abe94263d5
4
+ data.tar.gz: a6e74f587ad7c543123840d5f44440efca909a44efa0a59be5b8ce15d8d39f0f
5
5
  SHA512:
6
- metadata.gz: 9ea71245757351c9e2d503078721ee431a1525faa84ea83634ff9d0ed9b2ad486d7001941ef1de608ec86cf0141676bd9a1bbe6ebe1ed215094203a1efe98f01
7
- data.tar.gz: c445c1430312284afd41a91dd8b25478066ce5f93284f249cdedd5256ed62eb02fa09161eda7ff3298d0f33c6a233e858bc75b1f780153ea218096c3ec911036
6
+ metadata.gz: 1b90a82d07ffc1504ddf9d07fcd88a80d3371d32ed09eb8f799c4d228958e090522a44cac2abd6bdf661983fda43ce95b898fe440f8e76c0427621f55ad342a0
7
+ data.tar.gz: bd848d6485fdc502673f88ad3ea3dd776d486a1e11cf501592994efba1347cb799b28af0b6f06e5a29daf3a60c79c39072181fb03d36f0938df5ed52699bd881
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_support/concern'
4
- require 'active_support/core_ext/string/inflections' # for camelize, singularize etc.
5
-
6
3
  module ActiveCypher
7
4
  # Module to handle association definitions (has_many, belongs_to, etc.)
8
5
  # for ActiveCypher models.
@@ -1,11 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_cypher/schema/catalog'
4
+
3
5
  module ActiveCypher
4
6
  module ConnectionAdapters
5
7
  class MemgraphAdapter < AbstractBoltAdapter
6
8
  # Register this adapter with the registry
7
9
  Registry.register('memgraph', self)
8
10
 
11
+ def vendor = :memgraph
12
+
13
+ def schema_catalog
14
+ rows = run('SHOW SCHEMA')
15
+ parse_schema(rows)
16
+ rescue StandardError
17
+ introspect_fallback
18
+ end
19
+
9
20
  # Use id() for Memgraph instead of elementId()
10
21
  ID_FUNCTION = 'id'
11
22
 
@@ -58,6 +69,39 @@ module ActiveCypher
58
69
 
59
70
  protected
60
71
 
72
+ def parse_schema(rows)
73
+ nodes = []
74
+ edges = []
75
+ idx = []
76
+ cons = []
77
+
78
+ rows.each do |row|
79
+ case row['type']
80
+ when 'NODE'
81
+ nodes << Schema::NodeTypeDef.new(row['label'], row['properties'], row['primaryKey'])
82
+ when 'EDGE'
83
+ edges << Schema::EdgeTypeDef.new(row['label'], row['from'], row['to'], row['properties'])
84
+ when 'INDEX'
85
+ idx << Schema::IndexDef.new(row['name'], :node, row['label'], row['properties'], row['unique'], nil)
86
+ when 'CONSTRAINT'
87
+ cons << Schema::ConstraintDef.new(row['name'], row['label'], row['properties'], :unique)
88
+ end
89
+ end
90
+
91
+ Schema::Catalog.new(indexes: idx, constraints: cons, node_types: nodes, edge_types: edges)
92
+ end
93
+
94
+ def introspect_fallback
95
+ labels = run('MATCH (n) RETURN DISTINCT labels(n) AS lbl').flat_map { |r| r['lbl'] }
96
+
97
+ nodes = labels.map do |lbl|
98
+ props = run("MATCH (n:`#{lbl}`) WITH n LIMIT 100 UNWIND keys(n) AS k RETURN DISTINCT k").map { |r| r['k'] }
99
+ Schema::NodeTypeDef.new(lbl, props, nil)
100
+ end
101
+
102
+ Schema::Catalog.new(indexes: [], constraints: [], node_types: nodes, edge_types: [])
103
+ end
104
+
61
105
  def protocol_handler_class = ProtocolHandler
62
106
 
63
107
  def validate_connection
@@ -1,10 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_cypher/schema/catalog'
4
+
3
5
  module ActiveCypher
4
6
  module ConnectionAdapters
5
7
  class Neo4jAdapter < AbstractBoltAdapter
6
8
  Registry.register('neo4j', self)
7
9
 
10
+ def vendor = :neo4j
11
+
12
+ def schema_catalog
13
+ idx_rows = run('SHOW INDEXES')
14
+ con_rows = run('SHOW CONSTRAINTS')
15
+
16
+ idx_defs = idx_rows.map do |r|
17
+ Schema::IndexDef.new(
18
+ r['name'],
19
+ r['entityType'].downcase.to_sym,
20
+ r['labelsOrTypes'].first,
21
+ r['properties'],
22
+ r['uniqueness'] == 'UNIQUE',
23
+ r['type'] == 'VECTOR' ? r['options'] : nil
24
+ )
25
+ end
26
+
27
+ con_defs = con_rows.map do |r|
28
+ Schema::ConstraintDef.new(
29
+ r['name'],
30
+ r['labelsOrTypes'].first,
31
+ r['properties'],
32
+ r['type'].split('_').first.downcase.to_sym
33
+ )
34
+ end
35
+
36
+ Schema::Catalog.new(indexes: idx_defs, constraints: con_defs,
37
+ node_types: [], edge_types: [])
38
+ rescue StandardError
39
+ Schema::Catalog.new(indexes: [], constraints: [], node_types: [], edge_types: [])
40
+ end
41
+
8
42
  # Use elementId() for Neo4j
9
43
  ID_FUNCTION = 'elementId'
10
44
 
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/named_base'
4
+
5
+ module ActiveCypher
6
+ module Generators
7
+ class MigrationGenerator < Rails::Generators::NamedBase
8
+ source_root File.expand_path('templates', __dir__)
9
+
10
+ def create_migration_file
11
+ timestamp = Time.now.utc.strftime('%Y%m%d%H%M%S')
12
+ dir = File.join('graphdb', 'migrate')
13
+ FileUtils.mkdir_p(dir)
14
+ template 'migration.rb.erb', File.join(dir, "#{timestamp}_#{file_name}.rb")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ class <%= name.camelize %> < ActiveCypher::Migration
2
+ up do
3
+ # add operations or incantation here
4
+ end
5
+ end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class <%= class_name %> < ApplicationGraphRelationship
4
- from_class :<%= options[:from] %>
5
- to_class :<%= options[:to] %>
6
- type :<%= relationship_type %>
4
+ from_class '<%= options[:from] %>'
5
+ to_class '<%= options[:to] %>'
6
+ type '<%= relationship_type %>'
7
7
  <% if attributes.any? -%>
8
8
  <% attributes.each do |attr| -%>
9
9
  attribute :<%= attr.name %>, :<%= attr.type || "string" %>
@@ -132,7 +132,7 @@ module ActiveCypher
132
132
  # @param key [String, Symbol] The key to check
133
133
  # @return [Boolean] True if the key contains sensitive information
134
134
  def sensitive_key?(key)
135
- return true if key.to_s.match?(/\b(password|token|secret|credential|key)\b/i)
135
+ return true if key.to_s.match?(/(^|[\-_])(?:password|token|secret|credential|key)($|[\-_])/i)
136
136
 
137
137
  # Check against Rails filter parameters if available
138
138
  if defined?(Rails) && Rails.application
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveCypher
4
+ # Base class for GraphDB migrations.
5
+ # Provides a small DSL for defining index and constraint operations.
6
+ class Migration
7
+ class << self
8
+ attr_reader :up_block
9
+
10
+ # Define the migration steps.
11
+ def up(&block)
12
+ @up_block = block if block_given?
13
+ end
14
+ end
15
+
16
+ attr_reader :connection, :operations
17
+
18
+ def initialize(connection = ActiveCypher::Base.connection)
19
+ @connection = connection
20
+ @operations = []
21
+ end
22
+
23
+ # Execute the migration.
24
+ def run
25
+ instance_eval(&self.class.up_block) if self.class.up_block
26
+ execute_operations
27
+ end
28
+
29
+ # DSL ---------------------------------------------------------------
30
+
31
+ def create_node_index(label, *props, unique: false, if_not_exists: true, name: nil)
32
+ props_clause = props.map { |p| "n.#{p}" }.join(', ')
33
+ cypher = +'CREATE '
34
+ cypher << 'UNIQUE ' if unique
35
+ cypher << 'INDEX'
36
+ cypher << " #{name}" if name
37
+ cypher << ' IF NOT EXISTS' if if_not_exists
38
+ cypher << " FOR (n:#{label}) ON (#{props_clause})"
39
+ operations << cypher
40
+ end
41
+
42
+ def create_rel_index(rel_type, *props, if_not_exists: true, name: nil)
43
+ props_clause = props.map { |p| "r.#{p}" }.join(', ')
44
+ cypher = +'CREATE INDEX'
45
+ cypher << " #{name}" if name
46
+ cypher << ' IF NOT EXISTS' if if_not_exists
47
+ cypher << " FOR ()-[r:#{rel_type}]-() ON (#{props_clause})"
48
+ operations << cypher
49
+ end
50
+
51
+ def create_uniqueness_constraint(label, *props, if_not_exists: true, name: nil)
52
+ props_clause = props.map { |p| "n.#{p}" }.join(', ')
53
+ cypher = +'CREATE CONSTRAINT'
54
+ cypher << " #{name}" if name
55
+ cypher << ' IF NOT EXISTS' if if_not_exists
56
+ cypher << " FOR (n:#{label}) REQUIRE (#{props_clause}) IS UNIQUE"
57
+ operations << cypher
58
+ end
59
+
60
+ def execute(cypher_string)
61
+ operations << cypher_string.strip
62
+ end
63
+
64
+ private
65
+
66
+ def execute_operations
67
+ tx = connection.begin_transaction if connection.respond_to?(:begin_transaction)
68
+ operations.each do |cypher|
69
+ if tx
70
+ tx.run(cypher)
71
+ else
72
+ connection.execute_cypher(cypher)
73
+ end
74
+ end
75
+ connection.commit_transaction(tx) if tx
76
+ rescue StandardError
77
+ connection.rollback_transaction(tx) if tx
78
+ raise
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveCypher
4
+ # Runs pending graph database migrations.
5
+ class Migrator
6
+ MIGRATE_DIR = File.join('graphdb', 'migrate')
7
+
8
+ def initialize(connection = ActiveCypher::Base.connection)
9
+ @connection = connection
10
+ end
11
+
12
+ def migrate!
13
+ ensure_schema_migration_constraint
14
+ applied = existing_versions
15
+
16
+ migration_files.each do |file|
17
+ version = File.basename(file)[0, 14]
18
+ next if applied.include?(version)
19
+
20
+ require file
21
+ class_name = File.basename(file, '.rb').split('_', 2).last.camelize
22
+ klass = Object.const_get(class_name)
23
+ klass.new(@connection).run
24
+
25
+ @connection.execute_cypher(<<~CYPHER)
26
+ CREATE (:SchemaMigration { version: '#{version}', executed_at: datetime() })
27
+ CYPHER
28
+ end
29
+ end
30
+
31
+ def status
32
+ ensure_schema_migration_constraint
33
+ applied = existing_versions
34
+ migration_files.map do |file|
35
+ version = File.basename(file)[0, 14]
36
+ {
37
+ status: (applied.include?(version) ? 'up' : 'down'),
38
+ version: version,
39
+ name: File.basename(file)
40
+ }
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def adapter_dir
47
+ name = @connection.class.name.demodulize.sub('Adapter', '').downcase
48
+ File.join('graphdb', name)
49
+ end
50
+
51
+ def migration_dirs
52
+ dirs = [MIGRATE_DIR, adapter_dir]
53
+ extra = @connection.config[:migrations_paths]
54
+ dirs.concat(Array(extra)) if extra
55
+ dirs
56
+ end
57
+
58
+ def migration_files
59
+ migration_dirs.flat_map do |dir|
60
+ Dir[File.expand_path(File.join(dir, '*.rb'), Dir.pwd)]
61
+ end.sort
62
+ end
63
+
64
+ def existing_versions
65
+ @connection.execute_cypher('MATCH (m:SchemaMigration) RETURN m.version AS version')
66
+ .map { |r| r[:version].to_s }
67
+ rescue StandardError
68
+ []
69
+ end
70
+
71
+ def ensure_schema_migration_constraint
72
+ @connection.execute_cypher(<<~CYPHER)
73
+ CREATE CONSTRAINT graph_schema_migration IF NOT EXISTS
74
+ FOR (m:SchemaMigration)
75
+ REQUIRE m.version IS UNIQUE
76
+ CYPHER
77
+ end
78
+ end
79
+ end
@@ -64,6 +64,12 @@ module ActiveCypher
64
64
  require 'active_cypher/generators/install_generator'
65
65
  require 'active_cypher/generators/node_generator'
66
66
  require 'active_cypher/generators/relationship_generator'
67
+ require 'active_cypher/generators/migration_generator'
68
+ end
69
+
70
+ rake_tasks do
71
+ load File.expand_path('../tasks/graphdb_migrate.rake', __dir__)
72
+ load File.expand_path('../tasks/graphdb_schema.rake', __dir__)
67
73
  end
68
74
  end
69
75
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveCypher
4
+ module Schema
5
+ IndexDef = Data.define(:name, :element, :label, :props, :unique, :vector_opts)
6
+ ConstraintDef = Data.define(:name, :label, :props, :kind)
7
+ NodeTypeDef = Data.define(:label, :props, :primary_key)
8
+ EdgeTypeDef = Data.define(:type, :from, :to, :props)
9
+
10
+ Catalog = Data.define(:indexes, :constraints, :node_types, :edge_types) do
11
+ def empty?
12
+ indexes.empty? && constraints.empty? && node_types.empty? && edge_types.empty?
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'optparse'
5
+
6
+ module ActiveCypher
7
+ module Schema
8
+ # Dumps the graph schema to a Cypher script
9
+ class Dumper
10
+ DEFAULT_PATH = 'graphdb'
11
+
12
+ def initialize(connection = ActiveCypher::Base.connection, base_dir: Dir.pwd)
13
+ @connection = connection
14
+ @base_dir = base_dir
15
+ end
16
+
17
+ def dump_to_string
18
+ cat = @connection.schema_catalog
19
+ cat = catalog_from_migrations if cat.respond_to?(:empty?) && cat.empty?
20
+ Writer::Cypher.new(cat, @connection.vendor).to_s
21
+ end
22
+
23
+ def dump_to_file(path)
24
+ FileUtils.mkdir_p(File.dirname(path))
25
+ File.write(path, dump_to_string)
26
+ path
27
+ end
28
+
29
+ def self.run_from_cli(argv = ARGV)
30
+ opts = { stdout: false, connection: :primary }
31
+ OptionParser.new do |o|
32
+ o.on('--stdout') { opts[:stdout] = true }
33
+ o.on('--connection=NAME') { |v| opts[:connection] = v.to_sym }
34
+ end.parse!(argv)
35
+
36
+ pool = ActiveCypher::Base.connection_handler.pool(opts[:connection])
37
+ raise "Unknown connection: #{opts[:connection]}" unless pool
38
+
39
+ dumper = new(pool.connection)
40
+ file = output_file(opts[:connection])
41
+ if opts[:stdout]
42
+ puts dumper.dump_to_string
43
+ else
44
+ dumper.dump_to_file(file)
45
+ puts "Written #{file}"
46
+ end
47
+ end
48
+
49
+ def self.output_file(conn)
50
+ case conn.to_sym
51
+ when :primary
52
+ File.join(DEFAULT_PATH, 'schema.cypher')
53
+ when :analytics
54
+ File.join(DEFAULT_PATH, 'schema.analytics.cypher')
55
+ else
56
+ File.join(DEFAULT_PATH, "schema.#{conn}.cypher")
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def catalog_from_migrations
63
+ idx = []
64
+ cons = []
65
+ Dir[File.join(@base_dir, 'graphdb', 'migrate', '*.rb')].each do |file|
66
+ require file
67
+ class_name = File.basename(file, '.rb').split('_', 2).last.camelize
68
+ klass = Object.const_get(class_name)
69
+ mig = klass.new(@connection)
70
+ mig.instance_eval(&klass.up_block) if klass.respond_to?(:up_block) && klass.up_block
71
+ mig.operations.each do |cy|
72
+ if cy =~ /CREATE\s+(UNIQUE\s+)?INDEX/i
73
+ unique = !Regexp.last_match(1).nil?
74
+ name = cy[/CREATE\s+(?:UNIQUE\s+)?INDEX\s+(\w+)/i, 1] || 'idx'
75
+ label = cy[/\(n:`?([^:`)]+)`?\)/, 1] || 'Unknown'
76
+ props = cy[/ON \(([^)]+)\)/i, 1].to_s.split(',').map { |p| p.strip.sub(/^n\./, '').sub(/^r\./, '') }
77
+ elem = cy.include?('-[r:') ? :relationship : :node
78
+ idx << IndexDef.new(name, elem, label, props, unique, nil)
79
+ elsif cy =~ /CREATE\s+CONSTRAINT/i
80
+ name = cy[/CREATE\s+CONSTRAINT\s+(\w+)/i, 1] || 'constraint'
81
+ label = cy[/\(n:`?([^:`)]+)`?\)/, 1] || 'Unknown'
82
+ if cy =~ /UNIQUE/i
83
+ props = cy[/\(([^)]+)\)/, 1].to_s.split(',').map { |p| p.strip.sub(/^n\./, '') }
84
+ kind = :unique
85
+ else
86
+ prop = cy[/n\.(\w+)\s+IS NOT NULL/i, 1]
87
+ props = [prop].compact
88
+ kind = :exists
89
+ end
90
+ cons << ConstraintDef.new(name, label, props, kind)
91
+ end
92
+ end
93
+ end
94
+ Catalog.new(indexes: idx, constraints: cons, node_types: [], edge_types: [])
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveCypher
4
+ module Schema
5
+ module Writer
6
+ class Cypher
7
+ def initialize(catalog, vendor)
8
+ @catalog = catalog
9
+ @vendor = vendor
10
+ end
11
+
12
+ def to_s
13
+ sections = []
14
+ sections << constraint_lines(@catalog.constraints)
15
+ sections << index_lines(@catalog.indexes.select { |i| i.element == :node }, :node)
16
+ sections << index_lines(@catalog.indexes.select { |i| i.element == :relationship }, :relationship)
17
+ if @vendor == :memgraph
18
+ sections << node_type_lines(@catalog.node_types)
19
+ sections << edge_type_lines(@catalog.edge_types)
20
+ end
21
+ sections.reject(&:empty?).join("\n")
22
+ end
23
+
24
+ private
25
+
26
+ def constraint_lines(list)
27
+ list.sort_by(&:name).map do |c|
28
+ props = c.props.map { |p| "n.#{p}" }.join(', ')
29
+ case c.kind
30
+ when :unique
31
+ "CREATE CONSTRAINT #{c.name} FOR (n:`#{c.label}`) REQUIRE (#{props}) IS UNIQUE"
32
+ when :exists
33
+ "CREATE CONSTRAINT #{c.name} FOR (n:`#{c.label}`) REQUIRE n.#{c.props.first} IS NOT NULL"
34
+ else
35
+ "-- UNKNOWN CONSTRAINT #{c.name}"
36
+ end
37
+ end.join("\n")
38
+ end
39
+
40
+ def index_lines(list, element)
41
+ list.sort_by(&:name).map do |i|
42
+ if @vendor == :memgraph && i.vector_opts
43
+ "-- NOT-SUPPORTED ON MEMGRAPH 3.2: Vector index #{i.name}"
44
+ else
45
+ var = element == :node ? 'n' : 'r'
46
+ target = element == :node ? "(#{var}:`#{i.label}`)" : "()-[#{var}:`#{i.label}`]-()"
47
+ props = i.props.map { |p| "#{var}.#{p}" }.join(', ')
48
+ line = +'CREATE '
49
+ line << 'UNIQUE ' if i.unique
50
+ line << "INDEX #{i.name} FOR #{target} ON (#{props})"
51
+ if i.vector_opts && @vendor == :neo4j
52
+ opts = i.vector_opts.map { |k, v| "#{k}: #{v}" }.join(', ')
53
+ line << " OPTIONS { #{opts} }"
54
+ end
55
+ line
56
+ end
57
+ end.join("\n")
58
+ end
59
+
60
+ def node_type_lines(list)
61
+ list.sort_by(&:label).map do |nt|
62
+ props = nt.props.join(', ')
63
+ pk = nt.primary_key ? " PRIMARY KEY #{nt.primary_key}" : ''
64
+ "CREATE NODE TYPE #{nt.label}#{pk} PROPERTIES #{props}"
65
+ end.join("\n")
66
+ end
67
+
68
+ def edge_type_lines(list)
69
+ list.sort_by(&:type).map do |et|
70
+ props = et.props.join(', ')
71
+ "CREATE EDGE TYPE #{et.type} FROM #{et.from} TO #{et.to} PROPERTIES #{props}"
72
+ end.join("\n")
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveCypher
4
- VERSION = '0.6.3'
4
+ VERSION = '0.7.1'
5
5
 
6
6
  def self.gem_version
7
7
  Gem::Version.new VERSION
data/lib/cyrel/node.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_support/core_ext/string/inflections'
4
-
5
3
  module Cyrel
6
4
  # The base class for building Cypher queries.
7
5
  class Node
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :graphdb do
4
+ desc 'Run graph database migrations'
5
+ task migrate: :environment do
6
+ ActiveCypher::Migrator.new.migrate!
7
+ puts 'GraphDB migrations complete'
8
+ end
9
+
10
+ desc 'Show graph database migration status'
11
+ task status: :environment do
12
+ ActiveCypher::Migrator.new.status.each do |m|
13
+ puts format('%-4s %s %s', m[:status], m[:version], m[:name])
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :graphdb do
4
+ namespace :schema do
5
+ desc 'Dump current graph schema to graphdb/schema*.cypher'
6
+ task dump: :environment do
7
+ ActiveCypher::Schema::Dumper.run_from_cli
8
+ end
9
+ end
10
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activecypher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
@@ -132,15 +132,19 @@ files:
132
132
  - lib/active_cypher/fixtures/registry.rb
133
133
  - lib/active_cypher/fixtures/rel_builder.rb
134
134
  - lib/active_cypher/generators/install_generator.rb
135
+ - lib/active_cypher/generators/migration_generator.rb
135
136
  - lib/active_cypher/generators/node_generator.rb
136
137
  - lib/active_cypher/generators/relationship_generator.rb
137
138
  - lib/active_cypher/generators/templates/application_graph_node.rb
138
139
  - lib/active_cypher/generators/templates/application_graph_relationship.rb
139
140
  - lib/active_cypher/generators/templates/cypher_databases.yml
141
+ - lib/active_cypher/generators/templates/migration.rb.erb
140
142
  - lib/active_cypher/generators/templates/node.rb.erb
141
143
  - lib/active_cypher/generators/templates/relationship.rb.erb
142
144
  - lib/active_cypher/instrumentation.rb
143
145
  - lib/active_cypher/logging.rb
146
+ - lib/active_cypher/migration.rb
147
+ - lib/active_cypher/migrator.rb
144
148
  - lib/active_cypher/model/abstract.rb
145
149
  - lib/active_cypher/model/attributes.rb
146
150
  - lib/active_cypher/model/callbacks.rb
@@ -158,6 +162,9 @@ files:
158
162
  - lib/active_cypher/relation.rb
159
163
  - lib/active_cypher/relationship.rb
160
164
  - lib/active_cypher/runtime_registry.rb
165
+ - lib/active_cypher/schema/catalog.rb
166
+ - lib/active_cypher/schema/dumper.rb
167
+ - lib/active_cypher/schema/writer/cypher.rb
161
168
  - lib/active_cypher/scoping.rb
162
169
  - lib/active_cypher/utils/logger.rb
163
170
  - lib/active_cypher/version.rb
@@ -205,6 +212,8 @@ files:
205
212
  - lib/cyrel/return_only.rb
206
213
  - lib/cyrel/types/hash_type.rb
207
214
  - lib/cyrel/types/symbol_type.rb
215
+ - lib/tasks/graphdb_migrate.rake
216
+ - lib/tasks/graphdb_schema.rake
208
217
  - sig/activecypher.rbs
209
218
  homepage: https://github.com/seuros/activecypher
210
219
  licenses: