activegraph 10.0.0.pre.alpha.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1989 -0
- data/CONTRIBUTORS +12 -0
- data/Gemfile +24 -0
- data/README.md +107 -0
- data/bin/rake +17 -0
- data/config/locales/en.yml +5 -0
- data/config/neo4j/add_classnames.yml +1 -0
- data/config/neo4j/config.yml +38 -0
- data/lib/neo4j.rb +116 -0
- data/lib/neo4j/active_base.rb +89 -0
- data/lib/neo4j/active_node.rb +108 -0
- data/lib/neo4j/active_node/callbacks.rb +8 -0
- data/lib/neo4j/active_node/dependent.rb +11 -0
- data/lib/neo4j/active_node/dependent/association_methods.rb +49 -0
- data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +51 -0
- data/lib/neo4j/active_node/enum.rb +26 -0
- data/lib/neo4j/active_node/has_n.rb +612 -0
- data/lib/neo4j/active_node/has_n/association.rb +278 -0
- data/lib/neo4j/active_node/has_n/association/rel_factory.rb +61 -0
- data/lib/neo4j/active_node/has_n/association/rel_wrapper.rb +23 -0
- data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +108 -0
- data/lib/neo4j/active_node/id_property.rb +224 -0
- data/lib/neo4j/active_node/id_property/accessor.rb +62 -0
- data/lib/neo4j/active_node/initialize.rb +21 -0
- data/lib/neo4j/active_node/labels.rb +207 -0
- data/lib/neo4j/active_node/labels/index.rb +37 -0
- data/lib/neo4j/active_node/labels/reloading.rb +21 -0
- data/lib/neo4j/active_node/node_list_formatter.rb +13 -0
- data/lib/neo4j/active_node/node_wrapper.rb +54 -0
- data/lib/neo4j/active_node/orm_adapter.rb +82 -0
- data/lib/neo4j/active_node/persistence.rb +187 -0
- data/lib/neo4j/active_node/property.rb +60 -0
- data/lib/neo4j/active_node/query.rb +76 -0
- data/lib/neo4j/active_node/query/query_proxy.rb +374 -0
- data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +177 -0
- data/lib/neo4j/active_node/query/query_proxy_eager_loading/association_tree.rb +75 -0
- data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +110 -0
- data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +19 -0
- data/lib/neo4j/active_node/query/query_proxy_link.rb +139 -0
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +302 -0
- data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +86 -0
- data/lib/neo4j/active_node/query_methods.rb +68 -0
- data/lib/neo4j/active_node/reflection.rb +86 -0
- data/lib/neo4j/active_node/rels.rb +11 -0
- data/lib/neo4j/active_node/scope.rb +166 -0
- data/lib/neo4j/active_node/unpersisted.rb +48 -0
- data/lib/neo4j/active_node/validations.rb +59 -0
- data/lib/neo4j/active_rel.rb +67 -0
- data/lib/neo4j/active_rel/callbacks.rb +15 -0
- data/lib/neo4j/active_rel/initialize.rb +28 -0
- data/lib/neo4j/active_rel/persistence.rb +134 -0
- data/lib/neo4j/active_rel/persistence/query_factory.rb +95 -0
- data/lib/neo4j/active_rel/property.rb +95 -0
- data/lib/neo4j/active_rel/query.rb +101 -0
- data/lib/neo4j/active_rel/rel_wrapper.rb +31 -0
- data/lib/neo4j/active_rel/related_node.rb +87 -0
- data/lib/neo4j/active_rel/types.rb +82 -0
- data/lib/neo4j/active_rel/validations.rb +8 -0
- data/lib/neo4j/ansi.rb +14 -0
- data/lib/neo4j/class_arguments.rb +39 -0
- data/lib/neo4j/config.rb +135 -0
- data/lib/neo4j/core.rb +14 -0
- data/lib/neo4j/core/connection_failed_error.rb +6 -0
- data/lib/neo4j/core/cypher_error.rb +37 -0
- data/lib/neo4j/core/driver.rb +66 -0
- data/lib/neo4j/core/has_uri.rb +63 -0
- data/lib/neo4j/core/instrumentable.rb +36 -0
- data/lib/neo4j/core/label.rb +158 -0
- data/lib/neo4j/core/logging.rb +44 -0
- data/lib/neo4j/core/node.rb +23 -0
- data/lib/neo4j/core/querable.rb +88 -0
- data/lib/neo4j/core/query.rb +487 -0
- data/lib/neo4j/core/query_builder.rb +32 -0
- data/lib/neo4j/core/query_clauses.rb +727 -0
- data/lib/neo4j/core/query_ext.rb +24 -0
- data/lib/neo4j/core/query_find_in_batches.rb +49 -0
- data/lib/neo4j/core/relationship.rb +13 -0
- data/lib/neo4j/core/responses.rb +50 -0
- data/lib/neo4j/core/result.rb +33 -0
- data/lib/neo4j/core/schema.rb +30 -0
- data/lib/neo4j/core/schema_errors.rb +12 -0
- data/lib/neo4j/core/wrappable.rb +30 -0
- data/lib/neo4j/errors.rb +57 -0
- data/lib/neo4j/migration.rb +148 -0
- data/lib/neo4j/migrations.rb +27 -0
- data/lib/neo4j/migrations/base.rb +77 -0
- data/lib/neo4j/migrations/check_pending.rb +20 -0
- data/lib/neo4j/migrations/helpers.rb +105 -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 +51 -0
- data/lib/neo4j/migrations/migration_file.rb +24 -0
- data/lib/neo4j/migrations/runner.rb +195 -0
- data/lib/neo4j/migrations/schema.rb +44 -0
- data/lib/neo4j/migrations/schema_migration.rb +14 -0
- data/lib/neo4j/model_schema.rb +139 -0
- data/lib/neo4j/paginated.rb +27 -0
- data/lib/neo4j/railtie.rb +105 -0
- data/lib/neo4j/schema/operation.rb +102 -0
- data/lib/neo4j/shared.rb +60 -0
- data/lib/neo4j/shared/attributes.rb +216 -0
- data/lib/neo4j/shared/callbacks.rb +68 -0
- data/lib/neo4j/shared/cypher.rb +37 -0
- data/lib/neo4j/shared/declared_properties.rb +204 -0
- data/lib/neo4j/shared/declared_property.rb +109 -0
- data/lib/neo4j/shared/declared_property/index.rb +37 -0
- data/lib/neo4j/shared/enum.rb +167 -0
- data/lib/neo4j/shared/filtered_hash.rb +79 -0
- data/lib/neo4j/shared/identity.rb +34 -0
- data/lib/neo4j/shared/initialize.rb +64 -0
- data/lib/neo4j/shared/marshal.rb +23 -0
- data/lib/neo4j/shared/mass_assignment.rb +64 -0
- data/lib/neo4j/shared/permitted_attributes.rb +28 -0
- data/lib/neo4j/shared/persistence.rb +282 -0
- data/lib/neo4j/shared/property.rb +240 -0
- data/lib/neo4j/shared/query_factory.rb +102 -0
- data/lib/neo4j/shared/rel_type_converters.rb +43 -0
- data/lib/neo4j/shared/serialized_properties.rb +30 -0
- data/lib/neo4j/shared/type_converters.rb +433 -0
- data/lib/neo4j/shared/typecasted_attributes.rb +98 -0
- data/lib/neo4j/shared/typecaster.rb +53 -0
- data/lib/neo4j/shared/validations.rb +44 -0
- data/lib/neo4j/tasks/migration.rake +202 -0
- data/lib/neo4j/timestamps.rb +11 -0
- data/lib/neo4j/timestamps/created.rb +9 -0
- data/lib/neo4j/timestamps/updated.rb +9 -0
- data/lib/neo4j/transaction.rb +139 -0
- data/lib/neo4j/type_converters.rb +7 -0
- data/lib/neo4j/undeclared_properties.rb +53 -0
- data/lib/neo4j/version.rb +3 -0
- data/lib/neo4j/wrapper.rb +4 -0
- 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 +88 -0
- data/lib/rails/generators/neo4j/model/templates/migration.erb +9 -0
- data/lib/rails/generators/neo4j/model/templates/model.erb +15 -0
- data/lib/rails/generators/neo4j/upgrade_v8/templates/migration.erb +17 -0
- data/lib/rails/generators/neo4j/upgrade_v8/upgrade_v8_generator.rb +32 -0
- data/lib/rails/generators/neo4j_generator.rb +119 -0
- data/neo4j.gemspec +51 -0
- metadata +421 -0
@@ -0,0 +1,195 @@
|
|
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
|
+
INCOMPLETE_MESSAGE = 'incomplete'.freeze
|
11
|
+
MIGRATION_RUNNING = {up: 'running'.freeze, down: 'reverting'.freeze}.freeze
|
12
|
+
MIGRATION_DONE = {up: 'migrated'.freeze, down: 'reverted'.freeze}.freeze
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
@silenced = options[:silenced] || !!ENV['MIGRATIONS_SILENCED']
|
16
|
+
label = SchemaMigration.mapped_label
|
17
|
+
label.create_constraint(:migration_id, type: :unique) unless label.constraint?(:migration_id)
|
18
|
+
@schema_migrations = SchemaMigration.all.to_a
|
19
|
+
@up_versions = SortedSet.new(@schema_migrations.map(&:migration_id))
|
20
|
+
end
|
21
|
+
|
22
|
+
def all
|
23
|
+
handle_incomplete_states!
|
24
|
+
migration_files.each do |migration_file|
|
25
|
+
next if up?(migration_file.version)
|
26
|
+
migrate(:up, migration_file)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def up(version)
|
31
|
+
handle_incomplete_states!
|
32
|
+
migration_file = find_by_version!(version)
|
33
|
+
return if up?(version)
|
34
|
+
migrate(:up, migration_file)
|
35
|
+
end
|
36
|
+
|
37
|
+
def down(version)
|
38
|
+
handle_incomplete_states!
|
39
|
+
migration_file = find_by_version!(version)
|
40
|
+
return unless up?(version)
|
41
|
+
migrate(:down, migration_file)
|
42
|
+
end
|
43
|
+
|
44
|
+
def rollback(steps)
|
45
|
+
handle_incomplete_states!
|
46
|
+
@up_versions.to_a.reverse.first(steps).each do |version|
|
47
|
+
down(version)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def pending_migrations
|
52
|
+
all_migrations.select { |migration| !up?(migration) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def complete_migration_versions
|
56
|
+
@schema_migrations.map(&:migration_id)
|
57
|
+
end
|
58
|
+
|
59
|
+
def mark_versions_as_complete(versions)
|
60
|
+
Neo4j::ActiveBase.new_query
|
61
|
+
.with('{versions} AS versions').params(versions: versions).break
|
62
|
+
.unwind(version: :versions).break
|
63
|
+
.merge('(:`Neo4j::Migrations::SchemaMigration` {migration_id: version})')
|
64
|
+
.exec
|
65
|
+
end
|
66
|
+
|
67
|
+
def status
|
68
|
+
output STATUS_TABLE_FORMAT, *STATUS_TABLE_HEADER
|
69
|
+
output SEPARATOR
|
70
|
+
all_migrations.each do |version|
|
71
|
+
status = migration_status(version)
|
72
|
+
migration_file = find_by_version(version)
|
73
|
+
migration_name = migration_file ? migration_file.class_name : FILE_MISSING
|
74
|
+
output STATUS_TABLE_FORMAT, status, version, migration_name
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def resolve(version)
|
79
|
+
SchemaMigration.find_by!(migration_id: version).update!(incomplete: false)
|
80
|
+
output "Migration #{version} resolved."
|
81
|
+
end
|
82
|
+
|
83
|
+
def reset(version)
|
84
|
+
SchemaMigration.find_by!(migration_id: version).destroy
|
85
|
+
output "Migration #{version} reset."
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def migration_status(version)
|
91
|
+
return DOWN_MESSAGE unless up?(version)
|
92
|
+
incomplete_states.find { |v| v.migration_id == version } ? INCOMPLETE_MESSAGE : UP_MESSAGE
|
93
|
+
end
|
94
|
+
|
95
|
+
def handle_incomplete_states!
|
96
|
+
return unless incomplete_states.any?
|
97
|
+
incomplete_versions = incomplete_states.map(&:migration_id)
|
98
|
+
fail MigrationError, <<-MSG
|
99
|
+
There are migrations struck in an incomplete states, that could not be fixed automatically:
|
100
|
+
#{incomplete_versions.join('\n')}
|
101
|
+
This can happen when there's a critical error inside a migration.
|
102
|
+
|
103
|
+
If you think they were was completed correctly, run:
|
104
|
+
|
105
|
+
#{task_migration_messages('resolve', incomplete_versions)}
|
106
|
+
|
107
|
+
If you want to reset and run the migration again, run:
|
108
|
+
|
109
|
+
#{task_migration_messages('reset', incomplete_versions)}
|
110
|
+
|
111
|
+
MSG
|
112
|
+
end
|
113
|
+
|
114
|
+
def task_migration_messages(type, versions)
|
115
|
+
versions.map do |version|
|
116
|
+
"rake neo4j:migrate:#{type} VERSION=#{version}"
|
117
|
+
end.join("\n")
|
118
|
+
end
|
119
|
+
|
120
|
+
def up?(version)
|
121
|
+
@up_versions.include?(version)
|
122
|
+
end
|
123
|
+
|
124
|
+
def migrate(direction, migration_file)
|
125
|
+
migration_message(direction, migration_file) do
|
126
|
+
migration = migration_file.create(silenced: @silenced)
|
127
|
+
migration.migrate(direction)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def migration_message(direction, migration)
|
132
|
+
output_migration_message "#{migration.version} #{migration.class_name}: #{MIGRATION_RUNNING[direction]}..."
|
133
|
+
time = format('%.4fs', yield)
|
134
|
+
output_migration_message "#{migration.version} #{migration.class_name}: #{MIGRATION_DONE[direction]} (#{time})"
|
135
|
+
output ''
|
136
|
+
end
|
137
|
+
|
138
|
+
def output(*string_format)
|
139
|
+
puts format(*string_format) unless @silenced
|
140
|
+
end
|
141
|
+
|
142
|
+
def output_migration_message(message)
|
143
|
+
out = "== #{message} "
|
144
|
+
tail = '=' * [0, 80 - out.length].max
|
145
|
+
output "#{out}#{tail}"
|
146
|
+
end
|
147
|
+
|
148
|
+
def find_by_version!(version)
|
149
|
+
find_by_version(version) || fail(UnknownMigrationVersionError, "No such migration #{version}")
|
150
|
+
end
|
151
|
+
|
152
|
+
def find_by_version(version)
|
153
|
+
migration_files.find { |file| file.version == version }
|
154
|
+
end
|
155
|
+
|
156
|
+
def all_migrations
|
157
|
+
@up_versions + migration_files_versions
|
158
|
+
end
|
159
|
+
|
160
|
+
def incomplete_states
|
161
|
+
@incomplete_states ||= SortedSet.new(@schema_migrations.select(&:incomplete?))
|
162
|
+
end
|
163
|
+
|
164
|
+
delegate :migration_files, :migration_files_versions, to: :class
|
165
|
+
|
166
|
+
class <<self
|
167
|
+
def migration_files_versions
|
168
|
+
migration_files.map!(&:version)
|
169
|
+
end
|
170
|
+
|
171
|
+
def migration_files
|
172
|
+
files.map! { |file_path| MigrationFile.new(file_path) }
|
173
|
+
end
|
174
|
+
|
175
|
+
def latest_migration
|
176
|
+
migration_files.last
|
177
|
+
end
|
178
|
+
|
179
|
+
def files
|
180
|
+
Dir[files_path].sort
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
def files_path
|
186
|
+
app_root.join('db', 'neo4j', 'migrate', '*.rb')
|
187
|
+
end
|
188
|
+
|
189
|
+
def app_root
|
190
|
+
defined?(Rails) ? Rails.root : Pathname.new('.')
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Migrations
|
3
|
+
module Schema
|
4
|
+
class << self
|
5
|
+
def fetch_schema_data(session)
|
6
|
+
{constraints: fetch_constraint_descriptions(session).sort,
|
7
|
+
indexes: fetch_index_descriptions(session).sort}
|
8
|
+
end
|
9
|
+
|
10
|
+
def synchronize_schema_data(session, schema_data, remove_missing)
|
11
|
+
queries = []
|
12
|
+
queries += drop_and_create_queries(fetch_constraint_descriptions(session), schema_data[:constraints], remove_missing)
|
13
|
+
queries += drop_and_create_queries(fetch_index_descriptions(session), schema_data[:indexes], remove_missing)
|
14
|
+
session.queries do
|
15
|
+
queries.each { |query| append query }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def fetch_constraint_descriptions(session)
|
22
|
+
session.query('CALL db.constraints()').map(&:description)
|
23
|
+
end
|
24
|
+
|
25
|
+
def fetch_index_descriptions(session)
|
26
|
+
session.query('CALL db.indexes()').reject do |row|
|
27
|
+
# These indexes are created automagically when the corresponding constraints are created
|
28
|
+
row.type == 'node_unique_property'
|
29
|
+
end.map(&:description)
|
30
|
+
end
|
31
|
+
|
32
|
+
def drop_and_create_queries(existing, specified, remove_missing)
|
33
|
+
[].tap do |queries|
|
34
|
+
if remove_missing
|
35
|
+
(existing - specified).each { |description| queries << "DROP #{description}" }
|
36
|
+
end
|
37
|
+
|
38
|
+
(specified - existing).each { |description| queries << "CREATE #{description}" }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Migrations
|
3
|
+
class SchemaMigration
|
4
|
+
include Neo4j::ActiveNode
|
5
|
+
id_property :migration_id
|
6
|
+
property :migration_id, type: String
|
7
|
+
property :incomplete, type: Boolean
|
8
|
+
|
9
|
+
def <=>(other)
|
10
|
+
migration_id <=> other.migration_id
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'set'
|
2
|
+
module Neo4j
|
3
|
+
# This is here to support the removed functionality of being able to
|
4
|
+
# defined indexes and constraints on models
|
5
|
+
# This code should be removed later
|
6
|
+
module ModelSchema
|
7
|
+
MODEL_INDEXES = {}
|
8
|
+
MODEL_CONSTRAINTS = {}
|
9
|
+
REQUIRED_INDEXES = {}
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def add_defined_constraint(model, property_name)
|
13
|
+
MODEL_CONSTRAINTS[model] ||= Set.new
|
14
|
+
MODEL_CONSTRAINTS[model] << property_name.to_sym
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_defined_index(model, property_name)
|
18
|
+
MODEL_INDEXES[model] ||= Set.new
|
19
|
+
MODEL_INDEXES[model] << property_name.to_sym
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_required_index(model, property_name)
|
23
|
+
REQUIRED_INDEXES[model] ||= Set.new
|
24
|
+
REQUIRED_INDEXES[model] << property_name.to_sym
|
25
|
+
end
|
26
|
+
|
27
|
+
def defined_constraint?(model, property_name)
|
28
|
+
MODEL_CONSTRAINTS[model] &&
|
29
|
+
MODEL_CONSTRAINTS[model].include?(property_name.to_sym)
|
30
|
+
end
|
31
|
+
|
32
|
+
def model_constraints
|
33
|
+
return @model_constraints if @model_constraints
|
34
|
+
|
35
|
+
constraints = Neo4j::Transaction.constraints.each_with_object({}) do |row, result|
|
36
|
+
result[row[:label]] ||= []
|
37
|
+
result[row[:label]] << row[:properties]
|
38
|
+
end
|
39
|
+
|
40
|
+
@model_constraints = schema_elements_list(MODEL_CONSTRAINTS, constraints)
|
41
|
+
end
|
42
|
+
|
43
|
+
def model_indexes
|
44
|
+
return @model_indexes if @model_indexes
|
45
|
+
|
46
|
+
indexes = Neo4j::Transaction.indexes.each_with_object({}) do |row, result|
|
47
|
+
result[row[:label]] ||= []
|
48
|
+
result[row[:label]] << row[:properties]
|
49
|
+
end
|
50
|
+
|
51
|
+
@model_indexes = schema_elements_list(MODEL_INDEXES, indexes) +
|
52
|
+
schema_elements_list(REQUIRED_INDEXES, indexes).reject(&:last)
|
53
|
+
# reject required indexes which are already in the DB
|
54
|
+
end
|
55
|
+
|
56
|
+
# should be private
|
57
|
+
def schema_elements_list(by_model, db_results)
|
58
|
+
by_model.flat_map do |model, property_names|
|
59
|
+
label = model.mapped_label_name.to_sym
|
60
|
+
property_names.map do |property_name|
|
61
|
+
exists = db_results[label] && db_results[label].include?([property_name])
|
62
|
+
[model, label, property_name, exists]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def ensure_model_data_state!
|
68
|
+
# If we load a new model, reset everything
|
69
|
+
if @previously_loaded_models_count != Neo4j::ActiveNode.loaded_classes.size
|
70
|
+
# Make sure we've finalized id_property details and have called
|
71
|
+
# add_ constraint/index methods above
|
72
|
+
Neo4j::ActiveNode.loaded_classes.each(&:ensure_id_property_info!)
|
73
|
+
reload_models_data!
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def reload_models_data!
|
78
|
+
@previously_loaded_models_count = Neo4j::ActiveNode.loaded_classes.size
|
79
|
+
@model_indexes = @model_constraints = nil
|
80
|
+
end
|
81
|
+
|
82
|
+
def legacy_model_schema_informations
|
83
|
+
ensure_model_data_state!
|
84
|
+
data = {index: [], constraint: []}
|
85
|
+
each_schema_element do |type, model, label, property_name|
|
86
|
+
data[type] << {label: label, property_name: property_name, model: model}
|
87
|
+
end
|
88
|
+
data
|
89
|
+
end
|
90
|
+
|
91
|
+
def validate_model_schema!
|
92
|
+
ensure_model_data_state!
|
93
|
+
messages = {index: [], constraint: []}
|
94
|
+
each_schema_element do |type, model, label, property_name, exists|
|
95
|
+
if exists
|
96
|
+
log_warning!(type, model, property_name) if model.id_property_name.to_sym != property_name
|
97
|
+
else
|
98
|
+
messages[type] << force_add_message(type, label, property_name)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
return if messages.values.all?(&:empty?)
|
103
|
+
|
104
|
+
fail ::Neo4j::DeprecatedSchemaDefinitionError, validation_error_message(messages)
|
105
|
+
end
|
106
|
+
|
107
|
+
def validation_error_message(messages)
|
108
|
+
<<MSG
|
109
|
+
Some schema elements were defined by the model (which is no longer supported), but they do not exist in the database. Run the following to create them if you haven't already:
|
110
|
+
|
111
|
+
#{messages[:constraint].join("\n")}
|
112
|
+
#{messages[:index].join("\n")}
|
113
|
+
|
114
|
+
And then run `rake neo4j:migrate`
|
115
|
+
|
116
|
+
(zshell users may need to escape the brackets)
|
117
|
+
MSG
|
118
|
+
end
|
119
|
+
|
120
|
+
def force_add_message(index_or_constraint, label, property_name)
|
121
|
+
"rake neo4j:generate_schema_migration[#{index_or_constraint},#{label},#{property_name}]"
|
122
|
+
end
|
123
|
+
|
124
|
+
def log_warning!(index_or_constraint, model, property_name)
|
125
|
+
Neo4j::ActiveBase.logger.warn "WARNING: The #{index_or_constraint} option is no longer supported (Defined on #{model.name} for #{property_name})"
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def each_schema_element
|
131
|
+
[[:constraint, model_constraints], [:index, model_indexes]].each do |type, schema_elements|
|
132
|
+
schema_elements.each do |args|
|
133
|
+
yield(type, *args)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Neo4j
|
2
|
+
class Paginated
|
3
|
+
include Enumerable
|
4
|
+
attr_reader :items, :total, :current_page
|
5
|
+
|
6
|
+
def initialize(items, total, current_page)
|
7
|
+
@items = items
|
8
|
+
@total = total
|
9
|
+
@current_page = current_page
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.create_from(source, page, per_page, order = nil)
|
13
|
+
target = source.node_var || source.identity
|
14
|
+
partial = source.skip((page - 1) * per_page).limit(per_page)
|
15
|
+
ordered_partial, ordered_source = if order
|
16
|
+
[partial.order_by(order), source.query.with("#{target} as #{target}").pluck("COUNT(#{target})").first]
|
17
|
+
else
|
18
|
+
[partial, source.count]
|
19
|
+
end
|
20
|
+
Paginated.new(ordered_partial, ordered_source, page)
|
21
|
+
end
|
22
|
+
|
23
|
+
delegate :each, to: :items
|
24
|
+
delegate :pluck, to: :items
|
25
|
+
delegate :size, :[], to: :items
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'active_support/notifications'
|
2
|
+
require 'rails/railtie'
|
3
|
+
# Need the action_dispatch railtie to have action_dispatch.rescue_responses initialized correctly
|
4
|
+
require 'action_dispatch/railtie'
|
5
|
+
require 'neo4j/core/driver'
|
6
|
+
|
7
|
+
module Neo4j
|
8
|
+
class Railtie < ::Rails::Railtie
|
9
|
+
def empty_config
|
10
|
+
ActiveSupport::OrderedOptions.new.tap { |cfg| cfg.session = ActiveSupport::OrderedOptions.new }
|
11
|
+
end
|
12
|
+
|
13
|
+
config.neo4j = empty_config
|
14
|
+
|
15
|
+
if defined?(ActiveSupport::Reloader)
|
16
|
+
ActiveSupport::Reloader.to_prepare do
|
17
|
+
Neo4j::ActiveNode::Labels::Reloading.reload_models!
|
18
|
+
end
|
19
|
+
elsif const_defined?(:ActionDispatch)
|
20
|
+
ActionDispatch::Reloader.to_prepare do
|
21
|
+
Neo4j::ActiveNode::Labels::Reloading.reload_models!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Rescue responses similar to ActiveRecord.
|
26
|
+
config.action_dispatch.rescue_responses.merge!(
|
27
|
+
'Neo4j::RecordNotFound' => :not_found,
|
28
|
+
'Neo4j::ActiveNode::Labels::RecordNotFound' => :not_found
|
29
|
+
)
|
30
|
+
|
31
|
+
# Add ActiveModel translations to the I18n load_path
|
32
|
+
initializer 'i18n' do
|
33
|
+
config.i18n.load_path += Dir[File.join(File.dirname(__FILE__), '..', '..', '..', 'config', 'locales', '*.{rb,yml}')]
|
34
|
+
end
|
35
|
+
|
36
|
+
console do
|
37
|
+
Neo4j::Config[:logger] = ActiveSupport::Logger.new(STDOUT)
|
38
|
+
Neo4j::Config[:verbose_query_logs] = false
|
39
|
+
end
|
40
|
+
|
41
|
+
# Starting Neo after :load_config_initializers allows apps to
|
42
|
+
# register migrations in config/initializers
|
43
|
+
initializer 'neo4j.start', after: :load_config_initializers do |app|
|
44
|
+
app.config.neo4j.skip_migration_check = true if Rails.env.test?
|
45
|
+
|
46
|
+
neo4j_config = ActiveSupport::OrderedOptions.new
|
47
|
+
app.config.neo4j.each { |k, v| neo4j_config[k] = v } if app.config.neo4j
|
48
|
+
|
49
|
+
Neo4j::Config.configuration.merge!(neo4j_config.to_h)
|
50
|
+
|
51
|
+
Neo4j::ActiveBase.on_establish_session { setup! neo4j_config }
|
52
|
+
|
53
|
+
Neo4j::Config[:logger] ||= Rails.logger
|
54
|
+
|
55
|
+
if Neo4j::Config.fail_on_pending_migrations
|
56
|
+
config.app_middleware.insert_after ::ActionDispatch::Callbacks, Neo4j::Migrations::CheckPending
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def setup!(neo4j_config = empty_config)
|
61
|
+
url, path, options = final_session_config!(neo4j_config).values_at(:url, :path, :options)
|
62
|
+
options ||= {}
|
63
|
+
register_neo4j_cypher_logging
|
64
|
+
|
65
|
+
Neo4j::ActiveBase.new_driver( url || path || default_session_path_or_url, options)
|
66
|
+
end
|
67
|
+
|
68
|
+
def final_session_config!(neo4j_config)
|
69
|
+
(neo4j_config[:session].empty? ? yaml_config_data : neo4j_config[:session]).dup
|
70
|
+
end
|
71
|
+
|
72
|
+
def default_session_path_or_url
|
73
|
+
ENV['NEO4J_URL'] || ENV['NEO4J_PATH'] || 'http://localhost:7474'
|
74
|
+
end
|
75
|
+
|
76
|
+
def yaml_config_data
|
77
|
+
@yaml_config_data ||= if yaml_path
|
78
|
+
HashWithIndifferentAccess.new(YAML.load(ERB.new(yaml_path.read).result)[Rails.env])
|
79
|
+
else
|
80
|
+
{}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def yaml_path
|
85
|
+
return unless defined?(Rails)
|
86
|
+
@yaml_path ||= %w(config/neo4j.yml config/neo4j.yaml).map do |path|
|
87
|
+
Rails.root.join(path)
|
88
|
+
end.detect(&:exist?)
|
89
|
+
end
|
90
|
+
|
91
|
+
def register_neo4j_cypher_logging
|
92
|
+
return if @neo4j_cypher_logging_registered
|
93
|
+
|
94
|
+
Neo4j::Core::Query.pretty_cypher = Neo4j::Config[:pretty_logged_cypher_queries]
|
95
|
+
|
96
|
+
logger_proc = ->(message) do
|
97
|
+
(Neo4j::Config[:logger] ||= Rails.logger).debug message
|
98
|
+
end
|
99
|
+
Neo4j::Transaction.subscribe_to_query(&logger_proc)
|
100
|
+
Neo4j::Transaction.subscribe_to_request(&logger_proc)
|
101
|
+
|
102
|
+
@neo4j_cypher_logging_registered = true
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|