activegraph 11.0.0.beta.1-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +2016 -0
- data/CONTRIBUTORS +12 -0
- data/Gemfile +24 -0
- data/README.md +111 -0
- data/activegraph.gemspec +52 -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 +35 -0
- data/lib/active_graph.rb +123 -0
- data/lib/active_graph/ansi.rb +14 -0
- data/lib/active_graph/attribute_set.rb +32 -0
- data/lib/active_graph/base.rb +77 -0
- data/lib/active_graph/class_arguments.rb +39 -0
- data/lib/active_graph/config.rb +135 -0
- data/lib/active_graph/core.rb +14 -0
- data/lib/active_graph/core/connection_failed_error.rb +6 -0
- data/lib/active_graph/core/cypher_error.rb +37 -0
- data/lib/active_graph/core/entity.rb +11 -0
- data/lib/active_graph/core/instrumentable.rb +37 -0
- data/lib/active_graph/core/label.rb +135 -0
- data/lib/active_graph/core/logging.rb +44 -0
- data/lib/active_graph/core/node.rb +15 -0
- data/lib/active_graph/core/querable.rb +41 -0
- data/lib/active_graph/core/query.rb +485 -0
- data/lib/active_graph/core/query_builder.rb +18 -0
- data/lib/active_graph/core/query_clauses.rb +727 -0
- data/lib/active_graph/core/query_ext.rb +24 -0
- data/lib/active_graph/core/query_find_in_batches.rb +46 -0
- data/lib/active_graph/core/record.rb +51 -0
- data/lib/active_graph/core/result.rb +31 -0
- data/lib/active_graph/core/schema.rb +65 -0
- data/lib/active_graph/core/schema_errors.rb +12 -0
- data/lib/active_graph/core/wrappable.rb +30 -0
- data/lib/active_graph/errors.rb +59 -0
- data/lib/active_graph/lazy_attribute_hash.rb +38 -0
- data/lib/active_graph/migration.rb +148 -0
- data/lib/active_graph/migrations.rb +27 -0
- data/lib/active_graph/migrations/base.rb +77 -0
- data/lib/active_graph/migrations/check_pending.rb +20 -0
- data/lib/active_graph/migrations/helpers.rb +105 -0
- data/lib/active_graph/migrations/helpers/id_property.rb +72 -0
- data/lib/active_graph/migrations/helpers/relationships.rb +66 -0
- data/lib/active_graph/migrations/helpers/schema.rb +63 -0
- data/lib/active_graph/migrations/migration_file.rb +24 -0
- data/lib/active_graph/migrations/runner.rb +195 -0
- data/lib/active_graph/migrations/schema.rb +64 -0
- data/lib/active_graph/migrations/schema_migration.rb +14 -0
- data/lib/active_graph/model_schema.rb +139 -0
- data/lib/active_graph/node.rb +110 -0
- data/lib/active_graph/node/callbacks.rb +8 -0
- data/lib/active_graph/node/dependent.rb +11 -0
- data/lib/active_graph/node/dependent/association_methods.rb +49 -0
- data/lib/active_graph/node/dependent/query_proxy_methods.rb +52 -0
- data/lib/active_graph/node/dependent_callbacks.rb +31 -0
- data/lib/active_graph/node/enum.rb +26 -0
- data/lib/active_graph/node/has_n.rb +602 -0
- data/lib/active_graph/node/has_n/association.rb +278 -0
- data/lib/active_graph/node/has_n/association/rel_factory.rb +61 -0
- data/lib/active_graph/node/has_n/association/rel_wrapper.rb +23 -0
- data/lib/active_graph/node/has_n/association_cypher_methods.rb +108 -0
- data/lib/active_graph/node/id_property.rb +224 -0
- data/lib/active_graph/node/id_property/accessor.rb +62 -0
- data/lib/active_graph/node/initialize.rb +21 -0
- data/lib/active_graph/node/labels.rb +207 -0
- data/lib/active_graph/node/labels/index.rb +37 -0
- data/lib/active_graph/node/labels/reloading.rb +21 -0
- data/lib/active_graph/node/node_list_formatter.rb +13 -0
- data/lib/active_graph/node/node_wrapper.rb +54 -0
- data/lib/active_graph/node/orm_adapter.rb +82 -0
- data/lib/active_graph/node/persistence.rb +186 -0
- data/lib/active_graph/node/property.rb +60 -0
- data/lib/active_graph/node/query.rb +76 -0
- data/lib/active_graph/node/query/query_proxy.rb +367 -0
- data/lib/active_graph/node/query/query_proxy_eager_loading.rb +177 -0
- data/lib/active_graph/node/query/query_proxy_eager_loading/association_tree.rb +75 -0
- data/lib/active_graph/node/query/query_proxy_enumerable.rb +110 -0
- data/lib/active_graph/node/query/query_proxy_find_in_batches.rb +19 -0
- data/lib/active_graph/node/query/query_proxy_link.rb +139 -0
- data/lib/active_graph/node/query/query_proxy_methods.rb +303 -0
- data/lib/active_graph/node/query/query_proxy_methods_of_mass_updating.rb +99 -0
- data/lib/active_graph/node/query_methods.rb +68 -0
- data/lib/active_graph/node/reflection.rb +86 -0
- data/lib/active_graph/node/rels.rb +11 -0
- data/lib/active_graph/node/scope.rb +166 -0
- data/lib/active_graph/node/unpersisted.rb +48 -0
- data/lib/active_graph/node/validations.rb +59 -0
- data/lib/active_graph/paginated.rb +27 -0
- data/lib/active_graph/railtie.rb +108 -0
- data/lib/active_graph/relationship.rb +68 -0
- data/lib/active_graph/relationship/callbacks.rb +21 -0
- data/lib/active_graph/relationship/initialize.rb +28 -0
- data/lib/active_graph/relationship/persistence.rb +133 -0
- data/lib/active_graph/relationship/persistence/query_factory.rb +95 -0
- data/lib/active_graph/relationship/property.rb +92 -0
- data/lib/active_graph/relationship/query.rb +99 -0
- data/lib/active_graph/relationship/rel_wrapper.rb +31 -0
- data/lib/active_graph/relationship/related_node.rb +87 -0
- data/lib/active_graph/relationship/types.rb +80 -0
- data/lib/active_graph/relationship/validations.rb +8 -0
- data/lib/active_graph/schema/operation.rb +102 -0
- data/lib/active_graph/shared.rb +48 -0
- data/lib/active_graph/shared/attributes.rb +217 -0
- data/lib/active_graph/shared/callbacks.rb +66 -0
- data/lib/active_graph/shared/cypher.rb +37 -0
- data/lib/active_graph/shared/declared_properties.rb +204 -0
- data/lib/active_graph/shared/declared_property.rb +109 -0
- data/lib/active_graph/shared/declared_property/index.rb +37 -0
- data/lib/active_graph/shared/enum.rb +167 -0
- data/lib/active_graph/shared/filtered_hash.rb +79 -0
- data/lib/active_graph/shared/identity.rb +34 -0
- data/lib/active_graph/shared/initialize.rb +65 -0
- data/lib/active_graph/shared/marshal.rb +23 -0
- data/lib/active_graph/shared/mass_assignment.rb +63 -0
- data/lib/active_graph/shared/permitted_attributes.rb +28 -0
- data/lib/active_graph/shared/persistence.rb +272 -0
- data/lib/active_graph/shared/property.rb +249 -0
- data/lib/active_graph/shared/query_factory.rb +122 -0
- data/lib/active_graph/shared/rel_type_converters.rb +43 -0
- data/lib/active_graph/shared/serialized_properties.rb +30 -0
- data/lib/active_graph/shared/type_converters.rb +439 -0
- data/lib/active_graph/shared/typecasted_attributes.rb +99 -0
- data/lib/active_graph/shared/typecaster.rb +53 -0
- data/lib/active_graph/shared/validations.rb +44 -0
- data/lib/active_graph/tasks/migration.rake +204 -0
- data/lib/active_graph/timestamps.rb +11 -0
- data/lib/active_graph/timestamps/created.rb +9 -0
- data/lib/active_graph/timestamps/updated.rb +9 -0
- data/lib/active_graph/transaction.rb +22 -0
- data/lib/active_graph/transactions.rb +57 -0
- data/lib/active_graph/type_converters.rb +7 -0
- data/lib/active_graph/undeclared_properties.rb +53 -0
- data/lib/active_graph/version.rb +3 -0
- data/lib/active_graph/wrapper.rb +4 -0
- data/lib/rails/generators/active_graph/migration/migration_generator.rb +16 -0
- data/lib/rails/generators/active_graph/migration/templates/migration.erb +9 -0
- data/lib/rails/generators/active_graph/model/model_generator.rb +89 -0
- data/lib/rails/generators/active_graph/model/templates/migration.erb +11 -0
- data/lib/rails/generators/active_graph/model/templates/model.erb +15 -0
- data/lib/rails/generators/active_graph/upgrade_v8/templates/migration.erb +17 -0
- data/lib/rails/generators/active_graph/upgrade_v8/upgrade_v8_generator.rb +34 -0
- data/lib/rails/generators/active_graph_generator.rb +121 -0
- metadata +423 -0
@@ -0,0 +1,195 @@
|
|
1
|
+
module ActiveGraph
|
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
|
+
ActiveGraph::Base.new_query
|
61
|
+
.with('$versions AS versions').params(versions: versions).break
|
62
|
+
.unwind(version: :versions).break
|
63
|
+
.merge('(:`ActiveGraph::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,64 @@
|
|
1
|
+
module ActiveGraph
|
2
|
+
module Migrations
|
3
|
+
module Schema
|
4
|
+
class << self
|
5
|
+
def fetch_schema_data
|
6
|
+
{constraints: fetch_constraint_descriptions.sort,
|
7
|
+
indexes: fetch_index_descriptions.sort}
|
8
|
+
end
|
9
|
+
|
10
|
+
def synchronize_schema_data(schema_data, remove_missing)
|
11
|
+
queries = []
|
12
|
+
queries += drop_and_create_queries(fetch_constraint_descriptions, schema_data[:constraints], remove_missing)
|
13
|
+
queries += drop_and_create_queries(fetch_index_descriptions, schema_data[:indexes], remove_missing)
|
14
|
+
ActiveGraph::Base.write_transaction do
|
15
|
+
queries.each(&ActiveGraph::Base.method(:query))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def fetch_constraint_descriptions
|
22
|
+
ActiveGraph::Base.query('CALL db.constraints() YIELD description').map(&:first)
|
23
|
+
end
|
24
|
+
|
25
|
+
def fetch_index_descriptions
|
26
|
+
result = ActiveGraph::Base.query('CALL db.indexes()')
|
27
|
+
if result.keys.include?(:description)
|
28
|
+
v3_indexes(result)
|
29
|
+
else
|
30
|
+
v4_indexes(result)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def v3_indexes(result)
|
35
|
+
result.reject do |row|
|
36
|
+
# These indexes are created automagically when the corresponding constraints are created
|
37
|
+
row[:type] == 'node_unique_property'
|
38
|
+
end.map { |row| row[:description] }
|
39
|
+
end
|
40
|
+
|
41
|
+
def v4_indexes(result)
|
42
|
+
result.reject do |row|
|
43
|
+
# These indexes are created automagically when the corresponding constraints are created
|
44
|
+
row[:uniqueness] == 'UNIQUE'
|
45
|
+
end.map(&method(:description))
|
46
|
+
end
|
47
|
+
|
48
|
+
def description(row)
|
49
|
+
"INDEX FOR (n:#{row[:labelsOrTypes].first}) ON (#{row[:properties].map { |prop| "n.#{prop}" }.join(', ')})"
|
50
|
+
end
|
51
|
+
|
52
|
+
def drop_and_create_queries(existing, specified, remove_missing)
|
53
|
+
[].tap do |queries|
|
54
|
+
if remove_missing
|
55
|
+
(existing - specified).each { |description| queries << "DROP #{description}" }
|
56
|
+
end
|
57
|
+
|
58
|
+
(specified - existing).each { |description| queries << "CREATE #{description}" }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ActiveGraph
|
2
|
+
module Migrations
|
3
|
+
class SchemaMigration
|
4
|
+
include ActiveGraph::Node
|
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 ActiveGraph
|
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 = ActiveGraph::Base.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 = ActiveGraph::Base.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 != ActiveGraph::Node.loaded_classes.size
|
70
|
+
# Make sure we've finalized id_property details and have called
|
71
|
+
# add_ constraint/index methods above
|
72
|
+
ActiveGraph::Node.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 = ActiveGraph::Node.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 ::ActiveGraph::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
|
+
ActiveGraph::Base.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,110 @@
|
|
1
|
+
module ActiveGraph
|
2
|
+
# Makes Neo4j nodes and relationships behave like ActiveRecord objects.
|
3
|
+
# By including this module in your class it will create a mapping for the node to your ruby class
|
4
|
+
# by using a Neo4j Label with the same name as the class. When the node is loaded from the database it
|
5
|
+
# will check if there is a ruby class for the labels it has.
|
6
|
+
# If there Ruby class with the same name as the label then the Neo4j node will be wrapped
|
7
|
+
# in a new object of that class.
|
8
|
+
#
|
9
|
+
# = ClassMethods
|
10
|
+
# * {ActiveGraph::Node::Labels::ClassMethods} defines methods like: <tt>index</tt> and <tt>find</tt>
|
11
|
+
# * {ActiveGraph::Node::Persistence::ClassMethods} defines methods like: <tt>create</tt> and <tt>create!</tt>
|
12
|
+
# * {ActiveGraph::Node::Property::ClassMethods} defines methods like: <tt>property</tt>.
|
13
|
+
#
|
14
|
+
# @example Create a Ruby wrapper for a Neo4j Node
|
15
|
+
# class Company
|
16
|
+
# include ActiveGraph::Node
|
17
|
+
# property :name
|
18
|
+
# end
|
19
|
+
# company = Company.new
|
20
|
+
# company.name = 'My Company AB'
|
21
|
+
# Company.save
|
22
|
+
#
|
23
|
+
module Node
|
24
|
+
extend ActiveSupport::Concern
|
25
|
+
|
26
|
+
MARSHAL_INSTANCE_VARIABLES = [:@attributes, :@_persisted_obj, :@default_property_value]
|
27
|
+
|
28
|
+
include ActiveGraph::Shared
|
29
|
+
include ActiveGraph::Shared::Identity
|
30
|
+
include ActiveGraph::Shared::Marshal
|
31
|
+
include ActiveGraph::Node::Initialize
|
32
|
+
include ActiveGraph::Node::IdProperty
|
33
|
+
include ActiveGraph::Shared::SerializedProperties
|
34
|
+
include ActiveGraph::Node::Property
|
35
|
+
include ActiveGraph::Node::Reflection
|
36
|
+
include ActiveGraph::Node::Persistence
|
37
|
+
include ActiveGraph::Node::Validations
|
38
|
+
include ActiveGraph::Node::Callbacks
|
39
|
+
include ActiveGraph::Node::Query
|
40
|
+
include ActiveGraph::Node::Labels
|
41
|
+
include ActiveGraph::Node::Rels
|
42
|
+
include ActiveGraph::Node::Unpersisted
|
43
|
+
include ActiveGraph::Node::HasN
|
44
|
+
include ActiveGraph::Node::Scope
|
45
|
+
include ActiveGraph::Node::Dependent
|
46
|
+
include ActiveGraph::Node::Enum
|
47
|
+
include ActiveGraph::Shared::PermittedAttributes
|
48
|
+
include ActiveGraph::Node::DependentCallbacks
|
49
|
+
include ActiveGraph::Transactions
|
50
|
+
|
51
|
+
def initialize(args = nil)
|
52
|
+
self.class.ensure_id_property_info! # So that we make sure all objects have an id_property
|
53
|
+
|
54
|
+
args = sanitize_input_parameters(args)
|
55
|
+
super(args)
|
56
|
+
end
|
57
|
+
|
58
|
+
def neo4j_obj
|
59
|
+
_persisted_obj || fail('Tried to access native neo4j object on a non persisted object')
|
60
|
+
end
|
61
|
+
|
62
|
+
LOADED_CLASSES = []
|
63
|
+
|
64
|
+
def self.loaded_classes
|
65
|
+
LOADED_CLASSES
|
66
|
+
end
|
67
|
+
|
68
|
+
module ClassMethods
|
69
|
+
def nodeify(object)
|
70
|
+
if object.is_a?(::ActiveGraph::Node) || object.nil?
|
71
|
+
object
|
72
|
+
else
|
73
|
+
self.find(object)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
included do
|
79
|
+
include ActiveGraph::Timestamps if ActiveGraph::Config[:record_timestamps]
|
80
|
+
LOADED_CLASSES << self
|
81
|
+
|
82
|
+
def self.inherited?
|
83
|
+
!!@inherited
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.inherited(other)
|
87
|
+
ActiveGraph::Node::Labels.clear_wrapped_models
|
88
|
+
|
89
|
+
LOADED_CLASSES << other
|
90
|
+
other.instance_variable_set('@inherited', true)
|
91
|
+
inherit_id_property(other)
|
92
|
+
attributes.each_pair do |k, v|
|
93
|
+
other.inherit_property k.to_sym, v.clone, declared_properties[k].options
|
94
|
+
end
|
95
|
+
|
96
|
+
ActiveGraph::Node::Labels.add_wrapped_class(other)
|
97
|
+
super
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.inherit_id_property(other)
|
101
|
+
return if other.manual_id_property? || !self.id_property?
|
102
|
+
id_prop = self.id_property_info
|
103
|
+
conf = id_prop[:type].empty? && id_prop[:name] != :neo_id ? {auto: :uuid} : id_prop[:type]
|
104
|
+
other.id_property id_prop[:name], conf, true
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
ActiveSupport.run_load_hooks(:node, self)
|
109
|
+
end
|
110
|
+
end
|