neo4j 5.0.15 → 5.1.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -5
  3. data/Gemfile +1 -1
  4. data/README.md +8 -0
  5. data/lib/neo4j.rb +4 -0
  6. data/lib/neo4j/active_node.rb +3 -1
  7. data/lib/neo4j/active_node/dependent/association_methods.rb +4 -2
  8. data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +3 -3
  9. data/lib/neo4j/active_node/has_n.rb +103 -36
  10. data/lib/neo4j/active_node/has_n/association.rb +10 -33
  11. data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +108 -0
  12. data/lib/neo4j/active_node/id_property.rb +19 -11
  13. data/lib/neo4j/active_node/id_property/accessor.rb +62 -0
  14. data/lib/neo4j/active_node/labels.rb +13 -2
  15. data/lib/neo4j/active_node/persistence.rb +19 -4
  16. data/lib/neo4j/active_node/property.rb +4 -3
  17. data/lib/neo4j/active_node/query/query_proxy.rb +29 -13
  18. data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +8 -0
  19. data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +7 -0
  20. data/lib/neo4j/active_node/query/query_proxy_link.rb +16 -6
  21. data/lib/neo4j/active_node/query/query_proxy_methods.rb +4 -0
  22. data/lib/neo4j/active_node/query/query_proxy_unpersisted.rb +17 -0
  23. data/lib/neo4j/active_node/unpersisted.rb +49 -0
  24. data/lib/neo4j/active_node/validations.rb +1 -1
  25. data/lib/neo4j/active_rel.rb +17 -0
  26. data/lib/neo4j/active_rel/persistence.rb +10 -5
  27. data/lib/neo4j/active_rel/property.rb +17 -5
  28. data/lib/neo4j/railtie.rb +2 -1
  29. data/lib/neo4j/shared/declared_property_manager.rb +10 -0
  30. data/lib/neo4j/shared/initialize.rb +3 -3
  31. data/lib/neo4j/shared/property.rb +7 -51
  32. data/lib/neo4j/shared/property/default_property.rb +0 -0
  33. data/lib/neo4j/shared/type_converters.rb +49 -6
  34. data/lib/neo4j/shared/typecaster.rb +22 -18
  35. data/lib/neo4j/shared/validations.rb +1 -1
  36. data/lib/neo4j/version.rb +1 -1
  37. data/neo4j.gemspec +1 -1
  38. metadata +12 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 614d5fb5803862fb5eabc3e58e19fa52dfd707eb
4
- data.tar.gz: 5c5aab03b675ae7253552b07282132885454eb6d
3
+ metadata.gz: 7f1d7952f8318fc3525d1e1bfbf90729ddcd02f5
4
+ data.tar.gz: aaea4557f810905866707c6c19e480b3816efbd0
5
5
  SHA512:
6
- metadata.gz: 357f69c5ae85ea3a29661c03ec8bee6ed04123256f1d147f771da7d36fbc224e82f3e403df76c93d2845f3fa8083d5bf73758d7b5ba573d89e638ec8cc34f09c
7
- data.tar.gz: c82b2d8b33fd3ac9a32880dede8fe2d9cf4aaa726b421f6e2452fb06ff5db71479edaa21614f3c6693f3eb9108b52805d9c74c6508d26ca40a0d3e3dd45f92d3
6
+ metadata.gz: eed94e2d7d99f61592a341e233995b730c312ad8c71bc460f59a8d2b34a4c0cb63b918e803fd2fd13d18f5c221a82d890961fdaac8bf2ed5a122d82252ee044b
7
+ data.tar.gz: bf2c82d7b1c138964010b6bb80f01b0cc0341babc279adb8ee2a4cd3921d384e064a399927309b5a1114437cb2b86f7e74b2539b742372eec29ec6d55661e5f4
data/CHANGELOG.md CHANGED
@@ -5,30 +5,83 @@ This project adheres to [Semantic Versioning](http://semver.org/).
5
5
 
6
6
  ## [Unreleased][unreleased]
7
7
 
8
- ### Changed
9
- - `ActiveNode#destroyed?` and `ActiveRel#destroyed?` now only consider the in-memory state of if an object is destroyed without checking the database
10
-
11
8
  ### Fixed
12
9
  - Added a `before_remove_const` method to clear cached models when Rails `reload!` is called. 5.0.1 included a workaround but this appears to cut to the core of the issue. See https://github.com/neo4jrb/neo4j/pull/855.
13
- - Fixed bug which caused `QueryProxy` context to repeat (showed up in query logging)
10
+ - To prevent errors, changing an index to constraint or constraint to index will drop the existing index/constraint before adding the new.
11
+ - Fixed `AssociationProxy#method_missing` so it properly raises errors.
14
12
 
15
13
  ### Added
16
14
  - Added ability to view `model_class` from `Association` class for `rails_admin` Neo4j adapter
17
15
  - QueryProxy `where` will now look for declared properties matching hash keys. When found, it will send the value through that property's type converter if the type matches the property's unconverted state.
18
16
  - Improved handling of unpersisted nodes with associations. You can now use `<<` to create associations between unpersisted nodes. A `save` will cascade through unpersisted objects, creating nodes and rels along the way. See https://github.com/neo4jrb/neo4j/pull/871
19
17
  - Support formatted cypher queries for easy reading by humans via the `pretty_logged_cypher_queries` configuration variable
18
+ - Ability to query for just IDs on associations
20
19
  - On `QueryProxy` objects you can now use an `:id` key in `where` and `find_by` methods to refer to the property from `id_property` (`uuid` by default)
20
+ - Added `ActiveRel.creates_unique` and deprecated `ActiveRel.creates_unique_rel`
21
+ - Added #inspect method to ActiveRel to show Cypher-style representation of from node, to node, and relationship type
22
+
23
+ ### Changed
24
+
25
+ - Methods related to ActiveNode's IdProperty module were refactored to improve performance and simplify the API. Existing `default_properties` methods were reworked to reflect their use as-implemented: storage for a single default property, not multiple.
26
+ - Implementation adjustments that improve node and rel initialization speed, particularly when loading large numbers of objects from the database.
27
+
28
+ ## [5.0.15] - 08-12-2015
29
+
30
+ ### Fixed
31
+
32
+ - `reload!` within Rails apps will work correctly. An earlier release included a workaround but this uses ActiveModel's system for clearing caches to provide a more thorough resolution.
33
+
34
+ ## [5.0.14] - 08-09-2015
35
+
36
+ ### Fixed
37
+
38
+ - Calling `all` on a QueryProxy chain would cause the currently set node identity within Cypher to be lost.
39
+
40
+ ## [5.0.13] - 08-07-2015
41
+
42
+ ### Fixed
43
+ - Backport AssociationProxy#method_missing fix to raise errors on invalid methods
44
+ - Fix the count issue on depth two associations (#881)
45
+
46
+ ## [5.0.12] - ?
47
+
48
+ ### Fixed
49
+ - Break between associations so that potential `where` clauses get applied to the correct `(OPTIONAL )MATCH` clause
50
+
51
+ ### Fixed
52
+ - Delegate `first` and `last` from `AssociationProxy` to `QueryProxy`
53
+ - Fix `order` behavior for `first` and `last` in `QueryProxy`
54
+
55
+ ## [5.0.11] - ?
56
+
57
+ ### Fixed
58
+ - Delegate `first` and `last` from `AssociationProxy` to `QueryProxy`
59
+ - Fix `order` behavior for `first` and `last` in `QueryProxy`
60
+
61
+ ## [5.0.10] - 2015-07-31
62
+
63
+ ### Fixed
64
+ - Fix what should have been a very obvious bug in `_active_record_destroyed_behavior` behavior
65
+ - Add eager loading to QueryProxy so that it works in all expected places
21
66
 
22
67
  ## [5.0.9] - 2015-07-29
23
- - Fix "NameError: uninitialized constant Class::Date" (https://github.com/neo4jrb/neo4j/issues/852)
68
+
69
+ ### Fixed
70
+ - "NameError: uninitialized constant Class::Date" (https://github.com/neo4jrb/neo4j/issues/852)
24
71
 
25
72
  ## [5.0.8] - 2015-07-26
73
+
74
+ ### Changed
26
75
  - Copied QueryClauseMethods doc from master
27
76
 
28
77
  ## [5.0.7] - 2015-07-26
78
+
79
+ ### Changed
29
80
  - Copied `docs` folder from master because a lot of work had gone into the docs since 5.0.0 was released
30
81
 
31
82
  ## [5.0.6] - 2015-07-22
83
+
84
+ ### Fixed
32
85
  - Fix query logging so that by default it only outputs to the user in the console and development server. Logger can be changed with `neo4j.config.logger` configuration option
33
86
 
34
87
  ## [5.0.5] - 2015-07-19
@@ -45,6 +98,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
45
98
 
46
99
  ### Changed
47
100
  - Moved `#with_associations` method from `AssociationProxy` to `QueryProxy` so that all `QueryProxy` chains can benefit from it.
101
+ - Added `_active_record_destroyed_behavior` semi-hidden configuration variable so that behavior for `ActiveNode#destroyed?` and `ActiveRel#destroyed?` can be changed to upcoming 6.0.0 behavior (matching ActiveRecord) where the database is not accessed.
48
102
 
49
103
  ## [5.0.2] - 2015-06-30
50
104
 
@@ -52,6 +106,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
52
106
  - Fix error when calling `#empty?` or `#blank?` on a query chain with on `order` specified
53
107
  - Make `#find_each` and `#find_in_batches` return the actual objects rather than the result objects
54
108
  - Query logging on console should be to STDOUT with `puts`. Using `Rails.logger` outputs to the file in the `log` directory
109
+ - Modified queryproxy include? to accept a uuid instead of full node
55
110
 
56
111
  ## [5.0.1] - 2015-06-23
57
112
 
data/Gemfile CHANGED
@@ -2,7 +2,7 @@ source 'http://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- # gem 'neo4j-core', github: 'neo4jrb/neo4j-core', branch: 'master'
5
+ gem 'neo4j-core', github: 'neo4jrb/neo4j-core', branch: '5.1.x'
6
6
  # gem 'neo4j-core', path: '../neo4j-core'
7
7
 
8
8
  # gem 'active_attr', github: 'neo4jrb/active_attr', branch: 'performance'
data/README.md CHANGED
@@ -12,8 +12,16 @@
12
12
 
13
13
  ## Get Support
14
14
 
15
+ ### Documentation
16
+
17
+ All new documentation will be done via our [readthedocs](http://neo4jrb.readthedocs.org) site, though some old documentation has yet to be moved from our [wiki](https://github.com/neo4jrb/neo4j/wiki) (also there is the [neo4j-core wiki](https://github.com/neo4jrb/neo4j-core/wiki))
18
+
19
+ ### Contact Us
20
+
15
21
  [![StackOverflow](https://img.shields.io/badge/StackOverflow-Ask%20a%20question!-blue.svg)](http://stackoverflow.com/questions/ask?tags=neo4j.rb+neo4j+ruby) [![Gitter](https://img.shields.io/badge/Gitter-Join%20our%20chat!-blue.svg)](https://gitter.im/neo4jrb/neo4j?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Twitter](https://img.shields.io/badge/Twitter-Tweet%20with%20us!-blue.svg)](https://twitter.com/neo4jrb) [![Mailing list](https://img.shields.io/badge/Mailing%20list-Mail%20us!-blue.svg)](https://groups.google.com/forum/#!forum/neo4jrb)
16
22
 
23
+
24
+
17
25
  # Introduction
18
26
 
19
27
  Neo4j.rb is an Active Model compliant Ruby/JRuby wrapper for [the Neo4j graph database](http://www.neo4j.org/). It uses the [neo4j-core](https://github.com/neo4jrb/neo4j-core) and [active_attr](https://github.com/cgriego/active_attr) gems.
data/lib/neo4j.rb CHANGED
@@ -52,12 +52,14 @@ require 'neo4j/active_node/dependent'
52
52
  require 'neo4j/active_node/dependent/query_proxy_methods'
53
53
  require 'neo4j/active_node/dependent/association_methods'
54
54
  require 'neo4j/active_node/query_methods'
55
+ require 'neo4j/active_node/query/query_proxy_unpersisted'
55
56
  require 'neo4j/active_node/query/query_proxy_methods'
56
57
  require 'neo4j/active_node/query/query_proxy_enumerable'
57
58
  require 'neo4j/active_node/query/query_proxy_find_in_batches'
58
59
  require 'neo4j/active_node/query/query_proxy_eager_loading'
59
60
  require 'neo4j/active_node/query/query_proxy_link'
60
61
  require 'neo4j/active_node/labels'
62
+ require 'neo4j/active_node/id_property/accessor'
61
63
  require 'neo4j/active_node/id_property'
62
64
  require 'neo4j/active_node/callbacks'
63
65
  require 'neo4j/active_node/initialize'
@@ -66,7 +68,9 @@ require 'neo4j/active_node/persistence'
66
68
  require 'neo4j/active_node/validations'
67
69
  require 'neo4j/active_node/rels'
68
70
  require 'neo4j/active_node/reflection'
71
+ require 'neo4j/active_node/unpersisted'
69
72
  require 'neo4j/active_node/has_n'
73
+ require 'neo4j/active_node/has_n/association_cypher_methods'
70
74
  require 'neo4j/active_node/has_n/association'
71
75
  require 'neo4j/active_node/query/query_proxy'
72
76
  require 'neo4j/active_node/query'
@@ -36,6 +36,7 @@ module Neo4j
36
36
  include Neo4j::ActiveNode::Query
37
37
  include Neo4j::ActiveNode::Labels
38
38
  include Neo4j::ActiveNode::Rels
39
+ include Neo4j::ActiveNode::Unpersisted
39
40
  include Neo4j::ActiveNode::HasN
40
41
  include Neo4j::ActiveNode::Scope
41
42
  include Neo4j::ActiveNode::Dependent
@@ -75,7 +76,7 @@ module Neo4j
75
76
 
76
77
  def self.inherit_id_property(other)
77
78
  Neo4j::Session.on_session_available do |_|
78
- return unless self.id_property?
79
+ next if other.manual_id_property? || !self.id_property?
79
80
  id_prop = self.id_property_info
80
81
  conf = id_prop[:type].empty? ? {auto: :uuid} : id_prop[:type]
81
82
  other.id_property id_prop[:name], conf
@@ -83,6 +84,7 @@ module Neo4j
83
84
  end
84
85
 
85
86
  Neo4j::Session.on_session_available do |_|
87
+ next if manual_id_property?
86
88
  id_property :uuid, auto: :uuid unless self.id_property?
87
89
 
88
90
  name = Neo4j::Config[:id_property]
@@ -32,11 +32,13 @@ module Neo4j
32
32
  end
33
33
 
34
34
  def dependent_destroy_callback(object)
35
- object.association_query_proxy(name).each_for_destruction(object, &:destroy)
35
+ unique_query = object.association_query_proxy(name)
36
+ unique_query.each_for_destruction(object, &:destroy) if unique_query
36
37
  end
37
38
 
38
39
  def dependent_destroy_orphans_callback(object)
39
- object.as(:self).unique_nodes(self, :self, :n, :other_rel).each_for_destruction(object, &:destroy)
40
+ unique_query = object.as(:self).unique_nodes(self, :self, :n, :other_rel)
41
+ unique_query.each_for_destruction(object, &:destroy) if unique_query
40
42
  end
41
43
 
42
44
  # End callback methods
@@ -28,7 +28,7 @@ module Neo4j
28
28
  # @return [Neo4j::ActiveNode::Query::QueryProxy]
29
29
  def unique_nodes(association, self_identifer, other_node, other_rel)
30
30
  fail 'Only supported by in QueryProxy chains started by an instance' unless source_object
31
-
31
+ return false if send(association.name).empty?
32
32
  unique_nodes_query(association, self_identifer, other_node, other_rel)
33
33
  .proxy_as(association.target_class, other_node)
34
34
  end
@@ -40,9 +40,9 @@ module Neo4j
40
40
  .send(association.name, other_node, other_rel)
41
41
  .query
42
42
  .with(other_node)
43
- .match("()#{association.arrow_cypher}(#{other_node})")
43
+ .match("()#{association.arrow_cypher(:orphan_rel)}(#{other_node})")
44
44
  .with(other_node, count: 'count(*)')
45
- .where('count = 1')
45
+ .where('count = {one}', one: 1)
46
46
  end
47
47
  end
48
48
  end
@@ -24,7 +24,7 @@ module Neo4j::ActiveNode
24
24
  if @cached_result
25
25
  @cached_result.inspect
26
26
  else
27
- "<AssociationProxy @query_proxy=#{@query_proxy.inspect}>"
27
+ "#<AssociationProxy @query_proxy=#{@query_proxy.inspect}>"
28
28
  end
29
29
  end
30
30
 
@@ -121,7 +121,7 @@ module Neo4j::ActiveNode
121
121
 
122
122
  def association_proxy(name, options = {})
123
123
  name = name.to_sym
124
- hash = [name, options.values_at(:node, :rel, :labels)].hash
124
+ hash = [name, options.values_at(:node, :rel, :labels, :rel_length)].hash
125
125
  association_proxy_cache_fetch(hash) do
126
126
  if result_cache = self.instance_variable_get('@source_query_proxy_result_cache')
127
127
  result_by_previous_id = previous_proxy_results_by_previous_id(result_cache, name)
@@ -152,27 +152,19 @@ module Neo4j::ActiveNode
152
152
  Hash[*query_proxy.pluck('ID(previous)', 'collect(next)').flatten(1)]
153
153
  end
154
154
 
155
- def handle_non_persisted_node(other_node)
156
- return unless Neo4j::Config[:autosave_on_assignment]
157
- other_node.try(:save)
158
- save
159
- end
160
-
161
- def validate_persisted_for_association!
162
- fail(Neo4j::ActiveNode::HasN::NonPersistedNodeError, 'Unable to create relationship with non-persisted nodes') unless self._persisted_obj
163
- end
164
-
165
155
  module ClassMethods
166
- # :nocov:
167
156
  # rubocop:disable Style/PredicateName
157
+
158
+ # :nocov:
168
159
  def has_association?(name)
169
160
  ActiveSupport::Deprecation.warn 'has_association? is deprecated and may be removed from future releases, use association? instead.', caller
170
161
 
171
162
  association?(name)
172
163
  end
173
- # rubocop:enable Style/PredicateName
174
164
  # :nocov:
175
165
 
166
+ # rubocop:enable Style/PredicateName
167
+
176
168
  def association?(name)
177
169
  !!associations[name.to_sym]
178
170
  end
@@ -195,7 +187,10 @@ module Neo4j::ActiveNode
195
187
  # For defining an "has many" association on a model. This defines a set of methods on
196
188
  # your model instances. For instance, if you define the association on a Person model:
197
189
  #
198
- # has_many :out, :vehicles, type: :has_vehicle
190
+ #
191
+ # .. code-block:: ruby
192
+ #
193
+ # has_many :out, :vehicles, type: :has_vehicle
199
194
  #
200
195
  # This would define the following methods:
201
196
  #
@@ -213,7 +208,10 @@ module Neo4j::ActiveNode
213
208
  # either all ``Person`` nodes (if ``Person.vehicles`` is called), or all ``Vehicle`` objects
214
209
  # associated with the ``Person`` nodes thus far represented in the QueryProxy chain.
215
210
  # For example:
216
- # ``company.people.where(age: 40).vehicles``
211
+ #
212
+ # .. code-block:: ruby
213
+ #
214
+ # company.people.where(age: 40).vehicles
217
215
  #
218
216
  # Arguments:
219
217
  # **direction:**
@@ -222,18 +220,24 @@ module Neo4j::ActiveNode
222
220
  # Refers to the relative to the model on which the association is being defined.
223
221
  #
224
222
  # Example:
225
- # ``Person.has_many :out, :posts, type: :wrote``
226
223
  #
227
- # means that a `WROTE` relationship goes from a `Person` node to a `Post` node
224
+ # .. code-block:: ruby
225
+ #
226
+ # Person.has_many :out, :posts, type: :wrote
227
+ #
228
+ # means that a `WROTE` relationship goes from a `Person` node to a `Post` node
228
229
  #
229
230
  # **name:**
230
231
  # The name of the association. The affects the methods which are created (see above).
231
232
  # The name is also used to form default assumptions about the model which is being referred to
232
233
  #
233
234
  # Example:
234
- # ``Person.has_many :out, :posts, type: :wrote``
235
235
  #
236
- # will assume a `model_class` option of ``'Post'`` unless otherwise specified
236
+ # .. code-block:: ruby
237
+ #
238
+ # Person.has_many :out, :posts, type: :wrote
239
+ #
240
+ # will assume a `model_class` option of ``'Post'`` unless otherwise specified
237
241
  #
238
242
  # **options:** A ``Hash`` of options. Allowed keys are:
239
243
  # *type*: The Neo4j relationship type. This option is required unless either the
@@ -243,17 +247,21 @@ module Neo4j::ActiveNode
243
247
  # can be gathered.
244
248
  #
245
249
  # Example:
246
- # ``Person.has_many :out, :posts, origin: :author`` (`model_class` of `Post` is assumed here)
247
250
  #
248
- # ``Post.has_one :in, :author, type: :has_author, model_class: 'Person'``
251
+ # .. code-block:: ruby
252
+ #
253
+ # # `model_class` of `Post` is assumed here
254
+ # Person.has_many :out, :posts, origin: :author
255
+ #
256
+ # Post.has_one :in, :author, type: :has_author, model_class: 'Person'
249
257
  #
250
258
  # *model_class*: The model class to which the association is referring. Can be either a
251
- # model object ``include`` ing ``ActiveNode`` or a string (or an ``Array`` of same).
252
- # **A string is recommended** to avoid load-time issues
259
+ # model object ``include`` ing ``ActiveNode`` or a Symbol/String (or an ``Array`` of same).
260
+ # **A Symbol or String is recommended** to avoid load-time issues
253
261
  #
254
262
  # *rel_class*: The ``ActiveRel`` class to use for this association. Can be either a
255
- # model object ``include`` ing ``ActiveRel`` or a string (or an ``Array`` of same).
256
- # **A string is recommended** to avoid load-time issues
263
+ # model object ``include`` ing ``ActiveRel`` or a Symbol/String (or an ``Array`` of same).
264
+ # **A Symbol or String is recommended** to avoid load-time issues
257
265
  #
258
266
  # *dependent*: Enables deletion cascading.
259
267
  # **Available values:** ``:delete``, ``:delete_orphans``, ``:destroy``, ``:destroy_orphans``
@@ -287,13 +295,20 @@ module Neo4j::ActiveNode
287
295
 
288
296
  def define_has_many_methods(name)
289
297
  define_method(name) do |node = nil, rel = nil, options = {}|
290
- return [].freeze unless self._persisted_obj
298
+ # return [].freeze unless self._persisted_obj
299
+
300
+ if node.is_a?(Hash)
301
+ options = node
302
+ node = nil
303
+ end
291
304
 
292
305
  association_proxy(name, {node: node, rel: rel, source_object: self, labels: options[:labels]}.merge!(options))
293
306
  end
294
307
 
295
308
  define_has_many_setter(name)
296
309
 
310
+ define_has_many_id_methods(name)
311
+
297
312
  define_class_method(name) do |node = nil, rel = nil, options = {}|
298
313
  association_proxy(name, {node: node, rel: rel, labels: options[:labels]}.merge!(options))
299
314
  end
@@ -307,27 +322,79 @@ module Neo4j::ActiveNode
307
322
  end
308
323
  end
309
324
 
310
- def define_has_one_methods(name)
311
- define_method(name) do |node = nil, rel = nil|
312
- return nil unless self._persisted_obj
325
+ def define_has_many_id_methods(name)
326
+ define_method_unless_defined("#{name.to_s.singularize}_ids") do
327
+ association_proxy(name).pluck(:uuid)
328
+ end
313
329
 
314
- association_proxy(name, node: node, rel: rel).first
330
+ define_method_unless_defined("#{name.to_s.singularize}_ids=") do |ids|
331
+ association_proxy(name).replace_with(ids)
315
332
  end
316
333
 
334
+ define_method_unless_defined("#{name.to_s.singularize}_neo_ids") do
335
+ association_proxy(name).pluck(:neo_id)
336
+ end
337
+ end
338
+
339
+ def define_method_unless_defined(method_name, &block)
340
+ define_method(method_name, block) unless respond_to?(method_name)
341
+ end
342
+
343
+ def define_has_one_methods(name)
344
+ define_has_one_getter(name)
345
+
317
346
  define_has_one_setter(name)
318
347
 
348
+ define_has_one_id_methods(name)
349
+
319
350
  define_class_method(name) do |node = nil, rel = nil, options = {}|
320
351
  association_proxy(name, {node: node, rel: rel, labels: options[:labels]}.merge!(options))
321
352
  end
322
353
  end
323
354
 
355
+ def define_has_one_id_methods(name)
356
+ define_method("#{name}_id") do
357
+ association_proxy(name).pluck(:uuid).first
358
+ end
359
+
360
+ define_method_unless_defined("#{name}_id=") do |id|
361
+ association_proxy(name).replace_with(id)
362
+ end
363
+
364
+ define_method("#{name}_neo_id") do
365
+ association_proxy(name).pluck(:neo_id).first
366
+ end
367
+ end
368
+
369
+ def define_has_one_getter(name)
370
+ define_method(name) do |node = nil, rel = nil, options = {}|
371
+ return nil unless self._persisted_obj
372
+
373
+ if node.is_a?(Hash)
374
+ options = node
375
+ node = nil
376
+ end
377
+
378
+ # Return all results if a variable-length relationship length was given
379
+ results = association_proxy(name, {node: node, rel: rel}.merge!(options))
380
+ if options[:rel_length] && !options[:rel_length].is_a?(Fixnum)
381
+ results
382
+ else
383
+ results.first
384
+ end
385
+ end
386
+ end
387
+
324
388
  def define_has_one_setter(name)
325
389
  define_method("#{name}=") do |other_node|
326
- handle_non_persisted_node(other_node)
327
- validate_persisted_for_association!
328
- association_proxy_cache.clear # TODO: Should probably just clear for this association...
329
-
330
- Neo4j::Transaction.run { association_proxy(name).replace_with(other_node) }
390
+ if persisted?
391
+ other_node.save if other_node.respond_to?(:persisted?) && !other_node.persisted?
392
+ association_proxy_cache.clear # TODO: Should probably just clear for this association...
393
+ Neo4j::Transaction.run { association_proxy(name).replace_with(other_node) }
394
+ # handle_non_persisted_node(other_node)
395
+ else
396
+ association_proxy(name).defer_create(other_node, {}, :'=')
397
+ end
331
398
  end
332
399
  end
333
400