activegraph 10.0.0.pre.alpha.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1989 -0
- data/CONTRIBUTORS +12 -0
- data/Gemfile +24 -0
- data/README.md +107 -0
- data/bin/rake +17 -0
- data/config/locales/en.yml +5 -0
- data/config/neo4j/add_classnames.yml +1 -0
- data/config/neo4j/config.yml +38 -0
- data/lib/neo4j.rb +116 -0
- data/lib/neo4j/active_base.rb +89 -0
- data/lib/neo4j/active_node.rb +108 -0
- data/lib/neo4j/active_node/callbacks.rb +8 -0
- data/lib/neo4j/active_node/dependent.rb +11 -0
- data/lib/neo4j/active_node/dependent/association_methods.rb +49 -0
- data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +51 -0
- data/lib/neo4j/active_node/enum.rb +26 -0
- data/lib/neo4j/active_node/has_n.rb +612 -0
- data/lib/neo4j/active_node/has_n/association.rb +278 -0
- data/lib/neo4j/active_node/has_n/association/rel_factory.rb +61 -0
- data/lib/neo4j/active_node/has_n/association/rel_wrapper.rb +23 -0
- data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +108 -0
- data/lib/neo4j/active_node/id_property.rb +224 -0
- data/lib/neo4j/active_node/id_property/accessor.rb +62 -0
- data/lib/neo4j/active_node/initialize.rb +21 -0
- data/lib/neo4j/active_node/labels.rb +207 -0
- data/lib/neo4j/active_node/labels/index.rb +37 -0
- data/lib/neo4j/active_node/labels/reloading.rb +21 -0
- data/lib/neo4j/active_node/node_list_formatter.rb +13 -0
- data/lib/neo4j/active_node/node_wrapper.rb +54 -0
- data/lib/neo4j/active_node/orm_adapter.rb +82 -0
- data/lib/neo4j/active_node/persistence.rb +187 -0
- data/lib/neo4j/active_node/property.rb +60 -0
- data/lib/neo4j/active_node/query.rb +76 -0
- data/lib/neo4j/active_node/query/query_proxy.rb +374 -0
- data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +177 -0
- data/lib/neo4j/active_node/query/query_proxy_eager_loading/association_tree.rb +75 -0
- data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +110 -0
- data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +19 -0
- data/lib/neo4j/active_node/query/query_proxy_link.rb +139 -0
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +302 -0
- data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +86 -0
- data/lib/neo4j/active_node/query_methods.rb +68 -0
- data/lib/neo4j/active_node/reflection.rb +86 -0
- data/lib/neo4j/active_node/rels.rb +11 -0
- data/lib/neo4j/active_node/scope.rb +166 -0
- data/lib/neo4j/active_node/unpersisted.rb +48 -0
- data/lib/neo4j/active_node/validations.rb +59 -0
- data/lib/neo4j/active_rel.rb +67 -0
- data/lib/neo4j/active_rel/callbacks.rb +15 -0
- data/lib/neo4j/active_rel/initialize.rb +28 -0
- data/lib/neo4j/active_rel/persistence.rb +134 -0
- data/lib/neo4j/active_rel/persistence/query_factory.rb +95 -0
- data/lib/neo4j/active_rel/property.rb +95 -0
- data/lib/neo4j/active_rel/query.rb +101 -0
- data/lib/neo4j/active_rel/rel_wrapper.rb +31 -0
- data/lib/neo4j/active_rel/related_node.rb +87 -0
- data/lib/neo4j/active_rel/types.rb +82 -0
- data/lib/neo4j/active_rel/validations.rb +8 -0
- data/lib/neo4j/ansi.rb +14 -0
- data/lib/neo4j/class_arguments.rb +39 -0
- data/lib/neo4j/config.rb +135 -0
- data/lib/neo4j/core.rb +14 -0
- data/lib/neo4j/core/connection_failed_error.rb +6 -0
- data/lib/neo4j/core/cypher_error.rb +37 -0
- data/lib/neo4j/core/driver.rb +66 -0
- data/lib/neo4j/core/has_uri.rb +63 -0
- data/lib/neo4j/core/instrumentable.rb +36 -0
- data/lib/neo4j/core/label.rb +158 -0
- data/lib/neo4j/core/logging.rb +44 -0
- data/lib/neo4j/core/node.rb +23 -0
- data/lib/neo4j/core/querable.rb +88 -0
- data/lib/neo4j/core/query.rb +487 -0
- data/lib/neo4j/core/query_builder.rb +32 -0
- data/lib/neo4j/core/query_clauses.rb +727 -0
- data/lib/neo4j/core/query_ext.rb +24 -0
- data/lib/neo4j/core/query_find_in_batches.rb +49 -0
- data/lib/neo4j/core/relationship.rb +13 -0
- data/lib/neo4j/core/responses.rb +50 -0
- data/lib/neo4j/core/result.rb +33 -0
- data/lib/neo4j/core/schema.rb +30 -0
- data/lib/neo4j/core/schema_errors.rb +12 -0
- data/lib/neo4j/core/wrappable.rb +30 -0
- data/lib/neo4j/errors.rb +57 -0
- data/lib/neo4j/migration.rb +148 -0
- data/lib/neo4j/migrations.rb +27 -0
- data/lib/neo4j/migrations/base.rb +77 -0
- data/lib/neo4j/migrations/check_pending.rb +20 -0
- data/lib/neo4j/migrations/helpers.rb +105 -0
- data/lib/neo4j/migrations/helpers/id_property.rb +75 -0
- data/lib/neo4j/migrations/helpers/relationships.rb +66 -0
- data/lib/neo4j/migrations/helpers/schema.rb +51 -0
- data/lib/neo4j/migrations/migration_file.rb +24 -0
- data/lib/neo4j/migrations/runner.rb +195 -0
- data/lib/neo4j/migrations/schema.rb +44 -0
- data/lib/neo4j/migrations/schema_migration.rb +14 -0
- data/lib/neo4j/model_schema.rb +139 -0
- data/lib/neo4j/paginated.rb +27 -0
- data/lib/neo4j/railtie.rb +105 -0
- data/lib/neo4j/schema/operation.rb +102 -0
- data/lib/neo4j/shared.rb +60 -0
- data/lib/neo4j/shared/attributes.rb +216 -0
- data/lib/neo4j/shared/callbacks.rb +68 -0
- data/lib/neo4j/shared/cypher.rb +37 -0
- data/lib/neo4j/shared/declared_properties.rb +204 -0
- data/lib/neo4j/shared/declared_property.rb +109 -0
- data/lib/neo4j/shared/declared_property/index.rb +37 -0
- data/lib/neo4j/shared/enum.rb +167 -0
- data/lib/neo4j/shared/filtered_hash.rb +79 -0
- data/lib/neo4j/shared/identity.rb +34 -0
- data/lib/neo4j/shared/initialize.rb +64 -0
- data/lib/neo4j/shared/marshal.rb +23 -0
- data/lib/neo4j/shared/mass_assignment.rb +64 -0
- data/lib/neo4j/shared/permitted_attributes.rb +28 -0
- data/lib/neo4j/shared/persistence.rb +282 -0
- data/lib/neo4j/shared/property.rb +240 -0
- data/lib/neo4j/shared/query_factory.rb +102 -0
- data/lib/neo4j/shared/rel_type_converters.rb +43 -0
- data/lib/neo4j/shared/serialized_properties.rb +30 -0
- data/lib/neo4j/shared/type_converters.rb +433 -0
- data/lib/neo4j/shared/typecasted_attributes.rb +98 -0
- data/lib/neo4j/shared/typecaster.rb +53 -0
- data/lib/neo4j/shared/validations.rb +44 -0
- data/lib/neo4j/tasks/migration.rake +202 -0
- data/lib/neo4j/timestamps.rb +11 -0
- data/lib/neo4j/timestamps/created.rb +9 -0
- data/lib/neo4j/timestamps/updated.rb +9 -0
- data/lib/neo4j/transaction.rb +139 -0
- data/lib/neo4j/type_converters.rb +7 -0
- data/lib/neo4j/undeclared_properties.rb +53 -0
- data/lib/neo4j/version.rb +3 -0
- data/lib/neo4j/wrapper.rb +4 -0
- data/lib/rails/generators/neo4j/migration/migration_generator.rb +14 -0
- data/lib/rails/generators/neo4j/migration/templates/migration.erb +9 -0
- data/lib/rails/generators/neo4j/model/model_generator.rb +88 -0
- data/lib/rails/generators/neo4j/model/templates/migration.erb +9 -0
- data/lib/rails/generators/neo4j/model/templates/model.erb +15 -0
- data/lib/rails/generators/neo4j/upgrade_v8/templates/migration.erb +17 -0
- data/lib/rails/generators/neo4j/upgrade_v8/upgrade_v8_generator.rb +32 -0
- data/lib/rails/generators/neo4j_generator.rb +119 -0
- data/neo4j.gemspec +51 -0
- metadata +421 -0
@@ -0,0 +1,224 @@
|
|
1
|
+
module Neo4j::ActiveNode
|
2
|
+
# This module makes it possible to use other IDs than the build it neo4j id (neo_id)
|
3
|
+
#
|
4
|
+
# @example using generated UUIDs
|
5
|
+
# class Person
|
6
|
+
# include Neo4j::ActiveNode
|
7
|
+
# # creates a 'secret' neo4j property my_uuid which will be used as primary key
|
8
|
+
# id_property :my_uuid, auto: :uuid
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# @example using user defined ids
|
12
|
+
# class Person
|
13
|
+
# include Neo4j::ActiveNode
|
14
|
+
# property :title
|
15
|
+
# validates :title, :presence => true
|
16
|
+
# id_property :title_id, on: :title_to_url
|
17
|
+
#
|
18
|
+
# def title_to_url
|
19
|
+
# self.title.urlize # uses https://github.com/cheef/string-urlize gem
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @example using already exsting ids that you don't want a constraint added to
|
24
|
+
# class Person
|
25
|
+
# include Neo4j::ActiveNode
|
26
|
+
# property :title
|
27
|
+
# validates :title, :presence => true
|
28
|
+
# id_property :id, on: :id_builder, constraint: false
|
29
|
+
#
|
30
|
+
# def id_builder
|
31
|
+
# # only need to fill this out if you're gonna write to the db
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
module IdProperty
|
36
|
+
extend ActiveSupport::Concern
|
37
|
+
include Accessor
|
38
|
+
|
39
|
+
module TypeMethods
|
40
|
+
def define_id_methods(clazz, name, conf)
|
41
|
+
return if name == :neo_id
|
42
|
+
|
43
|
+
validate_conf!(conf)
|
44
|
+
|
45
|
+
if conf[:on]
|
46
|
+
define_custom_method(clazz, name, conf[:on])
|
47
|
+
elsif conf[:auto]
|
48
|
+
define_uuid_method(clazz, name)
|
49
|
+
elsif conf.empty?
|
50
|
+
define_property_method(clazz, name)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def validate_conf!(conf)
|
57
|
+
fail "Expected a Hash, got #{conf.class} (#{conf}) for id_property" if !conf.is_a?(Hash)
|
58
|
+
|
59
|
+
return if conf[:on]
|
60
|
+
|
61
|
+
if conf[:auto]
|
62
|
+
fail "only :uuid auto id_property allowed, got #{conf[:auto]}" if conf[:auto] != :uuid
|
63
|
+
return
|
64
|
+
end
|
65
|
+
|
66
|
+
return if conf.empty?
|
67
|
+
|
68
|
+
fail "Illegal value #{conf.inspect} for id_property, expected :on or :auto"
|
69
|
+
end
|
70
|
+
|
71
|
+
def define_property_method(clazz, name)
|
72
|
+
clear_methods(clazz, name)
|
73
|
+
|
74
|
+
clazz.module_eval(%(
|
75
|
+
def id
|
76
|
+
_persisted_obj ? #{name.to_sym == :id ? 'attribute(\'id\')' : name} : nil
|
77
|
+
end
|
78
|
+
|
79
|
+
property :#{name}
|
80
|
+
), __FILE__, __LINE__)
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
def define_uuid_method(clazz, name)
|
85
|
+
clear_methods(clazz, name)
|
86
|
+
|
87
|
+
clazz.module_eval(%(
|
88
|
+
default_property :#{name} do
|
89
|
+
::SecureRandom.uuid
|
90
|
+
end
|
91
|
+
|
92
|
+
def #{name}
|
93
|
+
default_property_value
|
94
|
+
end
|
95
|
+
|
96
|
+
alias_method :id, :#{name}
|
97
|
+
), __FILE__, __LINE__)
|
98
|
+
end
|
99
|
+
|
100
|
+
def define_custom_method(clazz, name, on)
|
101
|
+
clear_methods(clazz, name)
|
102
|
+
|
103
|
+
clazz.module_eval(%{
|
104
|
+
default_property :#{name} do |instance|
|
105
|
+
raise "Specifying custom id_property #{name} on non-existent method #{on}" unless instance.respond_to?(:#{on})
|
106
|
+
instance.#{on}
|
107
|
+
end
|
108
|
+
|
109
|
+
def #{name}
|
110
|
+
default_property_value
|
111
|
+
end
|
112
|
+
|
113
|
+
alias_method :id, :#{name}
|
114
|
+
}, __FILE__, __LINE__)
|
115
|
+
end
|
116
|
+
|
117
|
+
def clear_methods(clazz, name)
|
118
|
+
clazz.module_eval(%(undef_method :#{name}), __FILE__, __LINE__) if clazz.method_defined?(name)
|
119
|
+
clazz.module_eval(%(undef_property :#{name}), __FILE__, __LINE__) if clazz.attribute_names.include?(name.to_s)
|
120
|
+
end
|
121
|
+
|
122
|
+
extend self
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
module ClassMethods
|
127
|
+
attr_accessor :manual_id_property
|
128
|
+
|
129
|
+
def find_by_neo_id(id)
|
130
|
+
find_by(neo_id: id)
|
131
|
+
end
|
132
|
+
|
133
|
+
def find_by_id(id)
|
134
|
+
all.where(id_property_name => id).first
|
135
|
+
end
|
136
|
+
|
137
|
+
def find_by_ids(ids)
|
138
|
+
all.where(id_property_name => ids).to_a
|
139
|
+
end
|
140
|
+
|
141
|
+
def id_property(name, conf = {}, inherited = false)
|
142
|
+
self.manual_id_property = true
|
143
|
+
|
144
|
+
@id_property_info = {name: name, type: conf, inherited: inherited}
|
145
|
+
TypeMethods.define_id_methods(self, name, conf)
|
146
|
+
end
|
147
|
+
|
148
|
+
# rubocop:disable Naming/PredicateName
|
149
|
+
def has_id_property?
|
150
|
+
ActiveSupport::Deprecation.warn 'has_id_property? is deprecated and may be removed from future releases, use id_property? instead.', caller
|
151
|
+
|
152
|
+
id_property?
|
153
|
+
end
|
154
|
+
# rubocop:enable Naming/PredicateName
|
155
|
+
|
156
|
+
def id_property?
|
157
|
+
id_property_info && !id_property_info.empty?
|
158
|
+
end
|
159
|
+
|
160
|
+
def id_property_info
|
161
|
+
ensure_id_property_info!
|
162
|
+
|
163
|
+
@id_property_info ||= {}
|
164
|
+
end
|
165
|
+
|
166
|
+
def id_property_name
|
167
|
+
id_property_info[:name]
|
168
|
+
end
|
169
|
+
|
170
|
+
def manual_id_property?
|
171
|
+
!!manual_id_property
|
172
|
+
end
|
173
|
+
|
174
|
+
alias primary_key id_property_name
|
175
|
+
|
176
|
+
# Since there's no way to know when a class is done being described, we wait until the id_property
|
177
|
+
# information is requested and use that as the opportunity to set up the defaults if no others are specified
|
178
|
+
def ensure_id_property_info!
|
179
|
+
if !manual_id_property? && !@id_property_info
|
180
|
+
name, type, value = id_property_name_type_value
|
181
|
+
id_property(name, type => value)
|
182
|
+
end
|
183
|
+
|
184
|
+
handle_model_schema!
|
185
|
+
end
|
186
|
+
|
187
|
+
private
|
188
|
+
|
189
|
+
def handle_model_schema!
|
190
|
+
id_property_name = @id_property_info[:name]
|
191
|
+
|
192
|
+
return if id_property_name == :neo_id || @id_property_info[:inherited]
|
193
|
+
|
194
|
+
if @id_property_info[:type][:constraint] == false &&
|
195
|
+
!@id_property_info[:warned_of_constraint]
|
196
|
+
@id_property_info[:warned_of_constraint] = true
|
197
|
+
warn_constraint_option_false!(id_property_name)
|
198
|
+
return
|
199
|
+
end
|
200
|
+
|
201
|
+
Neo4j::ModelSchema.add_defined_constraint(self, id_property_name)
|
202
|
+
end
|
203
|
+
|
204
|
+
def warn_constraint_option_false!(id_property_name)
|
205
|
+
Neo4j::ActiveBase.logger.warn <<MSG
|
206
|
+
WARNING: The constraint option for id_property is no longer supported (Used on #{self.name}.#{id_property_name}).
|
207
|
+
Since you specified `constraint: false` this option can simply be removed.
|
208
|
+
MSG
|
209
|
+
end
|
210
|
+
|
211
|
+
def id_property_name_type_value
|
212
|
+
name, type, value = Neo4j::Config.to_hash.values_at('id_property', 'id_property_type', 'id_property_type_value')
|
213
|
+
|
214
|
+
unless name == :neo_id || (name && type && value)
|
215
|
+
name = :uuid
|
216
|
+
type = :auto
|
217
|
+
value = :uuid
|
218
|
+
end
|
219
|
+
|
220
|
+
[name, type, value]
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Neo4j::ActiveNode::IdProperty
|
2
|
+
# Provides get/set of the Id Property values.
|
3
|
+
# Some methods
|
4
|
+
module Accessor
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
attr_reader :default_property_value
|
8
|
+
|
9
|
+
def default_properties=(properties)
|
10
|
+
@default_property_value = properties[default_property_key]
|
11
|
+
end
|
12
|
+
|
13
|
+
def default_property(key)
|
14
|
+
return nil unless key == default_property_key
|
15
|
+
default_property_value
|
16
|
+
end
|
17
|
+
|
18
|
+
def default_property_key
|
19
|
+
self.class.default_property_key
|
20
|
+
end
|
21
|
+
|
22
|
+
def default_properties
|
23
|
+
@default_properties ||= Hash.new(nil)
|
24
|
+
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
def default_property_key
|
28
|
+
@default_property_key ||= default_properties_keys.first
|
29
|
+
end
|
30
|
+
|
31
|
+
# TODO: Move this to the DeclaredProperties
|
32
|
+
def default_property(name, &block)
|
33
|
+
reset_default_properties(name) if default_properties.respond_to?(:size)
|
34
|
+
default_properties[name] = block
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Hash<Symbol,Proc>]
|
38
|
+
def default_properties
|
39
|
+
@default_property ||= {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def default_properties_keys
|
43
|
+
@default_properties_keys ||= default_properties.keys
|
44
|
+
end
|
45
|
+
|
46
|
+
def reset_default_properties(name_to_keep)
|
47
|
+
default_properties.each_key do |property|
|
48
|
+
@default_properties_keys = nil
|
49
|
+
undef_method(property) unless property == name_to_keep
|
50
|
+
end
|
51
|
+
@default_properties_keys = nil
|
52
|
+
@default_property = {}
|
53
|
+
end
|
54
|
+
|
55
|
+
def default_property_values(instance)
|
56
|
+
default_properties.each_with_object({}) do |(key, block), result|
|
57
|
+
result[key] = block.call(instance)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Neo4j::ActiveNode::Initialize
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
include Neo4j::Shared::Initialize
|
4
|
+
|
5
|
+
attr_reader :called_by
|
6
|
+
|
7
|
+
# called when loading the node from the database
|
8
|
+
# @param [Neo4j::Node] persisted_node the node this class wraps
|
9
|
+
# @param [Hash] properties of the persisted node.
|
10
|
+
def init_on_load(persisted_node, properties)
|
11
|
+
self.class.extract_association_attributes!(properties)
|
12
|
+
@_persisted_obj = persisted_node
|
13
|
+
changed_attributes_clear!
|
14
|
+
@attributes = convert_and_assign_attributes(properties)
|
15
|
+
end
|
16
|
+
|
17
|
+
def init_on_reload(reloaded)
|
18
|
+
@attributes = nil
|
19
|
+
init_on_load(reloaded, reloaded.props)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'neo4j/core/label'
|
2
|
+
|
3
|
+
module Neo4j
|
4
|
+
module ActiveNode
|
5
|
+
# Provides a mapping between neo4j labels and Ruby classes
|
6
|
+
module Labels
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
include Neo4j::ActiveNode::Labels::Index
|
9
|
+
include Neo4j::ActiveNode::Labels::Reloading
|
10
|
+
|
11
|
+
WRAPPED_CLASSES = []
|
12
|
+
MODELS_FOR_LABELS_CACHE = {}
|
13
|
+
MODELS_FOR_LABELS_CACHE.clear
|
14
|
+
|
15
|
+
included do |model|
|
16
|
+
Neo4j::ActiveNode::Labels.clear_wrapped_models
|
17
|
+
|
18
|
+
Neo4j::ActiveNode::Labels.add_wrapped_class(model) unless Neo4j::ActiveNode::Labels._wrapped_classes.include?(model)
|
19
|
+
end
|
20
|
+
|
21
|
+
class RecordNotFound < Neo4j::RecordNotFound; end
|
22
|
+
|
23
|
+
# @return the labels
|
24
|
+
# @see Neo4j-core
|
25
|
+
def labels
|
26
|
+
@_persisted_obj.labels
|
27
|
+
end
|
28
|
+
|
29
|
+
# this is handled by core, leaving it now for posterity
|
30
|
+
# def queried_labels
|
31
|
+
# self.class.query_as(:result).where("ID(result)" => self.neo_id).return("LABELS(result) as result_labels").first.result_labels.map(&:to_sym)
|
32
|
+
# end
|
33
|
+
|
34
|
+
# adds one or more labels
|
35
|
+
# @see Neo4j-core
|
36
|
+
def add_labels(*labels)
|
37
|
+
labels.inject(query_as(:n)) do |query, label|
|
38
|
+
query.set("n:`#{label}`")
|
39
|
+
end.exec
|
40
|
+
@_persisted_obj.labels.concat(labels)
|
41
|
+
@_persisted_obj.labels.uniq!
|
42
|
+
end
|
43
|
+
|
44
|
+
# Removes one or more labels
|
45
|
+
# Be careful, don't remove the label representing the Ruby class.
|
46
|
+
# @see Neo4j-core
|
47
|
+
def remove_labels(*labels)
|
48
|
+
labels.inject(query_as(:n)) do |query, label|
|
49
|
+
query.remove("n:`#{label}`")
|
50
|
+
end.exec
|
51
|
+
labels.each(&@_persisted_obj.labels.method(:delete))
|
52
|
+
end
|
53
|
+
|
54
|
+
def self._wrapped_classes
|
55
|
+
WRAPPED_CLASSES
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.add_wrapped_class(model)
|
59
|
+
_wrapped_classes << model
|
60
|
+
end
|
61
|
+
|
62
|
+
# Finds an appropriate matching model given a set of labels
|
63
|
+
# which are assigned to a node
|
64
|
+
def self.model_for_labels(labels)
|
65
|
+
labels.sort!
|
66
|
+
return MODELS_FOR_LABELS_CACHE[labels] if MODELS_FOR_LABELS_CACHE[labels]
|
67
|
+
|
68
|
+
models = WRAPPED_CLASSES.select do |model|
|
69
|
+
(model.mapped_label_names - labels).empty?
|
70
|
+
end
|
71
|
+
|
72
|
+
MODELS_FOR_LABELS_CACHE[labels] = models.max_by do |model|
|
73
|
+
(model.mapped_label_names & labels).size
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.clear_wrapped_models
|
78
|
+
MODELS_FOR_LABELS_CACHE.clear
|
79
|
+
Neo4j::NodeWrapping::CONSTANTS_FOR_LABELS_CACHE.clear
|
80
|
+
end
|
81
|
+
|
82
|
+
module ClassMethods
|
83
|
+
include Neo4j::ActiveNode::QueryMethods
|
84
|
+
|
85
|
+
delegate :update_all, to: :all
|
86
|
+
|
87
|
+
# Returns the object with the specified neo4j id.
|
88
|
+
# @param [String,Integer] id of node to find
|
89
|
+
def find(id)
|
90
|
+
map_id = proc { |object| object.respond_to?(:id) ? object.send(:id) : object }
|
91
|
+
|
92
|
+
result = find_by_id_or_ids(map_id, id)
|
93
|
+
|
94
|
+
fail RecordNotFound.new(
|
95
|
+
"Couldn't find #{name} with '#{id_property_name}'=#{id.inspect}",
|
96
|
+
name, id_property_name, id) if result.blank?
|
97
|
+
result.tap { |r| find_callbacks!(r) }
|
98
|
+
end
|
99
|
+
|
100
|
+
# Finds the first record matching the specified conditions. There is no implied ordering so if order matters, you should specify it yourself.
|
101
|
+
# @param values Hash args of arguments to find
|
102
|
+
def find_by(values)
|
103
|
+
all.where(values).limit(1).query_as(:n).pluck(:n).first
|
104
|
+
end
|
105
|
+
|
106
|
+
# Like find_by, except that if no record is found, raises a RecordNotFound error.
|
107
|
+
def find_by!(values)
|
108
|
+
find_by(values) || fail(RecordNotFound.new("#{self.query_as(:n).where(n: values).limit(1).to_cypher} returned no results", name))
|
109
|
+
end
|
110
|
+
|
111
|
+
# Deletes all nodes and connected relationships from Cypher.
|
112
|
+
def delete_all
|
113
|
+
neo4j_query("MATCH (n:`#{mapped_label_name}`) OPTIONAL MATCH (n)-[r]-() DELETE n,r")
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns each node to Ruby and calls `destroy`. Be careful, as this can be a very slow operation if you have many nodes. It will generate at least
|
117
|
+
# one database query per node in the database, more if callbacks require them.
|
118
|
+
def destroy_all
|
119
|
+
all.each(&:destroy)
|
120
|
+
end
|
121
|
+
|
122
|
+
# @return [Array{Symbol}] all the labels that this class has
|
123
|
+
def mapped_label_names
|
124
|
+
self.ancestors.find_all { |a| a.respond_to?(:mapped_label_name) }.map { |a| a.mapped_label_name.to_sym }
|
125
|
+
end
|
126
|
+
|
127
|
+
# @return [Symbol] the label that this class has which corresponds to a Ruby class
|
128
|
+
def mapped_label_name
|
129
|
+
@mapped_label_name || label_for_model
|
130
|
+
end
|
131
|
+
|
132
|
+
# @return [Neo4j::Label] the label for this class
|
133
|
+
def mapped_label
|
134
|
+
Neo4j::Core::Label.new(mapped_label_name)
|
135
|
+
end
|
136
|
+
|
137
|
+
def base_class
|
138
|
+
unless self < Neo4j::ActiveNode
|
139
|
+
fail "#{name} doesn't belong in a hierarchy descending from ActiveNode"
|
140
|
+
end
|
141
|
+
|
142
|
+
if superclass == Object
|
143
|
+
self
|
144
|
+
else
|
145
|
+
superclass.base_class
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
protected
|
150
|
+
|
151
|
+
def mapped_labels
|
152
|
+
mapped_label_names.map { |label_name| Neo4j::Label.create(label_name) }
|
153
|
+
end
|
154
|
+
|
155
|
+
def mapped_label_name=(name)
|
156
|
+
@mapped_label_name = name.to_sym
|
157
|
+
end
|
158
|
+
|
159
|
+
# rubocop:disable Naming/AccessorMethodName
|
160
|
+
def set_mapped_label_name(name)
|
161
|
+
ActiveSupport::Deprecation.warn 'set_mapped_label_name is deprecated, use self.mapped_label_name= instead.', caller
|
162
|
+
|
163
|
+
self.mapped_label_name = name
|
164
|
+
end
|
165
|
+
# rubocop:enable Naming/AccessorMethodName
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
def find_by_id_or_ids(map_id, id)
|
170
|
+
if id.is_a?(Array)
|
171
|
+
find_by_ids(id.map(&map_id))
|
172
|
+
else
|
173
|
+
find_by_id(map_id.call(id))
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def find_callbacks!(result)
|
178
|
+
case result
|
179
|
+
when Neo4j::ActiveNode
|
180
|
+
result.run_callbacks(:find)
|
181
|
+
when Array
|
182
|
+
result.each { |r| find_callbacks!(r) }
|
183
|
+
else
|
184
|
+
result
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def label_for_model
|
189
|
+
(self.name.nil? ? object_id.to_s.to_sym : decorated_label_name)
|
190
|
+
end
|
191
|
+
|
192
|
+
def decorated_label_name
|
193
|
+
name = case Neo4j::Config[:module_handling]
|
194
|
+
when :demodulize
|
195
|
+
self.name.demodulize
|
196
|
+
when Proc
|
197
|
+
Neo4j::Config[:module_handling].call self.name
|
198
|
+
else
|
199
|
+
self.name
|
200
|
+
end
|
201
|
+
|
202
|
+
name.to_sym
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|