neo4j_legacy 7.2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1357 -0
  3. data/CONTRIBUTORS +8 -0
  4. data/Gemfile +38 -0
  5. data/README.md +103 -0
  6. data/bin/neo4j-jars +33 -0
  7. data/bin/rake +17 -0
  8. data/config/locales/en.yml +5 -0
  9. data/config/neo4j/add_classnames.yml +1 -0
  10. data/config/neo4j/config.yml +35 -0
  11. data/lib/active_support/per_thread_registry.rb +1 -0
  12. data/lib/backports/action_controller/metal/strong_parameters.rb +672 -0
  13. data/lib/backports/active_model/forbidden_attributes_protection.rb +30 -0
  14. data/lib/backports/active_support/concern.rb +13 -0
  15. data/lib/backports/active_support/core_ext/module/attribute_accessors.rb +10 -0
  16. data/lib/backports/active_support/logger.rb +99 -0
  17. data/lib/backports/active_support/logger_silence.rb +27 -0
  18. data/lib/backports/active_support/logger_thread_safe_level.rb +32 -0
  19. data/lib/backports/active_support/per_thread_registry.rb +60 -0
  20. data/lib/backports.rb +4 -0
  21. data/lib/neo4j/active_node/callbacks.rb +8 -0
  22. data/lib/neo4j/active_node/dependent/association_methods.rb +48 -0
  23. data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +50 -0
  24. data/lib/neo4j/active_node/dependent.rb +11 -0
  25. data/lib/neo4j/active_node/enum.rb +29 -0
  26. data/lib/neo4j/active_node/has_n/association/rel_factory.rb +61 -0
  27. data/lib/neo4j/active_node/has_n/association/rel_wrapper.rb +23 -0
  28. data/lib/neo4j/active_node/has_n/association.rb +280 -0
  29. data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +108 -0
  30. data/lib/neo4j/active_node/has_n.rb +532 -0
  31. data/lib/neo4j/active_node/id_property/accessor.rb +62 -0
  32. data/lib/neo4j/active_node/id_property.rb +187 -0
  33. data/lib/neo4j/active_node/initialize.rb +21 -0
  34. data/lib/neo4j/active_node/labels/index.rb +87 -0
  35. data/lib/neo4j/active_node/labels/reloading.rb +21 -0
  36. data/lib/neo4j/active_node/labels.rb +198 -0
  37. data/lib/neo4j/active_node/node_wrapper.rb +52 -0
  38. data/lib/neo4j/active_node/orm_adapter.rb +82 -0
  39. data/lib/neo4j/active_node/persistence.rb +175 -0
  40. data/lib/neo4j/active_node/property.rb +60 -0
  41. data/lib/neo4j/active_node/query/query_proxy.rb +361 -0
  42. data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +61 -0
  43. data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +90 -0
  44. data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +19 -0
  45. data/lib/neo4j/active_node/query/query_proxy_link.rb +117 -0
  46. data/lib/neo4j/active_node/query/query_proxy_methods.rb +210 -0
  47. data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +83 -0
  48. data/lib/neo4j/active_node/query.rb +76 -0
  49. data/lib/neo4j/active_node/query_methods.rb +65 -0
  50. data/lib/neo4j/active_node/reflection.rb +86 -0
  51. data/lib/neo4j/active_node/rels.rb +11 -0
  52. data/lib/neo4j/active_node/scope.rb +146 -0
  53. data/lib/neo4j/active_node/unpersisted.rb +48 -0
  54. data/lib/neo4j/active_node/validations.rb +59 -0
  55. data/lib/neo4j/active_node.rb +105 -0
  56. data/lib/neo4j/active_rel/callbacks.rb +15 -0
  57. data/lib/neo4j/active_rel/initialize.rb +28 -0
  58. data/lib/neo4j/active_rel/persistence/query_factory.rb +95 -0
  59. data/lib/neo4j/active_rel/persistence.rb +114 -0
  60. data/lib/neo4j/active_rel/property.rb +95 -0
  61. data/lib/neo4j/active_rel/query.rb +95 -0
  62. data/lib/neo4j/active_rel/rel_wrapper.rb +22 -0
  63. data/lib/neo4j/active_rel/related_node.rb +83 -0
  64. data/lib/neo4j/active_rel/types.rb +82 -0
  65. data/lib/neo4j/active_rel/validations.rb +8 -0
  66. data/lib/neo4j/active_rel.rb +67 -0
  67. data/lib/neo4j/class_arguments.rb +39 -0
  68. data/lib/neo4j/config.rb +124 -0
  69. data/lib/neo4j/core/query.rb +22 -0
  70. data/lib/neo4j/errors.rb +28 -0
  71. data/lib/neo4j/migration.rb +127 -0
  72. data/lib/neo4j/paginated.rb +27 -0
  73. data/lib/neo4j/railtie.rb +169 -0
  74. data/lib/neo4j/schema/operation.rb +91 -0
  75. data/lib/neo4j/shared/attributes.rb +220 -0
  76. data/lib/neo4j/shared/callbacks.rb +64 -0
  77. data/lib/neo4j/shared/cypher.rb +37 -0
  78. data/lib/neo4j/shared/declared_properties.rb +204 -0
  79. data/lib/neo4j/shared/declared_property/index.rb +37 -0
  80. data/lib/neo4j/shared/declared_property.rb +118 -0
  81. data/lib/neo4j/shared/enum.rb +148 -0
  82. data/lib/neo4j/shared/filtered_hash.rb +79 -0
  83. data/lib/neo4j/shared/identity.rb +28 -0
  84. data/lib/neo4j/shared/initialize.rb +28 -0
  85. data/lib/neo4j/shared/marshal.rb +23 -0
  86. data/lib/neo4j/shared/mass_assignment.rb +58 -0
  87. data/lib/neo4j/shared/permitted_attributes.rb +28 -0
  88. data/lib/neo4j/shared/persistence.rb +231 -0
  89. data/lib/neo4j/shared/property.rb +220 -0
  90. data/lib/neo4j/shared/query_factory.rb +101 -0
  91. data/lib/neo4j/shared/rel_type_converters.rb +43 -0
  92. data/lib/neo4j/shared/serialized_properties.rb +30 -0
  93. data/lib/neo4j/shared/type_converters.rb +418 -0
  94. data/lib/neo4j/shared/typecasted_attributes.rb +98 -0
  95. data/lib/neo4j/shared/typecaster.rb +53 -0
  96. data/lib/neo4j/shared/validations.rb +48 -0
  97. data/lib/neo4j/shared.rb +51 -0
  98. data/lib/neo4j/tasks/migration.rake +24 -0
  99. data/lib/neo4j/timestamps/created.rb +9 -0
  100. data/lib/neo4j/timestamps/updated.rb +9 -0
  101. data/lib/neo4j/timestamps.rb +11 -0
  102. data/lib/neo4j/type_converters.rb +7 -0
  103. data/lib/neo4j/version.rb +3 -0
  104. data/lib/neo4j/wrapper.rb +4 -0
  105. data/lib/neo4j.rb +96 -0
  106. data/lib/rails/generators/neo4j/model/model_generator.rb +86 -0
  107. data/lib/rails/generators/neo4j/model/templates/model.erb +15 -0
  108. data/lib/rails/generators/neo4j_generator.rb +67 -0
  109. data/neo4j.gemspec +43 -0
  110. metadata +389 -0
@@ -0,0 +1,532 @@
1
+ module Neo4j::ActiveNode
2
+ module HasN
3
+ extend ActiveSupport::Concern
4
+
5
+ class NonPersistedNodeError < Neo4j::Error; end
6
+
7
+ # Return this object from associations
8
+ # It uses a QueryProxy to get results
9
+ # But also caches results and can have results cached on it
10
+ class AssociationProxy
11
+ def initialize(query_proxy, deferred_objects = [], result_cache_proc = nil)
12
+ @query_proxy = query_proxy
13
+ @deferred_objects = deferred_objects
14
+
15
+ @result_cache_proc = result_cache_proc
16
+
17
+ # Represents the thing which can be enumerated
18
+ # default to @query_proxy, but will be set to
19
+ # @cached_result if that is set
20
+ @enumerable = @query_proxy
21
+ end
22
+
23
+ # States:
24
+ # Default
25
+ def inspect
26
+ if @cached_result
27
+ result_nodes.inspect
28
+ else
29
+ "#<AssociationProxy @query_proxy=#{@query_proxy.inspect}>"
30
+ end
31
+ end
32
+
33
+ extend Forwardable
34
+ %w(include? empty? count find first last ==).each do |delegated_method|
35
+ def_delegator :@enumerable, delegated_method
36
+ end
37
+
38
+ include Enumerable
39
+
40
+ def each(&block)
41
+ result_nodes.each(&block)
42
+ end
43
+
44
+ def ==(other)
45
+ self.to_a == other.to_a
46
+ end
47
+
48
+ def +(other)
49
+ self.to_a + other
50
+ end
51
+
52
+ def result
53
+ (@deferred_objects || []) + result_without_deferred
54
+ end
55
+
56
+ def result_without_deferred
57
+ cache_query_proxy_result if !@cached_result
58
+
59
+ @cached_result
60
+ end
61
+
62
+ def result_nodes
63
+ return result_objects if !@query_proxy.model
64
+
65
+ result_objects.map do |object|
66
+ object.is_a?(Neo4j::ActiveNode) ? object : @query_proxy.model.find(object)
67
+ end
68
+ end
69
+
70
+ def result_objects
71
+ @deferred_objects + result_without_deferred
72
+ end
73
+
74
+ def result_ids
75
+ result.map do |object|
76
+ object.is_a?(Neo4j::ActiveNode) ? object.id : object
77
+ end
78
+ end
79
+
80
+ def cache_result(result)
81
+ @cached_result = result
82
+ @enumerable = (@cached_result || @query_proxy)
83
+ end
84
+
85
+ def add_to_cache(object)
86
+ @cached_result ||= []
87
+ @cached_result << object
88
+ end
89
+
90
+ def cache_query_proxy_result
91
+ (result_cache_proc_cache || @query_proxy).to_a.tap { |result| cache_result(result) }
92
+ end
93
+
94
+ def result_cache_proc_cache
95
+ @result_cache_proc_cache ||= @result_cache_proc.call if @result_cache_proc
96
+ end
97
+
98
+ def clear_cache_result
99
+ cache_result(nil)
100
+ end
101
+
102
+ def cached?
103
+ !!@cached_result
104
+ end
105
+
106
+ def replace_with(*args)
107
+ @cached_result = nil
108
+
109
+ @query_proxy.public_send(:replace_with, *args)
110
+ end
111
+
112
+ QUERY_PROXY_METHODS = [:<<, :delete, :create, :pluck, :where, :where_not, :rel_where, :rel_order, :order, :skip, :limit]
113
+
114
+ QUERY_PROXY_METHODS.each do |method|
115
+ define_method(method) do |*args, &block|
116
+ @query_proxy.public_send(method, *args, &block)
117
+ end
118
+ end
119
+
120
+ CACHED_RESULT_METHODS = []
121
+
122
+ def method_missing(method_name, *args, &block)
123
+ target = target_for_missing_method(method_name)
124
+ super if target.nil?
125
+
126
+ cache_query_proxy_result if !cached? && !target.is_a?(Neo4j::ActiveNode::Query::QueryProxy)
127
+ clear_cache_result if target.is_a?(Neo4j::ActiveNode::Query::QueryProxy)
128
+
129
+ target.public_send(method_name, *args, &block)
130
+ end
131
+
132
+ def serializable_hash(options = {})
133
+ to_a.map { |record| record.serializable_hash(options) }
134
+ end
135
+
136
+ private
137
+
138
+ def target_for_missing_method(method_name)
139
+ case method_name
140
+ when *CACHED_RESULT_METHODS
141
+ @cached_result
142
+ else
143
+ if @cached_result && @cached_result.respond_to?(method_name)
144
+ @cached_result
145
+ elsif @query_proxy.respond_to?(method_name)
146
+ @query_proxy
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ # Returns the current AssociationProxy cache for the association cache. It is in the format
153
+ # { :association_name => AssociationProxy}
154
+ # This is so that we
155
+ # * don't need to re-build the QueryProxy objects
156
+ # * also because the QueryProxy object caches it's results
157
+ # * so we don't need to query again
158
+ # * so that we can cache results from association calls or eager loading
159
+ def association_proxy_cache
160
+ @association_proxy_cache ||= {}
161
+ end
162
+
163
+ def association_proxy_cache_fetch(key)
164
+ association_proxy_cache.fetch(key) do
165
+ value = yield
166
+ association_proxy_cache[key] = value
167
+ end
168
+ end
169
+
170
+ def association_query_proxy(name, options = {})
171
+ self.class.send(:association_query_proxy, name, {start_object: self}.merge!(options))
172
+ end
173
+
174
+ def association_proxy_hash(name, options = {})
175
+ [name.to_sym, options.values_at(:node, :rel, :labels, :rel_length)].hash
176
+ end
177
+
178
+ def association_proxy(name, options = {})
179
+ name = name.to_sym
180
+ hash = association_proxy_hash(name, options)
181
+ association_proxy_cache_fetch(hash) do
182
+ if result_cache = self.instance_variable_get('@source_proxy_result_cache')
183
+ cache = nil
184
+ result_cache.inject(nil) do |proxy_to_return, object|
185
+ proxy = fresh_association_proxy(name, options.merge(start_object: object),
186
+ proc { (cache ||= previous_proxy_results_by_previous_id(result_cache, name))[object.neo_id] })
187
+
188
+ object.association_proxy_cache[hash] = proxy
189
+
190
+ (self == object ? proxy : proxy_to_return)
191
+ end
192
+ else
193
+ fresh_association_proxy(name, options)
194
+ end
195
+ end
196
+ end
197
+
198
+ private
199
+
200
+ def fresh_association_proxy(name, options = {}, result_cache_proc = nil)
201
+ AssociationProxy.new(association_query_proxy(name, options), deferred_nodes_for_association(name), result_cache_proc)
202
+ end
203
+
204
+ def previous_proxy_results_by_previous_id(result_cache, association_name)
205
+ query_proxy = self.class.as(:previous).where(neo_id: result_cache.map(&:neo_id))
206
+ query_proxy = self.class.send(:association_query_proxy, association_name, previous_query_proxy: query_proxy, node: :next, optional: true)
207
+
208
+ Hash[*query_proxy.pluck('ID(previous)', 'collect(next)').flatten(1)].each do |_, records|
209
+ records.each do |record|
210
+ record.instance_variable_set('@source_proxy_result_cache', records)
211
+ end
212
+ end
213
+ end
214
+
215
+ module ClassMethods
216
+ # rubocop:disable Style/PredicateName
217
+
218
+ # :nocov:
219
+ def has_association?(name)
220
+ ActiveSupport::Deprecation.warn 'has_association? is deprecated and may be removed from future releases, use association? instead.', caller
221
+
222
+ association?(name)
223
+ end
224
+ # :nocov:
225
+
226
+ # rubocop:enable Style/PredicateName
227
+
228
+ def association?(name)
229
+ !!associations[name.to_sym]
230
+ end
231
+
232
+ def associations
233
+ @associations ||= {}
234
+ end
235
+
236
+ def associations_keys
237
+ @associations_keys ||= associations.keys
238
+ end
239
+
240
+ # make sure the inherited classes inherit the <tt>_decl_rels</tt> hash
241
+ def inherited(klass)
242
+ klass.instance_variable_set(:@associations, associations.clone)
243
+ @associations_keys = klass.associations_keys.clone
244
+ super
245
+ end
246
+
247
+ # For defining an "has many" association on a model. This defines a set of methods on
248
+ # your model instances. For instance, if you define the association on a Person model:
249
+ #
250
+ #
251
+ # .. code-block:: ruby
252
+ #
253
+ # has_many :out, :vehicles, type: :has_vehicle
254
+ #
255
+ # This would define the following methods:
256
+ #
257
+ # **#vehicles**
258
+ # Returns a QueryProxy object. This is an Enumerable object and thus can be iterated
259
+ # over. It also has the ability to accept class-level methods from the Vehicle model
260
+ # (including calls to association methods)
261
+ #
262
+ # **#vehicles=**
263
+ # Takes an array of Vehicle objects and replaces all current ``:HAS_VEHICLE`` relationships
264
+ # with new relationships refering to the specified objects
265
+ #
266
+ # **.vehicles**
267
+ # Returns a QueryProxy object. This would represent all ``Vehicle`` objects associated with
268
+ # either all ``Person`` nodes (if ``Person.vehicles`` is called), or all ``Vehicle`` objects
269
+ # associated with the ``Person`` nodes thus far represented in the QueryProxy chain.
270
+ # For example:
271
+ #
272
+ # .. code-block:: ruby
273
+ #
274
+ # company.people.where(age: 40).vehicles
275
+ #
276
+ # Arguments:
277
+ # **direction:**
278
+ # **Available values:** ``:in``, ``:out``, or ``:both``.
279
+ #
280
+ # Refers to the relative to the model on which the association is being defined.
281
+ #
282
+ # Example:
283
+ #
284
+ # .. code-block:: ruby
285
+ #
286
+ # Person.has_many :out, :posts, type: :wrote
287
+ #
288
+ # means that a `WROTE` relationship goes from a `Person` node to a `Post` node
289
+ #
290
+ # **name:**
291
+ # The name of the association. The affects the methods which are created (see above).
292
+ # The name is also used to form default assumptions about the model which is being referred to
293
+ #
294
+ # Example:
295
+ #
296
+ # .. code-block:: ruby
297
+ #
298
+ # Person.has_many :out, :posts, type: :wrote
299
+ #
300
+ # will assume a `model_class` option of ``'Post'`` unless otherwise specified
301
+ #
302
+ # **options:** A ``Hash`` of options. Allowed keys are:
303
+ # *type*: The Neo4j relationship type. This option is required unless either the
304
+ # `origin` or `rel_class` options are specified
305
+ #
306
+ # *origin*: The name of the association from another model which the `type` and `model_class`
307
+ # can be gathered.
308
+ #
309
+ # Example:
310
+ #
311
+ # .. code-block:: ruby
312
+ #
313
+ # # `model_class` of `Post` is assumed here
314
+ # Person.has_many :out, :posts, origin: :author
315
+ #
316
+ # Post.has_one :in, :author, type: :has_author, model_class: :Person
317
+ #
318
+ # *model_class*: The model class to which the association is referring. Can be a
319
+ # Symbol/String (or an ``Array`` of same) with the name of the `ActiveNode` class,
320
+ # `false` to specify any model, or nil to specify that it should be guessed.
321
+ #
322
+ # *rel_class*: The ``ActiveRel`` class to use for this association. Can be either a
323
+ # model object ``include`` ing ``ActiveRel`` or a Symbol/String (or an ``Array`` of same).
324
+ # **A Symbol or String is recommended** to avoid load-time issues
325
+ #
326
+ # *dependent*: Enables deletion cascading.
327
+ # **Available values:** ``:delete``, ``:delete_orphans``, ``:destroy``, ``:destroy_orphans``
328
+ # (note that the ``:destroy_orphans`` option is known to be "very metal". Caution advised)
329
+ #
330
+ def has_many(direction, name, options = {}) # rubocop:disable Style/PredicateName
331
+ name = name.to_sym
332
+ build_association(:has_many, direction, name, options)
333
+
334
+ define_has_many_methods(name)
335
+ end
336
+
337
+ # For defining an "has one" association on a model. This defines a set of methods on
338
+ # your model instances. For instance, if you define the association on a Person model:
339
+ #
340
+ # has_one :out, :vehicle, type: :has_vehicle
341
+ #
342
+ # This would define the methods: ``#vehicle``, ``#vehicle=``, and ``.vehicle``.
343
+ #
344
+ # See :ref:`#has_many <Neo4j/ActiveNode/HasN/ClassMethods#has_many>` for anything
345
+ # not specified here
346
+ #
347
+ def has_one(direction, name, options = {}) # rubocop:disable Style/PredicateName
348
+ name = name.to_sym
349
+ build_association(:has_one, direction, name, options)
350
+
351
+ define_has_one_methods(name)
352
+ end
353
+
354
+ private
355
+
356
+ def define_has_many_methods(name)
357
+ define_method(name) do |node = nil, rel = nil, options = {}|
358
+ # return [].freeze unless self._persisted_obj
359
+
360
+ options, node = node, nil if node.is_a?(Hash)
361
+
362
+ association_proxy(name, {node: node, rel: rel, source_object: self, labels: options[:labels]}.merge!(options))
363
+ end
364
+
365
+ define_has_many_setter(name)
366
+
367
+ define_has_many_id_methods(name)
368
+
369
+ define_class_method(name) do |node = nil, rel = nil, options = {}|
370
+ options, node = node, nil if node.is_a?(Hash)
371
+
372
+ association_proxy(name, {node: node, rel: rel, labels: options[:labels]}.merge!(options))
373
+ end
374
+ end
375
+
376
+ def define_has_many_setter(name)
377
+ define_method("#{name}=") do |other_nodes|
378
+ association_proxy_cache.clear
379
+
380
+ clear_deferred_nodes_for_association(name)
381
+
382
+ Neo4j::Transaction.run { association_proxy(name).replace_with(other_nodes) }
383
+ end
384
+ end
385
+
386
+ def define_has_many_id_methods(name)
387
+ define_method_unless_defined("#{name.to_s.singularize}_ids") do
388
+ association_proxy(name).result_ids
389
+ end
390
+
391
+ define_method_unless_defined("#{name.to_s.singularize}_ids=") do |ids|
392
+ clear_deferred_nodes_for_association(name)
393
+ association_proxy(name).replace_with(ids)
394
+ end
395
+
396
+ define_method_unless_defined("#{name.to_s.singularize}_neo_ids") do
397
+ association_proxy(name).pluck(:neo_id)
398
+ end
399
+ end
400
+
401
+ def define_method_unless_defined(method_name, &block)
402
+ define_method(method_name, block) unless method_defined?(method_name)
403
+ end
404
+
405
+ def define_has_one_methods(name)
406
+ define_has_one_getter(name)
407
+
408
+ define_has_one_setter(name)
409
+
410
+ define_has_one_id_methods(name)
411
+
412
+ define_class_method(name) do |node = nil, rel = nil, options = {}|
413
+ options, node = node, nil if node.is_a?(Hash)
414
+
415
+ association_proxy(name, {node: node, rel: rel, labels: options[:labels]}.merge!(options))
416
+ end
417
+ end
418
+
419
+ def define_has_one_id_methods(name)
420
+ define_method_unless_defined("#{name}_id") do
421
+ association_proxy(name).result_ids.first
422
+ end
423
+
424
+ define_method_unless_defined("#{name}_id=") do |id|
425
+ association_proxy(name).replace_with(id)
426
+ end
427
+
428
+ define_method_unless_defined("#{name}_neo_id") do
429
+ association_proxy(name).pluck(:neo_id).first
430
+ end
431
+ end
432
+
433
+ def define_has_one_getter(name)
434
+ define_method(name) do |node = nil, rel = nil, options = {}|
435
+ options, node = node, nil if node.is_a?(Hash)
436
+
437
+ # Return all results if a variable-length relationship length was given
438
+ association_proxy = association_proxy(name, {node: node, rel: rel}.merge!(options))
439
+ if options[:rel_length] && !options[:rel_length].is_a?(Fixnum)
440
+ association_proxy
441
+ else
442
+ target_class = self.class.send(:association_target_class, name)
443
+ o = association_proxy.result.first
444
+ if target_class
445
+ target_class.send(:nodeify, o)
446
+ else
447
+ o
448
+ end
449
+ end
450
+ end
451
+ end
452
+
453
+ def define_has_one_setter(name)
454
+ define_method("#{name}=") do |other_node|
455
+ if persisted?
456
+ other_node.save if other_node.respond_to?(:persisted?) && !other_node.persisted?
457
+ association_proxy_cache.clear # TODO: Should probably just clear for this association...
458
+ Neo4j::Transaction.run { association_proxy(name).replace_with(other_node) }
459
+ # handle_non_persisted_node(other_node)
460
+ else
461
+ defer_create(name, other_node, clear: true)
462
+ other_node
463
+ end
464
+ end
465
+ end
466
+
467
+ def define_class_method(*args, &block)
468
+ klass = class << self; self; end
469
+ klass.instance_eval do
470
+ define_method(*args, &block)
471
+ end
472
+ end
473
+
474
+ def association_query_proxy(name, options = {})
475
+ previous_query_proxy = options[:previous_query_proxy] || current_scope
476
+ query_proxy = previous_query_proxy || default_association_query_proxy
477
+ Neo4j::ActiveNode::Query::QueryProxy.new(association_target_class(name),
478
+ associations[name],
479
+ {session: neo4j_session,
480
+ query_proxy: query_proxy,
481
+ context: "#{query_proxy.context || self.name}##{name}",
482
+ optional: query_proxy.optional?,
483
+ association_labels: options[:labels],
484
+ source_object: query_proxy.source_object}.merge!(options)).tap do |query_proxy_result|
485
+ target_classes = association_target_classes(name)
486
+ return query_proxy_result.as_models(target_classes) if target_classes
487
+ end
488
+ end
489
+
490
+ def association_proxy(name, options = {})
491
+ AssociationProxy.new(association_query_proxy(name, options))
492
+ end
493
+
494
+ def association_target_class(name)
495
+ target_classes_or_nil = associations[name].target_classes_or_nil
496
+
497
+ return if !target_classes_or_nil.is_a?(Array) || target_classes_or_nil.size != 1
498
+
499
+ target_classes_or_nil[0]
500
+ end
501
+
502
+ def association_target_classes(name)
503
+ target_classes_or_nil = associations[name].target_classes_or_nil
504
+
505
+ return if !target_classes_or_nil.is_a?(Array) || target_classes_or_nil.size <= 1
506
+
507
+ target_classes_or_nil
508
+ end
509
+
510
+ def default_association_query_proxy
511
+ Neo4j::ActiveNode::Query::QueryProxy.new("::#{self.name}".constantize, nil,
512
+ session: neo4j_session, query_proxy: nil, context: "#{self.name}")
513
+ end
514
+
515
+ def build_association(macro, direction, name, options)
516
+ options[:model_class] = options[:model_class].name if options[:model_class] == self
517
+ Neo4j::ActiveNode::HasN::Association.new(macro, direction, name, options).tap do |association|
518
+ @associations ||= {}
519
+ @associations[name] = association
520
+ create_reflection(macro, name, association, self)
521
+ end
522
+
523
+ associations_keys << name
524
+
525
+ # Re-raise any exception with added class name and association name to
526
+ # make sure error message is helpful
527
+ rescue StandardError => e
528
+ raise e.class, "#{e.message} (#{self.class}##{name})"
529
+ end
530
+ end
531
+ end
532
+ 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