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,86 @@
|
|
|
1
|
+
module Neo4j::ActiveNode
|
|
2
|
+
# A reflection contains information about an association.
|
|
3
|
+
# They are often used in connection with form builders to determine associated classes.
|
|
4
|
+
# This module contains methods related to the creation and retrieval of reflections.
|
|
5
|
+
module Reflection
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
class_attribute :reflections
|
|
10
|
+
self.reflections = {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Adds methods to the class related to creating and retrieving reflections.
|
|
14
|
+
module ClassMethods
|
|
15
|
+
# @param macro [Symbol] the association type, :has_many or :has_one
|
|
16
|
+
# @param name [Symbol] the association name
|
|
17
|
+
# @param association_object [Neo4j::ActiveNode::HasN::Association] the association object created in the course of creating this reflection
|
|
18
|
+
def create_reflection(macro, name, association_object, model)
|
|
19
|
+
self.reflections = self.reflections.merge(name => AssociationReflection.new(macro, name, association_object))
|
|
20
|
+
association_object.add_destroy_callbacks(model)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private :create_reflection
|
|
24
|
+
# @param association [Symbol] an association declared on the model
|
|
25
|
+
# @return [Neo4j::ActiveNode::Reflection::AssociationReflection] of the given association
|
|
26
|
+
def reflect_on_association(association)
|
|
27
|
+
reflections[association.to_sym]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Returns an array containing one reflection for each association declared in the model.
|
|
31
|
+
def reflect_on_all_associations(macro = nil)
|
|
32
|
+
association_reflections = reflections.values
|
|
33
|
+
macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# The actual reflection object that contains information about the given association.
|
|
38
|
+
# These should never need to be created manually, they will always be created by declaring a :has_many or :has_one association on a model.
|
|
39
|
+
class AssociationReflection
|
|
40
|
+
# The name of the association
|
|
41
|
+
attr_reader :name
|
|
42
|
+
|
|
43
|
+
# The type of association
|
|
44
|
+
attr_reader :macro
|
|
45
|
+
|
|
46
|
+
# The association object referenced by this reflection
|
|
47
|
+
attr_reader :association
|
|
48
|
+
|
|
49
|
+
def initialize(macro, name, association)
|
|
50
|
+
@macro = macro
|
|
51
|
+
@name = name
|
|
52
|
+
@association = association
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Returns the target model
|
|
56
|
+
def klass
|
|
57
|
+
@klass ||= class_name.constantize
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Returns the name of the target model
|
|
61
|
+
def class_name
|
|
62
|
+
@class_name ||= association.target_class.name
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def rel_klass
|
|
66
|
+
@rel_klass ||= rel_class_name.constantize
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def rel_class_name
|
|
70
|
+
@rel_class_name ||= association.relationship_class.name.to_s
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def type
|
|
74
|
+
@type ||= association.relationship_type
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def collection?
|
|
78
|
+
macro == :has_many
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def validate?
|
|
82
|
+
true
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Neo4j::ActiveNode
|
|
2
|
+
module Rels
|
|
3
|
+
extend Forwardable
|
|
4
|
+
def_delegators :_rels_delegator, :rel?, :rel, :rels, :node, :nodes, :create_rel
|
|
5
|
+
|
|
6
|
+
def _rels_delegator
|
|
7
|
+
fail "Can't access relationship on a non persisted node" unless _persisted_obj
|
|
8
|
+
_persisted_obj
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
require 'active_support/per_thread_registry'
|
|
2
|
+
|
|
3
|
+
module Neo4j::ActiveNode
|
|
4
|
+
module Scope
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
module ClassMethods
|
|
8
|
+
# Similar to ActiveRecord scope
|
|
9
|
+
#
|
|
10
|
+
# @example without argument
|
|
11
|
+
# class Person
|
|
12
|
+
# include Neo4j::ActiveNode
|
|
13
|
+
# property :name
|
|
14
|
+
# property :score
|
|
15
|
+
# has_many :out, :friends, type: :has_friend, model_class: self
|
|
16
|
+
# scope :top_students, -> { where(score: 42)}") }
|
|
17
|
+
# end
|
|
18
|
+
# Person.top_students.to_a
|
|
19
|
+
# a_person.friends.top_students.to_a
|
|
20
|
+
# a_person.friends.friends.top_students.to_a
|
|
21
|
+
# a_person.friends.top_students.friends.to_a
|
|
22
|
+
#
|
|
23
|
+
# @example Argument for scopes
|
|
24
|
+
# Person.scope :level, ->(num) { where(level_num: num)}
|
|
25
|
+
#
|
|
26
|
+
# @example Argument as a cypher identifier
|
|
27
|
+
# class Person
|
|
28
|
+
# include Neo4j::ActiveNode
|
|
29
|
+
# property :name
|
|
30
|
+
# property :score
|
|
31
|
+
# has_many :out, :friends, type: :has_friend, model_class: self
|
|
32
|
+
# scope :great_students, ->(identifier) { where("#{identifier}.score > 41") }
|
|
33
|
+
# end
|
|
34
|
+
# Person.as(:all_people).great_students(:all_people).to_a
|
|
35
|
+
#
|
|
36
|
+
# @see http://guides.rubyonrails.org/active_record_querying.html#scopes
|
|
37
|
+
def scope(name, proc)
|
|
38
|
+
scopes[name.to_sym] = proc
|
|
39
|
+
|
|
40
|
+
klass = class << self; self; end
|
|
41
|
+
klass.instance_eval do
|
|
42
|
+
define_method(name) do |*query_params|
|
|
43
|
+
eval_context = ScopeEvalContext.new(self, current_scope || self.query_proxy)
|
|
44
|
+
proc = full_scopes[name.to_sym]
|
|
45
|
+
_call_scope_context(eval_context, query_params, proc)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
define_method(name) do |*query_params|
|
|
50
|
+
as(:n).public_send(name, *query_params)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# rubocop:disable Naming/PredicateName
|
|
55
|
+
def has_scope?(name)
|
|
56
|
+
ActiveSupport::Deprecation.warn 'has_scope? is deprecated and may be removed from future releases, use scope? instead.', caller
|
|
57
|
+
|
|
58
|
+
scope?(name)
|
|
59
|
+
end
|
|
60
|
+
# rubocop:enable Naming/PredicateName
|
|
61
|
+
|
|
62
|
+
# @return [Boolean] true if model has access to scope with this name
|
|
63
|
+
def scope?(name)
|
|
64
|
+
full_scopes.key?(name.to_sym)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @return [Hash] of scopes assigned to this model. Keys are scope name, value is scope callable.
|
|
68
|
+
def scopes
|
|
69
|
+
@scopes ||= {}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# @return [Hash] of scopes available to this model. Keys are scope name, value is scope callable.
|
|
73
|
+
def full_scopes
|
|
74
|
+
self.ancestors.find_all { |a| a.respond_to?(:scopes) }.reverse.inject({}) do |scopes, a|
|
|
75
|
+
scopes.merge(a.scopes)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def _call_scope_context(eval_context, query_params, proc)
|
|
80
|
+
eval_context.instance_exec(*query_params.fill(nil, query_params.length..proc.arity - 1), &proc)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def current_scope #:nodoc:
|
|
84
|
+
ScopeRegistry.value_for(:current_scope, base_class.to_s)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def current_scope=(scope) #:nodoc:
|
|
88
|
+
ScopeRegistry.set_value_for(:current_scope, base_class.to_s, scope)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def all(new_var = nil)
|
|
92
|
+
var = new_var || (current_scope ? current_scope.node_identity : :n)
|
|
93
|
+
if current_scope
|
|
94
|
+
current_scope.new_link(var)
|
|
95
|
+
else
|
|
96
|
+
self.as(var)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
class ScopeEvalContext
|
|
102
|
+
def initialize(target, query_proxy)
|
|
103
|
+
@query_proxy = query_proxy
|
|
104
|
+
@target = target
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def identity
|
|
108
|
+
query_proxy_or_target.identity
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
Neo4j::ActiveNode::Query::QueryProxy::METHODS.each do |method|
|
|
112
|
+
define_method(method) do |*args|
|
|
113
|
+
@target.all.scoping do
|
|
114
|
+
query_proxy_or_target.public_send(method, *args)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# method_missing is not delegated to super class but to aggregated class
|
|
120
|
+
# rubocop:disable Style/MethodMissingSuper
|
|
121
|
+
def method_missing(name, *params, &block)
|
|
122
|
+
query_proxy_or_target.public_send(name, *params, &block)
|
|
123
|
+
end
|
|
124
|
+
# rubocop:enable Style/MethodMissingSuper
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
def query_proxy_or_target
|
|
129
|
+
@query_proxy_or_target ||= @query_proxy || @target
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# Stolen from ActiveRecord
|
|
135
|
+
# https://github.com/rails/rails/blob/08754f12e65a9ec79633a605e986d0f1ffa4b251/activerecord/lib/active_record/scoping.rb#L57
|
|
136
|
+
class ScopeRegistry # :nodoc:
|
|
137
|
+
extend ActiveSupport::PerThreadRegistry
|
|
138
|
+
|
|
139
|
+
VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope]
|
|
140
|
+
|
|
141
|
+
def initialize
|
|
142
|
+
@registry = Hash.new { |hash, key| hash[key] = {} }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Obtains the value for a given +scope_name+ and +variable_name+.
|
|
146
|
+
def value_for(scope_type, variable_name)
|
|
147
|
+
raise_invalid_scope_type!(scope_type)
|
|
148
|
+
@registry[scope_type][variable_name]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Sets the +value+ for a given +scope_type+ and +variable_name+.
|
|
152
|
+
def set_value_for(scope_type, variable_name, value)
|
|
153
|
+
raise_invalid_scope_type!(scope_type)
|
|
154
|
+
@registry[scope_type][variable_name] = value
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
private
|
|
158
|
+
|
|
159
|
+
def raise_invalid_scope_type!(scope_type)
|
|
160
|
+
return if VALID_SCOPE_TYPES.include?(scope_type)
|
|
161
|
+
|
|
162
|
+
fail ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module ActiveNode
|
|
3
|
+
module Unpersisted
|
|
4
|
+
# The values in this Hash are returned and used outside by reference
|
|
5
|
+
# so any modifications to the Array should be in-place
|
|
6
|
+
def deferred_create_cache
|
|
7
|
+
@deferred_create_cache ||= {}
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def defer_create(association_name, object, options = {})
|
|
11
|
+
clear_deferred_nodes_for_association(association_name) if options[:clear]
|
|
12
|
+
|
|
13
|
+
deferred_nodes_for_association(association_name) << object
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def deferred_nodes_for_association(association_name)
|
|
17
|
+
deferred_create_cache[association_name.to_sym] ||= []
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def pending_deferred_creations?
|
|
21
|
+
!deferred_create_cache.values.all?(&:empty?)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def clear_deferred_nodes_for_association(association_name)
|
|
25
|
+
deferred_nodes_for_association(association_name.to_sym).clear
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def process_unpersisted_nodes!
|
|
31
|
+
deferred_create_cache.dup.each do |association_name, nodes|
|
|
32
|
+
association_proxy = association_proxy(association_name)
|
|
33
|
+
|
|
34
|
+
nodes.each do |node|
|
|
35
|
+
if node.respond_to?(:changed?)
|
|
36
|
+
node.save if node.changed? || !node.persisted?
|
|
37
|
+
fail "Unable to defer node persistence, could not save #{node.inspect}" unless node.persisted?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
association_proxy << node
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
@deferred_create_cache = {}
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module ActiveNode
|
|
3
|
+
# This mixin replace the original save method and performs validation before the save.
|
|
4
|
+
module Validations
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
include Neo4j::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,67 @@
|
|
|
1
|
+
module Neo4j
|
|
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 ActiveRel
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
MARSHAL_INSTANCE_VARIABLES = [:@attributes, :@rel_type, :@_persisted_obj]
|
|
8
|
+
|
|
9
|
+
include Neo4j::Shared
|
|
10
|
+
include Neo4j::ActiveRel::Initialize
|
|
11
|
+
include Neo4j::Shared::Identity
|
|
12
|
+
include Neo4j::Shared::Marshal
|
|
13
|
+
include Neo4j::Shared::SerializedProperties
|
|
14
|
+
include Neo4j::ActiveRel::Property
|
|
15
|
+
include Neo4j::ActiveRel::Persistence
|
|
16
|
+
include Neo4j::ActiveRel::Validations
|
|
17
|
+
include Neo4j::ActiveRel::Callbacks
|
|
18
|
+
include Neo4j::ActiveRel::Query
|
|
19
|
+
include Neo4j::ActiveRel::Types
|
|
20
|
+
include Neo4j::Shared::Enum
|
|
21
|
+
include Neo4j::Shared::PermittedAttributes
|
|
22
|
+
|
|
23
|
+
class FrozenRelError < Neo4j::Error; end
|
|
24
|
+
|
|
25
|
+
def initialize(from_node = nil, to_node = nil, args = nil)
|
|
26
|
+
load_nodes(node_or_nil(from_node), node_or_nil(to_node))
|
|
27
|
+
resolved_args = hash_or_nil(from_node, args)
|
|
28
|
+
symbol_args = sanitize_input_parameters(resolved_args)
|
|
29
|
+
super(symbol_args)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def node_cypher_representation(node)
|
|
33
|
+
node_class = node.class
|
|
34
|
+
id_name = node_class.id_property_name
|
|
35
|
+
labels = ':' + node_class.mapped_label_names.join(':')
|
|
36
|
+
|
|
37
|
+
"(#{labels} {#{id_name}: #{node.id.inspect}})"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def neo4j_obj
|
|
41
|
+
_persisted_obj || fail('Tried to access native neo4j object on a non persisted object')
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
included do
|
|
45
|
+
include Neo4j::Timestamps if Neo4j::Config[:record_timestamps]
|
|
46
|
+
|
|
47
|
+
def self.inherited(other)
|
|
48
|
+
attributes.each_pair do |k, v|
|
|
49
|
+
other.inherit_property k.to_sym, v.clone, declared_properties[k].options
|
|
50
|
+
end
|
|
51
|
+
super
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
ActiveSupport.run_load_hooks(:active_rel, self)
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def node_or_nil(node)
|
|
60
|
+
node.is_a?(Neo4j::ActiveNode) || node.is_a?(Integer) ? node : nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def hash_or_nil(node_or_hash, hash_or_nil)
|
|
64
|
+
hash_or_parameter?(node_or_hash) ? node_or_hash : hash_or_nil
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module ActiveRel
|
|
3
|
+
module Callbacks #:nodoc:
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
include Neo4j::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 Neo4j::ActiveRel::Persistence::RelInvalidError, 'from_node and to_node must be node objects'
|
|
10
|
+
end
|
|
11
|
+
super(*args)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|