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,224 @@
|
|
|
1
|
+
module ActiveGraph::Node
|
|
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 ActiveGraph::Node
|
|
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 ActiveGraph::Node
|
|
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 ActiveGraph::Node
|
|
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
|
+
ActiveGraph::ModelSchema.add_defined_constraint(self, id_property_name)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def warn_constraint_option_false!(id_property_name)
|
|
205
|
+
ActiveGraph::Base.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 = ActiveGraph::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 ActiveGraph::Node::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 ActiveGraph::Node::Initialize
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
include ActiveGraph::Shared::Initialize
|
|
4
|
+
|
|
5
|
+
attr_reader :called_by
|
|
6
|
+
|
|
7
|
+
# called when loading the node from the database
|
|
8
|
+
# @param [ActiveGraph::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.properties)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
require 'active_graph/core/label'
|
|
2
|
+
|
|
3
|
+
module ActiveGraph
|
|
4
|
+
module Node
|
|
5
|
+
# Provides a mapping between neo4j labels and Ruby classes
|
|
6
|
+
module Labels
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
include ActiveGraph::Node::Labels::Index
|
|
9
|
+
include ActiveGraph::Node::Labels::Reloading
|
|
10
|
+
|
|
11
|
+
WRAPPED_CLASSES = []
|
|
12
|
+
MODELS_FOR_LABELS_CACHE = {}
|
|
13
|
+
MODELS_FOR_LABELS_CACHE.clear
|
|
14
|
+
|
|
15
|
+
included do |model|
|
|
16
|
+
ActiveGraph::Node::Labels.clear_wrapped_models
|
|
17
|
+
|
|
18
|
+
ActiveGraph::Node::Labels.add_wrapped_class(model) unless ActiveGraph::Node::Labels._wrapped_classes.include?(model)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class RecordNotFound < ActiveGraph::RecordNotFound; end
|
|
22
|
+
|
|
23
|
+
# @return the labels
|
|
24
|
+
# @see ActiveGraph::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 ActiveGraph::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 ActiveGraph::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
|
+
ActiveGraph::NodeWrapping::CONSTANTS_FOR_LABELS_CACHE.clear
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
module ClassMethods
|
|
83
|
+
include ActiveGraph::Node::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 [ActiveGraph::Label] the label for this class
|
|
133
|
+
def mapped_label
|
|
134
|
+
ActiveGraph::Core::Label.new(mapped_label_name)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def base_class
|
|
138
|
+
unless self < ActiveGraph::Node
|
|
139
|
+
fail "#{name} doesn't belong in a hierarchy descending from Node"
|
|
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| ActiveGraph::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 ActiveGraph::Node
|
|
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 ActiveGraph::Config[:module_handling]
|
|
194
|
+
when :demodulize
|
|
195
|
+
self.name.demodulize
|
|
196
|
+
when Proc
|
|
197
|
+
ActiveGraph::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
|