neo4j 7.2.3 → 8.0.0.alpha.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 +4 -4
- data/CHANGELOG.md +30 -46
- data/Gemfile +15 -14
- data/README.md +21 -14
- data/bin/neo4j-jars +1 -1
- data/lib/neo4j.rb +12 -1
- data/lib/neo4j/active_base.rb +68 -0
- data/lib/neo4j/active_base/session_registry.rb +12 -0
- data/lib/neo4j/active_node.rb +13 -21
- data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +6 -6
- data/lib/neo4j/active_node/enum.rb +3 -6
- data/lib/neo4j/active_node/has_n.rb +24 -19
- data/lib/neo4j/active_node/has_n/association.rb +6 -2
- data/lib/neo4j/active_node/has_n/association/rel_factory.rb +1 -1
- data/lib/neo4j/active_node/has_n/association/rel_wrapper.rb +1 -1
- data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +1 -1
- data/lib/neo4j/active_node/id_property.rb +52 -15
- data/lib/neo4j/active_node/labels.rb +32 -10
- data/lib/neo4j/active_node/labels/index.rb +5 -55
- data/lib/neo4j/active_node/node_list_formatter.rb +13 -0
- data/lib/neo4j/active_node/node_wrapper.rb +39 -37
- data/lib/neo4j/active_node/persistence.rb +27 -13
- data/lib/neo4j/active_node/query/query_proxy.rb +11 -9
- data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +4 -4
- data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +1 -0
- data/lib/neo4j/active_node/query/query_proxy_link.rb +13 -9
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +76 -8
- data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +1 -1
- data/lib/neo4j/active_node/query_methods.rb +3 -3
- data/lib/neo4j/active_node/scope.rb +24 -7
- data/lib/neo4j/active_rel.rb +21 -3
- data/lib/neo4j/active_rel/initialize.rb +2 -2
- data/lib/neo4j/active_rel/persistence.rb +32 -6
- data/lib/neo4j/active_rel/persistence/query_factory.rb +3 -3
- data/lib/neo4j/active_rel/property.rb +9 -9
- data/lib/neo4j/active_rel/query.rb +6 -4
- data/lib/neo4j/active_rel/rel_wrapper.rb +24 -16
- data/lib/neo4j/active_rel/related_node.rb +5 -1
- data/lib/neo4j/active_rel/types.rb +2 -2
- data/lib/neo4j/config.rb +0 -1
- data/lib/neo4j/errors.rb +3 -0
- data/lib/neo4j/migration.rb +90 -71
- data/lib/neo4j/migrations.rb +10 -0
- data/lib/neo4j/migrations/base.rb +44 -0
- data/lib/neo4j/migrations/helpers.rb +101 -0
- data/lib/neo4j/migrations/helpers/id_property.rb +75 -0
- data/lib/neo4j/migrations/helpers/relationships.rb +66 -0
- data/lib/neo4j/migrations/helpers/schema.rb +53 -0
- data/lib/neo4j/migrations/migration_file.rb +24 -0
- data/lib/neo4j/migrations/runner.rb +110 -0
- data/lib/neo4j/migrations/schema_migration.rb +9 -0
- data/lib/neo4j/model_schema.rb +100 -0
- data/lib/neo4j/railtie.rb +29 -110
- data/lib/neo4j/schema/operation.rb +24 -13
- data/lib/neo4j/session_manager.rb +137 -0
- data/lib/neo4j/shared.rb +20 -11
- data/lib/neo4j/shared/attributes.rb +10 -16
- data/lib/neo4j/shared/callbacks.rb +3 -3
- data/lib/neo4j/shared/cypher.rb +1 -1
- data/lib/neo4j/shared/declared_properties.rb +1 -1
- data/lib/neo4j/shared/declared_property.rb +1 -1
- data/lib/neo4j/shared/enum.rb +6 -18
- data/lib/neo4j/shared/identity.rb +27 -21
- data/lib/neo4j/shared/persistence.rb +26 -17
- data/lib/neo4j/shared/property.rb +5 -2
- data/lib/neo4j/shared/query_factory.rb +4 -5
- data/lib/neo4j/shared/type_converters.rb +8 -9
- data/lib/neo4j/shared/validations.rb +1 -5
- data/lib/neo4j/tasks/migration.rake +83 -2
- data/lib/neo4j/version.rb +1 -1
- data/lib/rails/generators/neo4j/migration/migration_generator.rb +14 -0
- data/lib/rails/generators/neo4j/migration/templates/migration.erb +9 -0
- data/lib/rails/generators/neo4j/model/model_generator.rb +1 -3
- data/lib/rails/generators/neo4j_generator.rb +1 -0
- data/neo4j.gemspec +3 -3
- metadata +58 -65
- data/bin/rake +0 -17
- data/lib/neo4j/shared/permitted_attributes.rb +0 -28
@@ -0,0 +1,44 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Migrations
|
3
|
+
class Base < ::Neo4j::Migration
|
4
|
+
include Neo4j::Migrations::Helpers
|
5
|
+
include Neo4j::Migrations::Helpers::Schema
|
6
|
+
include Neo4j::Migrations::Helpers::IdProperty
|
7
|
+
include Neo4j::Migrations::Helpers::Relationships
|
8
|
+
|
9
|
+
def initialize(migration_id)
|
10
|
+
@migration_id = migration_id
|
11
|
+
end
|
12
|
+
|
13
|
+
def migrate(method)
|
14
|
+
ensure_schema_migration_constraint
|
15
|
+
Benchmark.realtime do
|
16
|
+
ActiveBase.run_transaction(transactions?) do
|
17
|
+
if method == :up
|
18
|
+
up
|
19
|
+
SchemaMigration.create!(migration_id: @migration_id)
|
20
|
+
else
|
21
|
+
down
|
22
|
+
SchemaMigration.find_by!(migration_id: @migration_id).destroy
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def up
|
29
|
+
fail NotImplementedError
|
30
|
+
end
|
31
|
+
|
32
|
+
def down
|
33
|
+
fail NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def ensure_schema_migration_constraint
|
39
|
+
SchemaMigration.first
|
40
|
+
Neo4j::Core::Label.wait_for_schema_changes(ActiveBase.current_session)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
|
3
|
+
module Neo4j
|
4
|
+
module Migrations
|
5
|
+
module Helpers
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
extend ActiveSupport::Autoload
|
8
|
+
|
9
|
+
autoload :Schema
|
10
|
+
autoload :IdProperty
|
11
|
+
autoload :Relationships
|
12
|
+
|
13
|
+
PROPERTY_ALREADY_DEFINED = 'Property `%{new_property}` is already defined in `%{label}`. '\
|
14
|
+
'To overwrite, call `remove_property(:%{label}, :%{new_property})` before this method.'.freeze
|
15
|
+
|
16
|
+
def remove_property(label, property)
|
17
|
+
by_label(label).remove(n: property).exec
|
18
|
+
end
|
19
|
+
|
20
|
+
def rename_property(label, old_property, new_property)
|
21
|
+
fail Neo4j::MigrationError, format(PROPERTY_ALREADY_DEFINED, new_property: new_property, label: label) if property_exists?(label, new_property)
|
22
|
+
by_label(label).set("n.#{new_property} = n.#{old_property}")
|
23
|
+
.remove("n.#{old_property}").exec
|
24
|
+
end
|
25
|
+
|
26
|
+
def drop_nodes(label)
|
27
|
+
query.match(n: label)
|
28
|
+
.optional_match('(n)-[r]-()')
|
29
|
+
.delete(:r, :n).exec
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_labels(label, new_labels)
|
33
|
+
by_label(label).set(n: new_labels).exec
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_label(label, new_label)
|
37
|
+
add_labels(label, [new_label])
|
38
|
+
end
|
39
|
+
|
40
|
+
def remove_labels(label, labels_to_remove)
|
41
|
+
by_label(label).remove(n: labels_to_remove).exec
|
42
|
+
end
|
43
|
+
|
44
|
+
def remove_label(label, label_to_remove)
|
45
|
+
remove_labels(label, [label_to_remove])
|
46
|
+
end
|
47
|
+
|
48
|
+
def rename_label(old_label, new_label)
|
49
|
+
by_label(old_label).set(n: new_label).remove(n: old_label).exec
|
50
|
+
end
|
51
|
+
|
52
|
+
def execute(string, params = {})
|
53
|
+
ActiveBase.query(string, params).to_a
|
54
|
+
end
|
55
|
+
|
56
|
+
def say_with_time(message)
|
57
|
+
say(message)
|
58
|
+
result = nil
|
59
|
+
time = Benchmark.measure { result = yield }
|
60
|
+
say format('%.4fs', time.real), :subitem
|
61
|
+
say("#{result} rows", :subitem) if result.is_a?(Integer)
|
62
|
+
result
|
63
|
+
end
|
64
|
+
|
65
|
+
def say(message, subitem = false)
|
66
|
+
output "#{subitem ? ' ->' : '--'} #{message}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def query(*args)
|
70
|
+
ActiveBase.new_query(*args)
|
71
|
+
end
|
72
|
+
|
73
|
+
protected
|
74
|
+
|
75
|
+
def transactions?
|
76
|
+
self.class.transaction?
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def property_exists?(label, property)
|
82
|
+
by_label(label).where("EXISTS(n.#{property})").return(:n).any?
|
83
|
+
end
|
84
|
+
|
85
|
+
def by_label(label, options = {})
|
86
|
+
symbol = options[:symbol] || :n
|
87
|
+
query.match(symbol => label)
|
88
|
+
end
|
89
|
+
|
90
|
+
module ClassMethods
|
91
|
+
def disable_transactions!
|
92
|
+
@disable_transactions = true
|
93
|
+
end
|
94
|
+
|
95
|
+
def transaction?
|
96
|
+
!@disable_transactions
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Migrations
|
3
|
+
module Helpers
|
4
|
+
module IdProperty
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
def populate_id_property(label)
|
8
|
+
model = label.to_s.constantize
|
9
|
+
max_per_batch = (ENV['MAX_PER_BATCH'] || default_max_per_batch).to_i
|
10
|
+
|
11
|
+
last_time_taken = nil
|
12
|
+
|
13
|
+
until (nodes_left = idless_count(label, model.primary_key)) == 0
|
14
|
+
print_status(last_time_taken, max_per_batch, nodes_left)
|
15
|
+
|
16
|
+
count = [nodes_left, max_per_batch].min
|
17
|
+
last_time_taken = Benchmark.realtime do
|
18
|
+
max_per_batch = id_batch_set(label, model.primary_key, Array.new(count) { new_id_for(model) }, count)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def idless_count(label, id_property)
|
26
|
+
query.match(n: label).where("NOT EXISTS(n.#{id_property})").pluck('COUNT(n) AS ids').first
|
27
|
+
end
|
28
|
+
|
29
|
+
def id_batch_set(label, id_property, new_ids, count)
|
30
|
+
tx = ActiveBase.new_transaction
|
31
|
+
|
32
|
+
execute("MATCH (n:`#{label}`) WHERE NOT EXISTS(n.#{id_property})
|
33
|
+
with COLLECT(n) as nodes, #{new_ids} as ids
|
34
|
+
FOREACH(i in range(0,#{count - 1})|
|
35
|
+
FOREACH(node in [nodes[i]]|
|
36
|
+
SET node.#{id_property} = ids[i]))
|
37
|
+
RETURN distinct(true)
|
38
|
+
LIMIT #{count}")
|
39
|
+
|
40
|
+
count
|
41
|
+
rescue Neo4j::Server::CypherResponse::ResponseError, Faraday::TimeoutError
|
42
|
+
new_max_per_batch = (max_per_batch * 0.8).round
|
43
|
+
output "Error querying #{max_per_batch} nodes. Trying #{new_max_per_batch}"
|
44
|
+
new_max_per_batch
|
45
|
+
ensure
|
46
|
+
tx.close
|
47
|
+
end
|
48
|
+
|
49
|
+
def print_status(last_time_taken, max_per_batch, nodes_left)
|
50
|
+
time_per_node = last_time_taken / max_per_batch if last_time_taken
|
51
|
+
message = if time_per_node
|
52
|
+
eta_seconds = (nodes_left * time_per_node).round
|
53
|
+
"#{nodes_left} nodes left. Last batch: #{(time_per_node * 1000.0).round(1)}ms / node (ETA: #{eta_seconds / 60} minutes)"
|
54
|
+
else
|
55
|
+
'Running first batch...'
|
56
|
+
end
|
57
|
+
|
58
|
+
output message
|
59
|
+
end
|
60
|
+
|
61
|
+
def default_max_per_batch
|
62
|
+
900
|
63
|
+
end
|
64
|
+
|
65
|
+
def new_id_for(model)
|
66
|
+
if model.id_property_info[:type][:auto]
|
67
|
+
SecureRandom.uuid
|
68
|
+
else
|
69
|
+
model.new.send(model.id_property_info[:type][:on])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Migrations
|
3
|
+
module Helpers
|
4
|
+
module Relationships
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
DEFAULT_MAX_PER_BATCH = 1000
|
8
|
+
|
9
|
+
def change_relations_style(relationships, old_style, new_style, params = {})
|
10
|
+
relationships.each do |rel|
|
11
|
+
relabel_relation(relationship_style(rel, old_style), relationship_style(rel, new_style), params)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def relabel_relation(old_name, new_name, params = {})
|
16
|
+
relation_query = match_relation(old_name, params)
|
17
|
+
|
18
|
+
max_per_batch = (ENV['MAX_PER_BATCH'] || DEFAULT_MAX_PER_BATCH).to_i
|
19
|
+
|
20
|
+
count = count_relations(relation_query)
|
21
|
+
output "Indexing #{count} #{old_name}s into #{new_name}..."
|
22
|
+
while count > 0
|
23
|
+
relation_query.create("(a)-[r2:`#{new_name}`]->(b)").set('r2 = r').with(:r).limit(max_per_batch).delete(:r).exec
|
24
|
+
count = count_relations(relation_query)
|
25
|
+
output "... #{count} #{old_name}'s left to go.." if count > 0
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def match_relation(label, params = {})
|
32
|
+
from = params[:from] ? "(a:`#{params[:from]}`)" : '(a)'
|
33
|
+
to = params[:to] ? "(b:`#{params[:to]}`)" : '(b)'
|
34
|
+
relation = arrow_cypher(label, params[:direction])
|
35
|
+
|
36
|
+
query.match("#{from}#{relation}#{to}")
|
37
|
+
end
|
38
|
+
|
39
|
+
def arrow_cypher(label, direction)
|
40
|
+
case direction
|
41
|
+
when :in
|
42
|
+
"<-[r:`#{label}`]-"
|
43
|
+
when :both
|
44
|
+
"<-[r:`#{label}`]->"
|
45
|
+
else
|
46
|
+
"-[r:`#{label}`]->"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def count_relations(query)
|
51
|
+
query.pluck('COUNT(r)').first
|
52
|
+
end
|
53
|
+
|
54
|
+
def relationship_style(relationship, format)
|
55
|
+
case format.to_s
|
56
|
+
when 'lower_hashtag' then "##{relationship.downcase}"
|
57
|
+
when 'lower' then relationship.downcase
|
58
|
+
when 'upper' then relationship.upcase
|
59
|
+
else
|
60
|
+
fail("Invalid relationship type style `#{format}`.")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Migrations
|
3
|
+
module Helpers
|
4
|
+
module Schema
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
MISSING_CONSTRAINT_OR_INDEX = 'No such %{type} for %{label}#%{property}'.freeze
|
7
|
+
DUPLICATE_CONSTRAINT_OR_INDEX = 'Duplicate %{type} for %{label}#%{property}'.freeze
|
8
|
+
|
9
|
+
def add_constraint(label, property, options = {})
|
10
|
+
force = options[:force] || false
|
11
|
+
label_object = ActiveBase.label_object(label)
|
12
|
+
fail_duplicate_constraint_or_index!(:constraint, label, property) if !force && label_object.constraint?(property)
|
13
|
+
label_object.create_constraint(property, type: :uniqueness)
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_index(label, property, options = {})
|
17
|
+
force = options[:force] || false
|
18
|
+
label_object = ActiveBase.label_object(label)
|
19
|
+
fail_duplicate_constraint_or_index!(:index, label, property) if !force && label_object.index?(property)
|
20
|
+
label_object.create_index(property)
|
21
|
+
end
|
22
|
+
|
23
|
+
def force_add_index(label, property)
|
24
|
+
add_index(label, property)
|
25
|
+
end
|
26
|
+
|
27
|
+
def drop_constraint(label, property)
|
28
|
+
label_object = ActiveBase.label_object(label)
|
29
|
+
fail_missing_constraint_or_index!(:constraint, label, property) if !label_object.constraint?(property)
|
30
|
+
label_object.drop_constraint(property, type: :uniqueness)
|
31
|
+
end
|
32
|
+
|
33
|
+
def drop_index(label, property)
|
34
|
+
label_object = ActiveBase.label_object(label)
|
35
|
+
fail_missing_constraint_or_index!(:index, label, property) if !label_object.index?(property)
|
36
|
+
label_object.drop_index(property)
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
def fail_missing_constraint_or_index!(type, label, property)
|
42
|
+
fail Neo4j::MigrationError,
|
43
|
+
format(MISSING_CONSTRAINT_OR_INDEX, type: type, label: label, property: property)
|
44
|
+
end
|
45
|
+
|
46
|
+
def fail_duplicate_constraint_or_index!(type, label, property)
|
47
|
+
fail Neo4j::MigrationError,
|
48
|
+
format(DUPLICATE_CONSTRAINT_OR_INDEX, type: type, label: label, property: property)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Migrations
|
3
|
+
class MigrationFile
|
4
|
+
attr_reader :file_name, :symbol_name, :class_name, :version
|
5
|
+
|
6
|
+
def initialize(file_name)
|
7
|
+
@file_name = file_name
|
8
|
+
extract_data!
|
9
|
+
end
|
10
|
+
|
11
|
+
def create
|
12
|
+
require @file_name
|
13
|
+
class_name.constantize.new(@version)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def extract_data!
|
19
|
+
@version, @symbol_name = File.basename(@file_name, '.rb').split('_', 2)
|
20
|
+
@class_name = @symbol_name.camelize
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Migrations
|
3
|
+
class Runner
|
4
|
+
STATUS_TABLE_FORMAT = '%-10s %-20s %s'.freeze
|
5
|
+
SEPARATOR = '--------------------------------------------------'.freeze
|
6
|
+
FILE_MISSING = '**** file missing ****'.freeze
|
7
|
+
STATUS_TABLE_HEADER = ['Status'.freeze, 'Migration ID'.freeze, 'Migration Name'.freeze].freeze
|
8
|
+
UP_MESSAGE = 'up'.freeze
|
9
|
+
DOWN_MESSAGE = 'down'.freeze
|
10
|
+
MIGRATION_RUNNING = {up: 'running'.freeze, down: 'reverting'.freeze}.freeze
|
11
|
+
MIGRATION_DONE = {up: 'migrated'.freeze, down: 'reverted'.freeze}.freeze
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
SchemaMigration.mapped_label.create_constraint(:migration_id, type: :unique)
|
15
|
+
@up_versions = SortedSet.new(SchemaMigration.all.pluck(:migration_id))
|
16
|
+
end
|
17
|
+
|
18
|
+
def all
|
19
|
+
migration_files.each do |migration_file|
|
20
|
+
next if up?(migration_file.version)
|
21
|
+
migrate(:up, migration_file)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def up(version)
|
26
|
+
migration_file = find_by_version!(version)
|
27
|
+
return if up?(version)
|
28
|
+
migrate(:up, migration_file)
|
29
|
+
end
|
30
|
+
|
31
|
+
def down(version)
|
32
|
+
migration_file = find_by_version!(version)
|
33
|
+
return unless up?(version)
|
34
|
+
migrate(:down, migration_file)
|
35
|
+
end
|
36
|
+
|
37
|
+
def rollback(steps)
|
38
|
+
@up_versions.to_a.reverse.first(steps).each do |version|
|
39
|
+
down(version)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def status
|
44
|
+
output STATUS_TABLE_FORMAT, *STATUS_TABLE_HEADER
|
45
|
+
output SEPARATOR
|
46
|
+
all_migrations.each do |version|
|
47
|
+
status = up?(version) ? UP_MESSAGE : DOWN_MESSAGE
|
48
|
+
migration_file = find_by_version(version)
|
49
|
+
migration_name = migration_file ? migration_file.class_name : FILE_MISSING
|
50
|
+
output STATUS_TABLE_FORMAT, status, version, migration_name
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def up?(version)
|
57
|
+
@up_versions.include?(version)
|
58
|
+
end
|
59
|
+
|
60
|
+
def migrate(direction, migration_file)
|
61
|
+
migration_message(direction, migration_file) do
|
62
|
+
migration = migration_file.create
|
63
|
+
migration.migrate(direction)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def migration_message(direction, migration)
|
68
|
+
output "== #{migration.version} #{migration.class_name}: #{MIGRATION_RUNNING[direction]}... ========="
|
69
|
+
yield
|
70
|
+
output "== #{migration.version} #{migration.class_name}: #{MIGRATION_DONE[direction]} ========="
|
71
|
+
end
|
72
|
+
|
73
|
+
def output(*string_format)
|
74
|
+
puts format(*string_format) unless !!ENV['MIGRATIONS_SILENCED']
|
75
|
+
end
|
76
|
+
|
77
|
+
def find_by_version!(version)
|
78
|
+
find_by_version(version) || fail(UnknownMigrationVersionError, "No such migration #{version}")
|
79
|
+
end
|
80
|
+
|
81
|
+
def find_by_version(version)
|
82
|
+
migration_files.find { |file| file.version == version }
|
83
|
+
end
|
84
|
+
|
85
|
+
def all_migrations
|
86
|
+
@up_versions + files_versions
|
87
|
+
end
|
88
|
+
|
89
|
+
def files_versions
|
90
|
+
migration_files.map(&:version)
|
91
|
+
end
|
92
|
+
|
93
|
+
def migration_files
|
94
|
+
files.map { |file_path| MigrationFile.new(file_path) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def files
|
98
|
+
Dir[files_path].sort
|
99
|
+
end
|
100
|
+
|
101
|
+
def files_path
|
102
|
+
app_root.join('db', 'neo4j', 'migrate', '*.rb')
|
103
|
+
end
|
104
|
+
|
105
|
+
def app_root
|
106
|
+
defined?(Rails) ? Rails.root : Pathname.new('.')
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|