activegraph 10.0.0.pre.alpha.6
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 +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,98 @@
|
|
|
1
|
+
module Neo4j::Shared
|
|
2
|
+
# TypecastedAttributes allows types to be declared for your attributes
|
|
3
|
+
#
|
|
4
|
+
# Types are declared by passing the :type option to the attribute class
|
|
5
|
+
# method. After a type is declared, attribute readers will convert any
|
|
6
|
+
# assigned attribute value to the declared type. If the assigned value
|
|
7
|
+
# cannot be cast, nil will be returned instead. You can access the original
|
|
8
|
+
# assigned value using the before_type_cast methods.
|
|
9
|
+
#
|
|
10
|
+
# See {Typecasting} for the currently supported types.
|
|
11
|
+
#
|
|
12
|
+
# @example Usage
|
|
13
|
+
# class Person
|
|
14
|
+
# include Neo4j::Shared::TypecastedAttributes
|
|
15
|
+
# attribute :age, :type => Integer
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# person = Person.new
|
|
19
|
+
# person.age = "29"
|
|
20
|
+
# person.age #=> 29
|
|
21
|
+
# person.age_before_type_cast #=> "29"
|
|
22
|
+
#
|
|
23
|
+
# Originally part of ActiveAttr, https://github.com/cgriego/active_attr
|
|
24
|
+
module TypecastedAttributes
|
|
25
|
+
extend ActiveSupport::Concern
|
|
26
|
+
include Neo4j::Shared::Attributes
|
|
27
|
+
|
|
28
|
+
included do
|
|
29
|
+
attribute_method_suffix '_before_type_cast'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Read the raw attribute value
|
|
33
|
+
#
|
|
34
|
+
# @example Reading a raw age value
|
|
35
|
+
# person.age = "29"
|
|
36
|
+
# person.attribute_before_type_cast(:age) #=> "29"
|
|
37
|
+
#
|
|
38
|
+
# @param [String, Symbol, #to_s] name Attribute name
|
|
39
|
+
#
|
|
40
|
+
# @return [Object, nil] The attribute value before typecasting
|
|
41
|
+
def attribute_before_type_cast(name)
|
|
42
|
+
@attributes ||= {}
|
|
43
|
+
@attributes[name.to_s]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
# Reads the attribute and typecasts the result
|
|
49
|
+
def attribute(name)
|
|
50
|
+
typecast_attribute(_attribute_typecaster(name), super)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def typecast_attribute(typecaster, value)
|
|
54
|
+
self.class.typecast_attribute(typecaster, value)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Calculates an attribute type
|
|
58
|
+
#
|
|
59
|
+
# @private
|
|
60
|
+
def _attribute_type(attribute_name)
|
|
61
|
+
self.class._attribute_type(attribute_name)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Resolve an attribute typecaster
|
|
65
|
+
#
|
|
66
|
+
# @private
|
|
67
|
+
def _attribute_typecaster(attribute_name)
|
|
68
|
+
type = _attribute_type(attribute_name)
|
|
69
|
+
caster = self.class.attributes[attribute_name].typecaster || Neo4j::Shared::TypeConverters.typecaster_for(type)
|
|
70
|
+
caster || fail(Neo4j::UnknownTypeConverterError, "Unable to cast to type #{type}")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
module ClassMethods
|
|
74
|
+
# Returns the class name plus its attribute names and types
|
|
75
|
+
#
|
|
76
|
+
# @example Inspect the model's definition.
|
|
77
|
+
# Person.inspect
|
|
78
|
+
#
|
|
79
|
+
# @return [String] Human-readable presentation of the attributes
|
|
80
|
+
def inspect
|
|
81
|
+
inspected_attributes = attribute_names.sort.map { |name| "#{name}: #{_attribute_type(name)}" }
|
|
82
|
+
attributes_list = "(#{inspected_attributes.join(', ')})" unless inspected_attributes.empty?
|
|
83
|
+
"#{name}#{attributes_list}"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Calculates an attribute type
|
|
87
|
+
#
|
|
88
|
+
# @private
|
|
89
|
+
def _attribute_type(attribute_name)
|
|
90
|
+
attributes[attribute_name].type || Object
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def typecast_attribute(typecaster, value)
|
|
94
|
+
Neo4j::Shared::TypeConverters.typecast_attribute(typecaster, value)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module Shared
|
|
3
|
+
# This module provides a convenient way of registering a custom Typecasting class. Custom Typecasters all follow a simple pattern.
|
|
4
|
+
#
|
|
5
|
+
# EXAMPLE:
|
|
6
|
+
#
|
|
7
|
+
# .. code-block:: ruby
|
|
8
|
+
#
|
|
9
|
+
# class RangeConverter
|
|
10
|
+
# class << self
|
|
11
|
+
# def primitive_type
|
|
12
|
+
# String
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# def convert_type
|
|
16
|
+
# Range
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# def to_db(value)
|
|
20
|
+
# value.to_s
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# def to_ruby(value)
|
|
24
|
+
# ends = value.to_s.split('..').map { |d| Integer(d) }
|
|
25
|
+
# ends[0]..ends[1]
|
|
26
|
+
# end
|
|
27
|
+
# alias_method :call, :to_ruby
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# include Neo4j::Shared::Typecaster
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# This would allow you to use `property :my_prop, type: Range` in a model.
|
|
34
|
+
# Each method and the `alias_method` call is required. Make sure the module inclusion happens at the end of the file.
|
|
35
|
+
#
|
|
36
|
+
# `primitive_type` is used to fool ActiveAttr's type converters, which only recognize a few basic Ruby classes.
|
|
37
|
+
#
|
|
38
|
+
# `convert_type` must match the constant given to the `type` option.
|
|
39
|
+
#
|
|
40
|
+
# `to_db` provides logic required to transform your value into the class defined by `primitive_type`
|
|
41
|
+
#
|
|
42
|
+
# `to_ruby` provides logic to transform the DB-provided value back into the class expected by code using the property.
|
|
43
|
+
# In other words, it should match the `convert_type`.
|
|
44
|
+
#
|
|
45
|
+
# Note that `alias_method` is used to make `to_ruby` respond to `call`. This is to provide compatibility with ActiveAttr.
|
|
46
|
+
|
|
47
|
+
module Typecaster
|
|
48
|
+
def self.included(other)
|
|
49
|
+
Neo4j::Shared::TypeConverters.register_converter(other)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module Shared
|
|
3
|
+
module Validations
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
include ActiveModel::Validations
|
|
6
|
+
# Implements the ActiveModel::Validation hook method.
|
|
7
|
+
# @see http://rubydoc.info/docs/rails/ActiveModel/Validations:read_attribute_for_validation
|
|
8
|
+
def read_attribute_for_validation(key)
|
|
9
|
+
respond_to?(key) ? send(key) : self[key]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# The validation process on save can be skipped by passing false. The regular Model#save method is
|
|
13
|
+
# replaced with this when the validations module is mixed in, which it is by default.
|
|
14
|
+
# @param [Hash] options the options to create a message with.
|
|
15
|
+
# @option options [true, false] :validate if false no validation will take place
|
|
16
|
+
# @return [Boolean] true if it saved it successfully
|
|
17
|
+
def save(options = {})
|
|
18
|
+
perform_validations(options) ? super : false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @return [Boolean] true if valid
|
|
22
|
+
def valid?(context = nil)
|
|
23
|
+
context ||= (new_record? ? :create : :update)
|
|
24
|
+
super(context)
|
|
25
|
+
errors.empty?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def perform_validations(options = {})
|
|
31
|
+
perform_validation = case options
|
|
32
|
+
when Hash
|
|
33
|
+
options[:validate] != false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if perform_validation
|
|
37
|
+
valid?(options.is_a?(Hash) ? options[:context] : nil)
|
|
38
|
+
else
|
|
39
|
+
true
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
require 'rake'
|
|
2
|
+
require 'active_support/concern'
|
|
3
|
+
require 'neo4j/migration'
|
|
4
|
+
|
|
5
|
+
if !defined?(Rails) && !Rake::Task.task_defined?('environment')
|
|
6
|
+
desc 'Run a script against the database to perform system-wide changes'
|
|
7
|
+
task :environment do
|
|
8
|
+
require 'neo4j/session_manager'
|
|
9
|
+
require 'ostruct'
|
|
10
|
+
neo4j_url = ENV['NEO4J_URL'] || 'http://localhost:7474'
|
|
11
|
+
$LOAD_PATH.unshift File.dirname('./')
|
|
12
|
+
Neo4j::ActiveBase.on_establish_session do
|
|
13
|
+
Neo4j::ActiveBase.new_driver(neo4j_url)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
namespace :neo4j do
|
|
19
|
+
task :allow_migrations do
|
|
20
|
+
Neo4j::Migrations.currently_running_migrations = true
|
|
21
|
+
end
|
|
22
|
+
desc 'Run a script against the database to perform system-wide changes'
|
|
23
|
+
task :legacy_migrate, [:task_name, :subtask] => :environment do |_, args|
|
|
24
|
+
path = Rake.original_dir
|
|
25
|
+
migration_task = args[:task_name]
|
|
26
|
+
task_name_constant = migration_task.split('_').map(&:capitalize).join('')
|
|
27
|
+
begin
|
|
28
|
+
migration_class = "Neo4j::Migration::#{task_name_constant}".constantize
|
|
29
|
+
rescue NameError
|
|
30
|
+
load File.join(path, 'db', 'neo4j-migrate', "#{migration_task}.rb")
|
|
31
|
+
migration_class = task_name_constant.to_s.constantize
|
|
32
|
+
end
|
|
33
|
+
migration = migration_class.new(path)
|
|
34
|
+
|
|
35
|
+
subtask = args[:subtask]
|
|
36
|
+
if subtask
|
|
37
|
+
migration.send(subtask)
|
|
38
|
+
else
|
|
39
|
+
migration.migrate
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
desc 'A shortcut for neo4j::migrate::all'
|
|
44
|
+
task :migrate do
|
|
45
|
+
Rake::Task['neo4j:migrate:all'].invoke
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# TODO: Make sure these tasks don't run in versions of Neo4j before 3.0
|
|
49
|
+
namespace :schema do
|
|
50
|
+
SCHEMA_YAML_PATH = 'db/neo4j/schema.yml'
|
|
51
|
+
SCHEMA_YAML_COMMENT = <<COMMENT
|
|
52
|
+
# This file is auto-generated from the current state of the database. Instead
|
|
53
|
+
# of editing this file, please use the migrations feature of ActiveNode to
|
|
54
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
|
55
|
+
#
|
|
56
|
+
# Note that this schema.yml definition is the authoritative source for your
|
|
57
|
+
# database schema. If you need to create the application database on another
|
|
58
|
+
# system, you should be using neo4j:schema:load, not running all the migrations
|
|
59
|
+
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
|
60
|
+
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
|
61
|
+
#
|
|
62
|
+
# It's strongly recommended that you check this file into your version control system.
|
|
63
|
+
|
|
64
|
+
COMMENT
|
|
65
|
+
|
|
66
|
+
def check_neo4j_version_3
|
|
67
|
+
if Neo4j::ActiveBase.transaction.version > '3.0.0'
|
|
68
|
+
yield
|
|
69
|
+
else
|
|
70
|
+
puts 'WARNING: This task does not work for versions of Neo4j before 3.0.0'
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
desc 'Creates a db/neo4j/schema.yml file which represents the indexes / constraints in the Neo4j DB'
|
|
75
|
+
task dump: :environment do
|
|
76
|
+
check_neo4j_version_3 do
|
|
77
|
+
require 'neo4j/migrations/schema'
|
|
78
|
+
|
|
79
|
+
schema_data = Neo4j::Migrations::Schema.fetch_schema_data(Neo4j::ActiveBase.transaction)
|
|
80
|
+
|
|
81
|
+
runner = Neo4j::Migrations::Runner.new
|
|
82
|
+
schema_data[:versions] = runner.complete_migration_versions.sort
|
|
83
|
+
|
|
84
|
+
FileUtils.mkdir_p(File.dirname(SCHEMA_YAML_PATH))
|
|
85
|
+
File.open(SCHEMA_YAML_PATH, 'w') { |file| file << SCHEMA_YAML_COMMENT + schema_data.to_yaml }
|
|
86
|
+
|
|
87
|
+
puts "Dumped updated schema file to #{SCHEMA_YAML_PATH}"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
desc "Loads a db/neo4j/schema.yml file into the database\nOptionally removes schema elements which aren't in the schema.yml file (defaults to false)"
|
|
92
|
+
task :load, [:remove_missing] => :environment do |_t, args|
|
|
93
|
+
check_neo4j_version_3 do
|
|
94
|
+
require 'neo4j/migrations/schema'
|
|
95
|
+
|
|
96
|
+
args.with_defaults(remove_missing: false)
|
|
97
|
+
|
|
98
|
+
schema_data = YAML.safe_load(File.read(SCHEMA_YAML_PATH), [Symbol])
|
|
99
|
+
|
|
100
|
+
Neo4j::Core::CypherSession::Adaptors::Base.subscribe_to_query(&method(:puts))
|
|
101
|
+
|
|
102
|
+
Neo4j::ActiveBase.run_transaction do
|
|
103
|
+
Neo4j::Migrations::Schema.synchronize_schema_data(Neo4j::ActiveBase.transaction, schema_data, args[:remove_missing])
|
|
104
|
+
|
|
105
|
+
runner = Neo4j::Migrations::Runner.new
|
|
106
|
+
runner.mark_versions_as_complete(schema_data[:versions]) # Run in test mode?
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
namespace :migrate do
|
|
113
|
+
desc 'Run all pending migrations'
|
|
114
|
+
task all: [:allow_migrations, :environment] do
|
|
115
|
+
runner = Neo4j::Migrations::Runner.new
|
|
116
|
+
runner.all
|
|
117
|
+
|
|
118
|
+
Rake::Task['neo4j:schema:dump'].invoke
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
desc 'Run a migration given its VERSION'
|
|
122
|
+
task up: [:allow_migrations, :environment] do
|
|
123
|
+
version = ENV['VERSION'] || fail(ArgumentError, 'VERSION is required')
|
|
124
|
+
runner = Neo4j::Migrations::Runner.new
|
|
125
|
+
runner.up version
|
|
126
|
+
|
|
127
|
+
Rake::Task['neo4j:schema:dump'].invoke
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
desc 'Revert a migration given its VERSION'
|
|
131
|
+
task down: [:allow_migrations, :environment] do
|
|
132
|
+
version = ENV['VERSION'] || fail(ArgumentError, 'VERSION is required')
|
|
133
|
+
runner = Neo4j::Migrations::Runner.new
|
|
134
|
+
runner.down version
|
|
135
|
+
|
|
136
|
+
Rake::Task['neo4j:schema:dump'].invoke
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
desc 'Print a report of migrations status'
|
|
140
|
+
task status: [:allow_migrations, :environment] do
|
|
141
|
+
runner = Neo4j::Migrations::Runner.new
|
|
142
|
+
runner.status
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
desc 'Resolve an incomplete version state'
|
|
146
|
+
task resolve: [:allow_migrations, :environment] do
|
|
147
|
+
version = ENV['VERSION'] || fail(ArgumentError, 'VERSION is required')
|
|
148
|
+
runner = Neo4j::Migrations::Runner.new
|
|
149
|
+
runner.resolve version
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
desc 'Resolve an incomplete version state'
|
|
153
|
+
task reset: [:allow_migrations, :environment] do
|
|
154
|
+
version = ENV['VERSION'] || fail(ArgumentError, 'VERSION is required')
|
|
155
|
+
runner = Neo4j::Migrations::Runner.new
|
|
156
|
+
runner.reset version
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
desc 'Rollbacks migrations given a STEP number'
|
|
161
|
+
task rollback: [:allow_migrations, :environment] do
|
|
162
|
+
steps = (ENV['STEP'] || 1).to_i
|
|
163
|
+
runner = Neo4j::Migrations::Runner.new
|
|
164
|
+
runner.rollback(steps)
|
|
165
|
+
|
|
166
|
+
Rake::Task['neo4j:schema:dump'].invoke
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Temporary to help people change to 8.0
|
|
170
|
+
desc 'Generates a migration for the specified constraint/index and label/property combination.'
|
|
171
|
+
task :generate_schema_migration, :index_or_constraint, :label, :property_name do |_t, args|
|
|
172
|
+
index_or_constraint, label, property_name = args.values_at(:index_or_constraint, :label, :property_name)
|
|
173
|
+
|
|
174
|
+
if !%w(index constraint).include?(index_or_constraint)
|
|
175
|
+
fail "Invalid schema element type: #{index_or_constraint} (should be either `index` or `constraint`)"
|
|
176
|
+
end
|
|
177
|
+
fail 'Label must be specified' if label.blank?
|
|
178
|
+
fail 'Property name must be specified' if property_name.blank?
|
|
179
|
+
|
|
180
|
+
migration_class_name = "ForceCreate#{label.camelize}#{property_name.camelize}#{index_or_constraint.capitalize}".gsub('::', '').underscore.camelize
|
|
181
|
+
|
|
182
|
+
require 'fileutils'
|
|
183
|
+
FileUtils.mkdir_p('db/neo4j/migrate')
|
|
184
|
+
path = File.join('db/neo4j/migrate', "#{DateTime.now.utc.strftime('%Y%m%d%H%M%S')}_#{migration_class_name.underscore}.rb")
|
|
185
|
+
|
|
186
|
+
content = <<-CONTENT
|
|
187
|
+
class #{migration_class_name} < Neo4j::Migrations::Base
|
|
188
|
+
def up
|
|
189
|
+
add_#{index_or_constraint} #{label.to_sym.inspect}, #{property_name.to_sym.inspect}, force: true
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def down
|
|
193
|
+
drop_#{index_or_constraint} #{label.to_sym.inspect}, #{property_name.to_sym.inspect}
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
CONTENT
|
|
197
|
+
|
|
198
|
+
File.open(path, 'w') { |f| f << content }
|
|
199
|
+
|
|
200
|
+
puts "Generated #{path}"
|
|
201
|
+
end
|
|
202
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
|
2
|
+
require 'active_support/core_ext/module/attribute_accessors_per_thread'
|
|
3
|
+
require 'neo4j/core/querable'
|
|
4
|
+
require 'neo4j/core/schema'
|
|
5
|
+
|
|
6
|
+
module Neo4j
|
|
7
|
+
class Transaction
|
|
8
|
+
include Neo4j::Core::Querable
|
|
9
|
+
extend Neo4j::Core::Schema
|
|
10
|
+
|
|
11
|
+
thread_mattr_accessor :stack
|
|
12
|
+
attr_reader :root
|
|
13
|
+
attr_reader :driver_tx, :driver_session
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
# Runs the given block in a new transaction.
|
|
17
|
+
# @param [Boolean] run_in_tx if true a new transaction will not be created, instead if will simply yield to the given block
|
|
18
|
+
# @@yield [Neo4j::Transaction::Instance]
|
|
19
|
+
def run(driver, run_in_tx)
|
|
20
|
+
return yield(nil) unless run_in_tx
|
|
21
|
+
|
|
22
|
+
tx = Neo4j::Transaction.new
|
|
23
|
+
yield tx
|
|
24
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
25
|
+
tx.mark_failed unless tx.nil?
|
|
26
|
+
raise e
|
|
27
|
+
ensure
|
|
28
|
+
tx.close unless tx.nil?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def root
|
|
32
|
+
initialized_stack.first
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def initialized_stack
|
|
36
|
+
self.stack ||= []
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def initialize(_options = {})
|
|
41
|
+
self.class.initialized_stack << self
|
|
42
|
+
@root = stack.first
|
|
43
|
+
return unless root?
|
|
44
|
+
@driver_session = ActiveBase.current_driver.driver.session(Neo4j::Driver::AccessMode::WRITE)
|
|
45
|
+
@driver_tx = @driver_session.begin_transaction
|
|
46
|
+
rescue StandardError => e
|
|
47
|
+
self.stack = []
|
|
48
|
+
@driver_tx.close if @driver_tx
|
|
49
|
+
@driver_session.close if @driver_session
|
|
50
|
+
raise e
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Commits or marks this transaction for rollback, depending on whether #mark_failed has been previously invoked.
|
|
54
|
+
def close
|
|
55
|
+
fail 'Tried closing when transaction stack is empty (maybe you closed too many?)' if stack.empty?
|
|
56
|
+
fail "Closed transaction which wasn't the most recent on the stack (maybe you forgot to close one?)" if stack.pop != self
|
|
57
|
+
|
|
58
|
+
post_close! if stack.empty?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Marks this transaction as failed,
|
|
62
|
+
# which means that it will unconditionally be rolled back
|
|
63
|
+
# when #close is called.
|
|
64
|
+
# Aliased for legacy purposes.
|
|
65
|
+
def mark_failed
|
|
66
|
+
root.mark_failed if root && root != self
|
|
67
|
+
@failure = true
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
alias failure mark_failed
|
|
71
|
+
|
|
72
|
+
# If it has been marked as failed.
|
|
73
|
+
# Aliased for legacy purposes.
|
|
74
|
+
def failed?
|
|
75
|
+
!!@failure
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
alias failure? failed?
|
|
79
|
+
|
|
80
|
+
def root?
|
|
81
|
+
@root == self
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def query(*args)
|
|
85
|
+
options = if args[0].is_a?(::Neo4j::Core::Query)
|
|
86
|
+
args[1] ||= {}
|
|
87
|
+
else
|
|
88
|
+
args[1] ||= {}
|
|
89
|
+
args[2] ||= {}
|
|
90
|
+
end
|
|
91
|
+
options[:transaction] ||= self
|
|
92
|
+
|
|
93
|
+
self.class.query(*args)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def queries(options = {}, &block)
|
|
97
|
+
self.class.queries({ transaction: self }.merge(options), &block)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def after_commit_registry
|
|
101
|
+
@after_commit_registry ||= []
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def after_commit(&block)
|
|
105
|
+
after_commit_registry << block
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def commit
|
|
109
|
+
return unless root?
|
|
110
|
+
begin
|
|
111
|
+
@driver_tx.success
|
|
112
|
+
@driver_tx.close
|
|
113
|
+
ensure
|
|
114
|
+
@driver_session.close
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def delete
|
|
119
|
+
root.driver_tx.failure
|
|
120
|
+
root.driver_tx.close
|
|
121
|
+
root.driver_session.close
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def root_tx
|
|
125
|
+
root.driver_tx
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
def post_close!
|
|
131
|
+
if failed?
|
|
132
|
+
delete
|
|
133
|
+
else
|
|
134
|
+
commit
|
|
135
|
+
after_commit_registry.each(&:call)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|