activegraph 11.0.0.beta.1-java
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 +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,59 @@
|
|
|
1
|
+
module ActiveGraph
|
|
2
|
+
module Node
|
|
3
|
+
# This mixin replace the original save method and performs validation before the save.
|
|
4
|
+
module Validations
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
include ActiveGraph::Shared::Validations
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# @return [Boolean] true if valid
|
|
10
|
+
def valid?(context = nil)
|
|
11
|
+
context ||= (new_record? ? :create : :update)
|
|
12
|
+
super(context)
|
|
13
|
+
errors.empty?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module ClassMethods
|
|
17
|
+
def validates_uniqueness_of(*attr_names)
|
|
18
|
+
validates_with UniquenessValidator, _merge_attributes(attr_names)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class UniquenessValidator < ::ActiveModel::EachValidator
|
|
24
|
+
def initialize(options)
|
|
25
|
+
super(options.reverse_merge(case_sensitive: true))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def validate_each(record, attribute, value)
|
|
29
|
+
return unless found(record, attribute, value).exists?
|
|
30
|
+
|
|
31
|
+
record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(value: value))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def found(record, attribute, value)
|
|
35
|
+
conditions = scope_conditions(record)
|
|
36
|
+
|
|
37
|
+
# TODO: Added as find(:name => nil) throws error
|
|
38
|
+
value = '' if value.nil?
|
|
39
|
+
|
|
40
|
+
conditions[attribute] = options[:case_sensitive] ? value : /#{Regexp.escape(value.to_s)}/i
|
|
41
|
+
|
|
42
|
+
found = record.class.as(:result).where(conditions)
|
|
43
|
+
found = found.where_not(neo_id: record.neo_id) if record._persisted_obj
|
|
44
|
+
found
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def message(instance)
|
|
48
|
+
super || 'has already been taken'
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def scope_conditions(instance)
|
|
52
|
+
Array(options[:scope] || []).inject({}) do |conditions, key|
|
|
53
|
+
conditions.merge(key => instance[key])
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module ActiveGraph
|
|
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,108 @@
|
|
|
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 'active_graph'
|
|
6
|
+
|
|
7
|
+
module ActiveGraph
|
|
8
|
+
class Railtie < ::Rails::Railtie
|
|
9
|
+
def empty_config
|
|
10
|
+
ActiveSupport::OrderedOptions.new.tap do |cfg|
|
|
11
|
+
cfg.driver = ActiveSupport::OrderedOptions.new
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
config.neo4j = empty_config
|
|
16
|
+
|
|
17
|
+
if defined?(ActiveSupport::Reloader)
|
|
18
|
+
ActiveSupport::Reloader.to_prepare do
|
|
19
|
+
ActiveGraph::Node::Labels::Reloading.reload_models!
|
|
20
|
+
end
|
|
21
|
+
elsif const_defined?(:ActionDispatch)
|
|
22
|
+
ActionDispatch::Reloader.to_prepare do
|
|
23
|
+
ActiveGraph::Node::Labels::Reloading.reload_models!
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Rescue responses similar to ActiveRecord.
|
|
28
|
+
config.action_dispatch.rescue_responses.merge!(
|
|
29
|
+
'ActiveGraph::RecordNotFound' => :not_found,
|
|
30
|
+
'ActiveGraph::Node::Labels::RecordNotFound' => :not_found
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Add ActiveModel translations to the I18n load_path
|
|
34
|
+
initializer 'i18n' do
|
|
35
|
+
config.i18n.load_path += Dir[File.join(File.dirname(__FILE__), '..', '..', '..', 'config', 'locales', '*.{rb,yml}')]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
console do
|
|
39
|
+
ActiveGraph::Config[:logger] = ActiveSupport::Logger.new(STDOUT)
|
|
40
|
+
ActiveGraph::Config[:verbose_query_logs] = false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Starting Neo after :load_config_initializers allows apps to
|
|
44
|
+
# register migrations in config/initializers
|
|
45
|
+
initializer 'neo4j.start', after: :load_config_initializers do |app|
|
|
46
|
+
app.config.neo4j.skip_migration_check = true if Rails.env.test?
|
|
47
|
+
|
|
48
|
+
neo4j_config = ActiveSupport::OrderedOptions.new
|
|
49
|
+
app.config.neo4j.each { |k, v| neo4j_config[k] = v } if app.config.neo4j
|
|
50
|
+
|
|
51
|
+
ActiveGraph::Config.configuration.merge!(neo4j_config.to_h)
|
|
52
|
+
|
|
53
|
+
ActiveGraph::Base.on_establish_driver { setup! neo4j_config }
|
|
54
|
+
|
|
55
|
+
ActiveGraph::Config[:logger] ||= Rails.logger
|
|
56
|
+
|
|
57
|
+
if ActiveGraph::Config.fail_on_pending_migrations
|
|
58
|
+
config.app_middleware.insert_after ::ActionDispatch::Callbacks, ActiveGraph::Migrations::CheckPending
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def setup!(config = empty_config)
|
|
63
|
+
config = final_driver_config!(config)
|
|
64
|
+
scheme = config.delete(:scheme) || 'bolt'
|
|
65
|
+
host = config.delete(:host) || 'localhost'
|
|
66
|
+
port = config.delete(:port) || 7687
|
|
67
|
+
url = config.delete(:url) || URI::Generic.build( scheme: scheme, host: host, port: port ).to_s
|
|
68
|
+
username = config.delete(:username)
|
|
69
|
+
password = config.delete(:password)
|
|
70
|
+
auth_token = config.delete(:auth_token)
|
|
71
|
+
auth_token ||= username ? Neo4j::Driver::AuthTokens.basic(username, password) : Neo4j::Driver::AuthTokens.none
|
|
72
|
+
register_neo4j_cypher_logging
|
|
73
|
+
|
|
74
|
+
method = url.is_a?(Enumerable) ? :routing_driver : :driver
|
|
75
|
+
Neo4j::Driver::GraphDatabase.send(method, url, auth_token, config)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def final_driver_config!(config)
|
|
79
|
+
{ url: ENV['NEO4J_URL'] }.compact.merge(config[:driver]).merge(yaml_config_data)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def yaml_config_data
|
|
83
|
+
@yaml_config_data ||=
|
|
84
|
+
yaml_path ? YAML.load(ERB.new(yaml_path.read).result)[Rails.env].transform_keys!(&:to_sym) : {}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def yaml_path
|
|
88
|
+
return unless defined?(Rails)
|
|
89
|
+
@yaml_path ||= %w(config/neo4j.yml config/neo4j.yaml).map do |path|
|
|
90
|
+
Rails.root.join(path)
|
|
91
|
+
end.detect(&:exist?)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def register_neo4j_cypher_logging
|
|
95
|
+
return if @neo4j_cypher_logging_registered
|
|
96
|
+
|
|
97
|
+
ActiveGraph::Core::Query.pretty_cypher = ActiveGraph::Config[:pretty_logged_cypher_queries]
|
|
98
|
+
|
|
99
|
+
logger_proc = ->(message) do
|
|
100
|
+
(ActiveGraph::Config[:logger] ||= Rails.logger).debug message
|
|
101
|
+
end
|
|
102
|
+
ActiveGraph::Base.subscribe_to_query(&logger_proc)
|
|
103
|
+
ActiveGraph::Base.subscribe_to_request(&logger_proc)
|
|
104
|
+
|
|
105
|
+
@neo4j_cypher_logging_registered = true
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module ActiveGraph
|
|
2
|
+
# Makes Neo4j Relationships more or less act like ActiveRecord objects.
|
|
3
|
+
# See documentation at https://github.com/neo4jrb/neo4j/wiki/Neo4j%3A%3AActiveRel
|
|
4
|
+
module Relationship
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
MARSHAL_INSTANCE_VARIABLES = [:@attributes, :@type, :@_persisted_obj]
|
|
8
|
+
|
|
9
|
+
include ActiveGraph::Shared
|
|
10
|
+
include ActiveGraph::Relationship::Initialize
|
|
11
|
+
include ActiveGraph::Shared::Identity
|
|
12
|
+
include ActiveGraph::Shared::Marshal
|
|
13
|
+
include ActiveGraph::Shared::SerializedProperties
|
|
14
|
+
include ActiveGraph::Relationship::Property
|
|
15
|
+
include ActiveGraph::Relationship::Persistence
|
|
16
|
+
include ActiveGraph::Relationship::Validations
|
|
17
|
+
include ActiveGraph::Relationship::Callbacks
|
|
18
|
+
include ActiveGraph::Relationship::Query
|
|
19
|
+
include ActiveGraph::Relationship::Types
|
|
20
|
+
include ActiveGraph::Shared::Enum
|
|
21
|
+
include ActiveGraph::Shared::PermittedAttributes
|
|
22
|
+
include ActiveGraph::Transactions
|
|
23
|
+
|
|
24
|
+
class FrozenRelError < ActiveGraph::Error; end
|
|
25
|
+
|
|
26
|
+
def initialize(from_node = nil, to_node = nil, args = nil)
|
|
27
|
+
load_nodes(node_or_nil(from_node), node_or_nil(to_node))
|
|
28
|
+
resolved_args = hash_or_nil(from_node, args)
|
|
29
|
+
symbol_args = sanitize_input_parameters(resolved_args)
|
|
30
|
+
super(symbol_args)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def node_cypher_representation(node)
|
|
34
|
+
node_class = node.class
|
|
35
|
+
id_name = node_class.id_property_name
|
|
36
|
+
labels = ':' + node_class.mapped_label_names.join(':')
|
|
37
|
+
|
|
38
|
+
"(#{labels} {#{id_name}: #{node.id.inspect}})"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def neo4j_obj
|
|
42
|
+
_persisted_obj || fail('Tried to access native neo4j object on a non persisted object')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
included do
|
|
46
|
+
include ActiveGraph::Timestamps if ActiveGraph::Config[:record_timestamps]
|
|
47
|
+
|
|
48
|
+
def self.inherited(other)
|
|
49
|
+
attributes.each_pair do |k, v|
|
|
50
|
+
other.inherit_property k.to_sym, v.clone, declared_properties[k].options
|
|
51
|
+
end
|
|
52
|
+
super
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
ActiveSupport.run_load_hooks(:relationship, self)
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def node_or_nil(node)
|
|
61
|
+
node.is_a?(ActiveGraph::Node) || node.is_a?(Integer) ? node : nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def hash_or_nil(node_or_hash, hash_or_nil)
|
|
65
|
+
hash_or_parameter?(node_or_hash) ? node_or_hash : hash_or_nil
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module ActiveGraph
|
|
2
|
+
module Relationship
|
|
3
|
+
module Callbacks #:nodoc:
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
include ActiveGraph::Shared::Callbacks
|
|
6
|
+
|
|
7
|
+
def save(*args)
|
|
8
|
+
unless _persisted_obj || (from_node.respond_to?(:neo_id) && to_node.respond_to?(:neo_id))
|
|
9
|
+
fail ActiveGraph::Relationship::Persistence::RelInvalidError, 'from_node and to_node must be node objects'
|
|
10
|
+
end
|
|
11
|
+
super(*args)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def destroy
|
|
15
|
+
to_node.callbacks_from_relationship(self, :in, from_node).try(:last)
|
|
16
|
+
from_node.callbacks_from_relationship(self, :out, to_node).try(:last)
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module ActiveGraph::Relationship
|
|
2
|
+
module Initialize
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
include ActiveGraph::Shared::Initialize
|
|
5
|
+
|
|
6
|
+
# called when loading the rel from the database
|
|
7
|
+
# @param [ActiveGraph::Embedded::EmbeddedRelationship, Neo4j::Server::CypherRelationship] persisted_rel properties of this relationship
|
|
8
|
+
# @param [ActiveGraph::Relationship] from_node_id The neo_id of the starting node of this rel
|
|
9
|
+
# @param [ActiveGraph::Relationship] to_node_id The neo_id of the ending node of this rel
|
|
10
|
+
# @param [String] type the relationship type
|
|
11
|
+
def init_on_load(persisted_rel, from_node_id, to_node_id, type)
|
|
12
|
+
@type = type
|
|
13
|
+
@_persisted_obj = persisted_rel
|
|
14
|
+
changed_attributes_clear!
|
|
15
|
+
@attributes = convert_and_assign_attributes(persisted_rel.properties)
|
|
16
|
+
load_nodes(from_node_id, to_node_id)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def init_on_reload(unwrapped_reloaded)
|
|
20
|
+
@attributes = nil
|
|
21
|
+
init_on_load(unwrapped_reloaded,
|
|
22
|
+
unwrapped_reloaded.start_node_id,
|
|
23
|
+
unwrapped_reloaded.end_node_id,
|
|
24
|
+
unwrapped_reloaded.type)
|
|
25
|
+
self
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
module ActiveGraph::Relationship
|
|
2
|
+
module Persistence
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
include ActiveGraph::Shared::Cypher::RelIdentifiers
|
|
5
|
+
include ActiveGraph::Shared::Persistence
|
|
6
|
+
|
|
7
|
+
class RelInvalidError < RuntimeError; end
|
|
8
|
+
class ModelClassInvalidError < RuntimeError; end
|
|
9
|
+
class RelCreateFailedError < RuntimeError; end
|
|
10
|
+
|
|
11
|
+
def from_node_identifier
|
|
12
|
+
@from_node_identifier || :from_node
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_node_identifier
|
|
16
|
+
@to_node_identifier || :to_node
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def from_node_identifier=(id)
|
|
20
|
+
@from_node_identifier = id.to_sym
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_node_identifier=(id)
|
|
24
|
+
@to_node_identifier = id.to_sym
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def cypher_identifier
|
|
28
|
+
@cypher_identifier || :rel
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def save(*)
|
|
32
|
+
create_or_update
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def save!(*args)
|
|
36
|
+
save(*args) or fail(RelInvalidError, inspect) # rubocop:disable Style/AndOr
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Increments concurrently a numeric attribute by a centain amount
|
|
40
|
+
# @param [Symbol, String] attribute name of the attribute to increment
|
|
41
|
+
# @param [Integer, Float] by amount to increment
|
|
42
|
+
def concurrent_increment!(attribute, by = 1)
|
|
43
|
+
increment_by_query! query_as(:r), attribute, by, :r
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def create_model
|
|
47
|
+
validate_node_classes!
|
|
48
|
+
delete_has_one_rel
|
|
49
|
+
rel = _create_rel
|
|
50
|
+
return self unless rel.respond_to?(:properties)
|
|
51
|
+
init_on_load(rel, from_node, to_node, @type)
|
|
52
|
+
true
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def delete_has_one_rel
|
|
56
|
+
to_node.delete_reverse_has_one_relationship(self, :in, from_node) if to_node.persisted?
|
|
57
|
+
from_node.delete_reverse_has_one_relationship(self, :out, to_node) if from_node.persisted?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def query_as(var)
|
|
61
|
+
# This should query based on the nodes, not the rel neo_id, I think
|
|
62
|
+
# Also, picky point: Should the var be `n`?
|
|
63
|
+
self.class.query_as(neo_id, var)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
module ClassMethods
|
|
67
|
+
# Creates a new relationship between objects
|
|
68
|
+
# @param [Hash] args the properties the new relationship should have
|
|
69
|
+
def create(*args)
|
|
70
|
+
new(*args).tap(&:save)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Same as #create, but raises an error if there is a problem during save.
|
|
74
|
+
def create!(*args)
|
|
75
|
+
new(*args).tap(&:save!)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def create_method
|
|
79
|
+
creates_unique? ? :create_unique : :create
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def load_entity(id)
|
|
83
|
+
query_as(id).pluck(:r).first
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def query_as(neo_id, var = :r)
|
|
87
|
+
ActiveGraph::Base.new_query.match("()-[#{var}]->()").where(var => {neo_id: neo_id})
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def create_method
|
|
92
|
+
self.class.create_method
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def destroy_query
|
|
98
|
+
query_as(:r).delete(:r)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def validate_node_classes!
|
|
102
|
+
[from_node, to_node].each do |node|
|
|
103
|
+
type = from_node == node ? :_from_class : :_to_class
|
|
104
|
+
type_class = self.class.send(type)
|
|
105
|
+
|
|
106
|
+
unless valid_type?(type_class, node)
|
|
107
|
+
fail ModelClassInvalidError, type_validation_error_message(node, type_class)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def valid_type?(type_object, node)
|
|
113
|
+
case type_object
|
|
114
|
+
when false, :any
|
|
115
|
+
true
|
|
116
|
+
when Array
|
|
117
|
+
type_object.any? { |c| valid_type?(c, node) }
|
|
118
|
+
else
|
|
119
|
+
node.class.mapped_label_names.include?(type_object.to_s.constantize.mapped_label_name)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def type_validation_error_message(node, type_class)
|
|
124
|
+
"Node class was #{node.class} (#{node.class.object_id}), expected #{type_class} (#{type_class.object_id})"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def _create_rel
|
|
128
|
+
factory = QueryFactory.new(from_node, to_node, self)
|
|
129
|
+
factory.build!
|
|
130
|
+
factory.unwrapped_rel
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|