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
@@ -6,6 +6,7 @@ module Neo4j
6
6
  class Association
7
7
  include Neo4j::Shared::RelTypeConverters
8
8
  include Neo4j::ActiveNode::Dependent::AssociationMethods
9
+ include Neo4j::ActiveNode::HasN::AssociationCypherMethods
9
10
 
10
11
  attr_reader :type, :name, :relationship, :direction, :dependent, :model_class
11
12
 
@@ -29,7 +30,9 @@ module Neo4j
29
30
 
30
31
  def refresh_model_class!
31
32
  @pending_model_refresh = @target_classes_or_nil = nil
32
- @model_class = @model_class.name.constantize if @model_class
33
+
34
+ # Using #to_s on purpose here to take care of classes/strings/symbols
35
+ @model_class = @model_class.to_s.constantize if @model_class
33
36
  end
34
37
 
35
38
  def queue_model_refresh!
@@ -49,12 +52,6 @@ module Neo4j
49
52
  end
50
53
  end
51
54
 
52
- # Return cypher partial query string for the relationship part of a MATCH (arrow / relationship definition)
53
- def arrow_cypher(var = nil, properties = {}, create = false, reverse = false)
54
- validate_origin!
55
- direction_cypher(get_relationship_cypher(var, properties, create), create, reverse)
56
- end
57
-
58
55
  def pending_model_refresh?
59
56
  !!@pending_model_refresh
60
57
  end
@@ -110,9 +107,12 @@ module Neo4j
110
107
 
111
108
  def relationship_type(create = false)
112
109
  case
113
- when relationship_class then relationship_class_type
114
- when @relationship_type then @relationship_type
115
- when @origin then origin_type
110
+ when relationship_class
111
+ relationship_class_type
112
+ when @relationship_type
113
+ @relationship_type
114
+ when @origin
115
+ origin_type
116
116
  else
117
117
  (create || exceptional_target_class?) && decorated_rel_type(@name)
118
118
  end
@@ -148,14 +148,6 @@ module Neo4j
148
148
  Neo4j::Config.association_model_namespace_string
149
149
  end
150
150
 
151
- def direction_cypher(relationship_cypher, create, reverse = false)
152
- case get_direction(create, reverse)
153
- when :out then "-#{relationship_cypher}->"
154
- when :in then "<-#{relationship_cypher}-"
155
- when :both then "-#{relationship_cypher}-"
156
- end
157
- end
158
-
159
151
  def get_direction(create, reverse = false)
160
152
  dir = (create && @direction == :both) ? :out : @direction
161
153
  if reverse
@@ -169,21 +161,6 @@ module Neo4j
169
161
  end
170
162
  end
171
163
 
172
- def get_relationship_cypher(var, properties, create)
173
- relationship_type = relationship_type(create)
174
- relationship_name_cypher = ":`#{relationship_type}`" if relationship_type
175
- properties_string = get_properties_string(properties)
176
-
177
- "[#{var}#{relationship_name_cypher}#{properties_string}]"
178
- end
179
-
180
- def get_properties_string(properties)
181
- p = properties.map do |key, value|
182
- "#{key}: #{value.inspect}"
183
- end.join(', ')
184
- p.size == 0 ? '' : " {#{p}}"
185
- end
186
-
187
164
  def origin_association
188
165
  target_class.associations[@origin]
189
166
  end
@@ -0,0 +1,108 @@
1
+ module Neo4j
2
+ module ActiveNode
3
+ module HasN
4
+ module AssociationCypherMethods
5
+ # Return cypher partial query string for the relationship part of a MATCH (arrow / relationship definition)
6
+ def arrow_cypher(var = nil, properties = {}, create = false, reverse = false, length = nil)
7
+ validate_origin!
8
+
9
+ if create && length.present?
10
+ fail(ArgumentError, 'rel_length option cannot be specified when creating a relationship')
11
+ end
12
+
13
+ direction_cypher(get_relationship_cypher(var, properties, create, length), create, reverse)
14
+ end
15
+
16
+ private
17
+
18
+ def direction_cypher(relationship_cypher, create, reverse = false)
19
+ case get_direction(create, reverse)
20
+ when :out
21
+ "-#{relationship_cypher}->"
22
+ when :in
23
+ "<-#{relationship_cypher}-"
24
+ when :both
25
+ "-#{relationship_cypher}-"
26
+ end
27
+ end
28
+
29
+ def get_relationship_cypher(var, properties, create, length)
30
+ relationship_type = relationship_type(create)
31
+ relationship_name_cypher = ":`#{relationship_type}`" if relationship_type
32
+ rel_length_cypher = cypher_for_rel_length(length)
33
+ properties_string = get_properties_string(properties)
34
+
35
+ "[#{var}#{relationship_name_cypher}#{rel_length_cypher}#{properties_string}]"
36
+ end
37
+
38
+ def get_properties_string(properties)
39
+ p = properties.map do |key, value|
40
+ "#{key}: #{value.inspect}"
41
+ end.join(', ')
42
+ p.size == 0 ? '' : " {#{p}}"
43
+ end
44
+
45
+ VALID_REL_LENGTH_SYMBOLS = {
46
+ any: '*'
47
+ }
48
+
49
+ def cypher_for_rel_length(length)
50
+ return nil if length.blank?
51
+
52
+ validate_rel_length!(length)
53
+
54
+ case length
55
+ when Symbol then VALID_REL_LENGTH_SYMBOLS[length]
56
+ when Fixnum then "*#{length}"
57
+ when Range then cypher_for_range_rel_length(length)
58
+ when Hash then cypher_for_hash_rel_length(length)
59
+ end
60
+ end
61
+
62
+ def cypher_for_range_rel_length(length)
63
+ range_end = length.end
64
+ range_end = nil if range_end == Float::INFINITY
65
+ "*#{length.begin}..#{range_end}"
66
+ end
67
+
68
+ def cypher_for_hash_rel_length(length)
69
+ range_end = length[:max]
70
+ range_end = nil if range_end == Float::INFINITY
71
+ "*#{length[:min]}..#{range_end}"
72
+ end
73
+
74
+ def validate_rel_length!(length)
75
+ message = rel_length_error_message(length)
76
+ fail(ArgumentError, "Invalid value for rel_length (#{length.inspect}): #{message}") if message
77
+ true
78
+ end
79
+
80
+ def rel_length_error_message(length)
81
+ case length
82
+ when Fixnum then 'cannot be negative' if length < 0
83
+ when Symbol then rel_length_symbol_error_message(length)
84
+ when Range then rel_length_range_error_message(length)
85
+ when Hash then rel_length_hash_error_message(length)
86
+ else 'should be a Symbol, Fixnum, Range or Hash'
87
+ end
88
+ end
89
+
90
+ def rel_length_symbol_error_message(length)
91
+ "expecting one of #{VALID_REL_LENGTH_SYMBOLS.keys.inspect}" if !VALID_REL_LENGTH_SYMBOLS.key?(length)
92
+ end
93
+
94
+ def rel_length_range_error_message(length)
95
+ if length.begin > length.end
96
+ 'cannot be a decreasing Range'
97
+ elsif length.begin < 0
98
+ 'cannot include negative values'
99
+ end
100
+ end
101
+
102
+ def rel_length_hash_error_message(length)
103
+ 'Hash keys should be a subset of [:min, :max]' if (length.keys & [:min, :max]) != length.keys
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -34,7 +34,7 @@ module Neo4j::ActiveNode
34
34
  #
35
35
  module IdProperty
36
36
  extend ActiveSupport::Concern
37
-
37
+ include Accessor
38
38
 
39
39
  module TypeMethods
40
40
  def define_id_methods(clazz, name, conf)
@@ -90,7 +90,7 @@ module Neo4j::ActiveNode
90
90
  end
91
91
 
92
92
  def #{name}
93
- default_property :#{name}
93
+ default_property_value
94
94
  end
95
95
 
96
96
  alias_method :id, :#{name}
@@ -102,12 +102,12 @@ module Neo4j::ActiveNode
102
102
 
103
103
  clazz.module_eval(%{
104
104
  default_property :#{name} do |instance|
105
- raise "Specifying custom id_property #{name} on none existing method #{on}" unless instance.respond_to?(:#{on})
106
- instance.#{on}
105
+ raise "Specifying custom id_property #{name} on non-existent method #{on}" unless instance.respond_to?(:#{on})
106
+ instance.#{on}
107
107
  end
108
108
 
109
109
  def #{name}
110
- default_property :#{name}
110
+ default_property_value
111
111
  end
112
112
 
113
113
  alias_method :id, :#{name}
@@ -124,6 +124,8 @@ module Neo4j::ActiveNode
124
124
 
125
125
 
126
126
  module ClassMethods
127
+ attr_accessor :manual_id_property
128
+
127
129
  def find_by_neo_id(id)
128
130
  Neo4j::Node.load(id)
129
131
  end
@@ -137,12 +139,14 @@ module Neo4j::ActiveNode
137
139
  end
138
140
 
139
141
  def id_property(name, conf = {})
140
- id_property_constraint(name)
141
- @id_property_info = {name: name, type: conf}
142
- TypeMethods.define_id_methods(self, name, conf)
143
- constraint name, type: :unique unless conf[:constraint] == false
142
+ self.manual_id_property = true
143
+ Neo4j::Session.on_session_available do |_|
144
+ @id_property_info = {name: name, type: conf}
145
+ TypeMethods.define_id_methods(self, name, conf)
146
+ constraint(name, type: :unique) unless conf[:constraint] == false
144
147
 
145
- self.define_singleton_method(:find_by_id) { |key| self.where(name => key).first }
148
+ self.define_singleton_method(:find_by_id) { |key| self.where(name => key).first }
149
+ end
146
150
  end
147
151
 
148
152
  # rubocop:disable Style/PredicateName
@@ -165,6 +169,10 @@ module Neo4j::ActiveNode
165
169
  id_property_info[:name]
166
170
  end
167
171
 
172
+ def manual_id_property?
173
+ !!manual_id_property
174
+ end
175
+
168
176
  alias_method :primary_key, :id_property_name
169
177
 
170
178
  private
@@ -173,7 +181,7 @@ module Neo4j::ActiveNode
173
181
  if id_property?
174
182
  unless mapped_label.uniqueness_constraints[:property_keys].include?([name])
175
183
  # Neo4j Embedded throws a crazy error when a constraint can't be dropped
176
- drop_constraint(id_property_name, type: :unique)
184
+ drop_constraint(id_property_name, type: :unique) if constraint?(mapped_label_name, id_property_name)
177
185
  end
178
186
  end
179
187
  rescue Neo4j::Server::CypherResponse::ResponseError, Java::OrgNeo4jCypher::CypherExecutionException
@@ -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 DeclaredPropertyManager
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
@@ -103,7 +103,7 @@ module Neo4j
103
103
  # Finds the first record matching the specified conditions. There is no implied ordering so if order matters, you should specify it yourself.
104
104
  # @param Hash args of arguments to find
105
105
  def find_by(values)
106
- all.query_as(:n).where(n: values).limit(1).pluck(:n).first
106
+ all.where(values).limit(1).query_as(:n).pluck(:n).first
107
107
  end
108
108
 
109
109
  # Like find_by, except that if no record is found, raises a RecordNotFound error.
@@ -147,6 +147,7 @@ module Neo4j
147
147
  # end
148
148
  def index(property, conf = {})
149
149
  Neo4j::Session.on_session_available do |_|
150
+ drop_constraint(property, type: :unique) if Neo4j::Label.constraint?(mapped_label_name, property)
150
151
  _index(property, conf)
151
152
  end
152
153
  indexed_properties.push property unless indexed_properties.include? property
@@ -161,12 +162,22 @@ module Neo4j
161
162
  Neo4j::Session.on_session_available do |session|
162
163
  unless Neo4j::Label.constraint?(mapped_label_name, property)
163
164
  label = Neo4j::Label.create(mapped_label_name)
165
+ drop_index(property, label) if index?(property)
164
166
  label.create_constraint(property, constraints, session)
165
167
  end
166
168
  end
167
169
  end
168
170
 
169
- def drop_constraint(property, constraint)
171
+ # @param [Symbol] property The name of the property index to be dropped
172
+ # @param [Neo4j::Label] label An instance of label from Neo4j::Core
173
+ def drop_index(property, label = nil)
174
+ label_obj = label || Neo4j::Label.create(mapped_label_name)
175
+ label_obj.drop_index(property)
176
+ end
177
+
178
+ # @param [Symbol] property The name of the property constraint to be dropped
179
+ # @param [Hash] constraint The constraint type to be dropped.
180
+ def drop_constraint(property, constraint = {type: :unique})
170
181
  Neo4j::Session.on_session_available do |session|
171
182
  label = Neo4j::Label.create(mapped_label_name)
172
183
  label.drop_constraint(property, constraint, session)
@@ -24,8 +24,10 @@ module Neo4j::ActiveNode
24
24
  # If any of the before_* callbacks return false the action is cancelled and save returns false.
25
25
  def save(*)
26
26
  update_magic_properties
27
- association_proxy_cache.clear
28
- create_or_update
27
+ cascade_save do
28
+ association_proxy_cache.clear
29
+ create_or_update
30
+ end
29
31
  end
30
32
 
31
33
  # Persist the object to the database. Validations and Callbacks are included
@@ -53,7 +55,7 @@ module Neo4j::ActiveNode
53
55
  node = _create_node(properties)
54
56
  init_on_load(node, node.props)
55
57
  send_props(@relationship_props) if @relationship_props
56
- @relationship_props = nil
58
+ @relationship_props = @deferred_nodes = nil
57
59
  true
58
60
  end
59
61
 
@@ -66,12 +68,25 @@ module Neo4j::ActiveNode
66
68
  session.create_node(props, labels)
67
69
  end
68
70
 
71
+ private
72
+
73
+ # The pending associations are cleared during the save process, so it's necessary to
74
+ # build the processable hash before it begins. If there are nodes and associations that
75
+ # need to be created after the node is saved, a new transaction is started.
76
+ def cascade_save
77
+ deferred_nodes = pending_associations_with_nodes
78
+ Neo4j::Transaction.run(!deferred_nodes.blank?) do
79
+ result = yield
80
+ process_unpersisted_nodes!(deferred_nodes) if deferred_nodes
81
+ result
82
+ end
83
+ end
84
+
69
85
  module ClassMethods
70
86
  # Creates and saves a new node
71
87
  # @param [Hash] props the properties the new node should have
72
88
  def create(props = {})
73
89
  association_props = extract_association_attributes!(props) || {}
74
-
75
90
  new(props).tap do |obj|
76
91
  yield obj if block_given?
77
92
  obj.save
@@ -3,9 +3,9 @@ module Neo4j::ActiveNode
3
3
  extend ActiveSupport::Concern
4
4
  include Neo4j::Shared::Property
5
5
 
6
- def initialize(attributes = {}, options = {})
7
- super(attributes, options)
8
- @attributes ||= self.class.attributes_nil_hash.dup
6
+ def initialize(attributes = nil)
7
+ super(attributes)
8
+ @attributes ||= Hash[self.class.attributes_nil_hash]
9
9
  send_props(@relationship_props) if _persisted_obj && !@relationship_props.nil?
10
10
  end
11
11
 
@@ -22,6 +22,7 @@ module Neo4j::ActiveNode
22
22
  private
23
23
 
24
24
  def contains_association?(attributes)
25
+ return false unless attributes
25
26
  attributes.each_key { |key| return true if associations_keys.include?(key) }
26
27
  false
27
28
  end
@@ -6,6 +6,7 @@ module Neo4j
6
6
  include Neo4j::ActiveNode::Query::QueryProxyMethods
7
7
  include Neo4j::ActiveNode::Query::QueryProxyFindInBatches
8
8
  include Neo4j::ActiveNode::Query::QueryProxyEagerLoading
9
+ include Neo4j::ActiveNode::Query::QueryProxyUnpersisted
9
10
  include Neo4j::ActiveNode::Dependent::QueryProxyMethods
10
11
 
11
12
  # The most recent node to start a QueryProxy chain.
@@ -26,11 +27,13 @@ module Neo4j
26
27
  # @param [Neo4j::ActiveNode::HasN::Association] association The ActiveNode association (an object created by a <tt>has_one</tt> or
27
28
  # <tt>has_many</tt>) that created this object.
28
29
  # @param [Hash] options Additional options pertaining to the QueryProxy object. These may include:
29
- # * node_var: A string or symbol to be used by Cypher within its query string as an identifier
30
- # * rel_var: Same as above but pertaining to a relationship identifier
31
- # * session: The session to be used for this query
32
- # * source_object: The node instance at the start of the QueryProxy chain
33
- # * query_proxy: An existing QueryProxy chain upon which this new object should be built
30
+ # @option options [String, Symbol] :node_var A string or symbol to be used by Cypher within its query string as an identifier
31
+ # @option options [String, Symbol] :rel_var Same as above but pertaining to a relationship identifier
32
+ # @option options [Range, Fixnum, Symbol, Hash] :rel_length A Range, a Fixnum, a Hash or a Symbol to indicate the variable-length/fixed-length
33
+ # qualifier of the relationship. See http://neo4jrb.readthedocs.org/en/latest/Querying.html#variable-length-relationships.
34
+ # @option options [Neo4j::Session] :session The session to be used for this query
35
+ # @option options [Neo4j::ActiveNode] :source_object The node instance at the start of the QueryProxy chain
36
+ # @option options [QueryProxy] :query_proxy An existing QueryProxy chain upon which this new object should be built
34
37
  #
35
38
  # QueryProxy objects are evaluated lazily.
36
39
  def initialize(model, association = nil, options = {})
@@ -51,7 +54,7 @@ module Neo4j
51
54
  end
52
55
 
53
56
  def inspect
54
- "<QueryProxy #{ANSI::CYAN}#{@context}#{ANSI::CLEAR} CYPHER: #{ANSI::YELLOW}#{self.to_cypher.inspect}#{ANSI::CLEAR}>"
57
+ "#<QueryProxy #{@context} CYPHER: #{self.to_cypher.inspect}>"
55
58
  end
56
59
 
57
60
  attr_reader :start_object, :query_proxy
@@ -84,6 +87,9 @@ module Neo4j
84
87
  # Build a Neo4j::Core::Query object for the QueryProxy. This is necessary when you want to take an existing QueryProxy chain
85
88
  # and work with it from the more powerful (but less friendly) Neo4j::Core::Query.
86
89
  # @param [String,Symbol] var The identifier to use for node at this link of the QueryProxy chain.
90
+ #
91
+ # .. code-block:: ruby
92
+ #
87
93
  # student.lessons.query_as(:l).with('your cypher here...')
88
94
  def query_as(var, with_labels = true)
89
95
  result_query = @chain.inject(base_query(var, with_labels).params(@params)) do |query, link|
@@ -115,6 +121,8 @@ module Neo4j
115
121
 
116
122
  # Scope all queries to the current scope.
117
123
  #
124
+ # .. code-block:: ruby
125
+ #
118
126
  # Comment.where(post_id: 1).scoping do
119
127
  # Comment.first
120
128
  # end
@@ -141,8 +149,12 @@ module Neo4j
141
149
  alias_method :order_by, :order
142
150
 
143
151
  # Cypher string for the QueryProxy's query. This will not include params. For the full output, see <tt>to_cypher_with_params</tt>.
144
- def to_cypher
145
- query.to_cypher
152
+ def to_cypher(*args)
153
+ query.to_cypher(*args)
154
+ end
155
+
156
+ def print_cypher
157
+ query.print_cypher
146
158
  end
147
159
 
148
160
  # Returns a string of the cypher query with return objects and params
@@ -155,8 +167,7 @@ module Neo4j
155
167
 
156
168
  # To add a relationship for the node for the association on this QueryProxy
157
169
  def <<(other_node)
158
- create(other_node, {})
159
-
170
+ @start_object._persisted_obj ? create(other_node, {}) : defer_create(other_node, {}, :<<)
160
171
  self
161
172
  end
162
173
 
@@ -278,7 +289,7 @@ module Neo4j
278
289
  end
279
290
 
280
291
  def _association_arrow(properties = {}, create = false)
281
- @association && @association.arrow_cypher(@rel_var, properties, create)
292
+ @association && @association.arrow_cypher(@rel_var, properties, create, false, @rel_length)
282
293
  end
283
294
 
284
295
  def _chain_level
@@ -312,8 +323,13 @@ module Neo4j
312
323
  private
313
324
 
314
325
  def instance_vars_from_options!(options)
315
- @node_var, @session, @source_object, @starting_query, @optional, @start_object, @query_proxy, @chain_level, @association_labels =
316
- options.values_at(:node, :session, :source_object, :starting_query, :optional, :start_object, :query_proxy, :chain_level, :association_labels)
326
+ @node_var, @session, @source_object, @starting_query, @optional,
327
+ @start_object, @query_proxy, @chain_level, @association_labels,
328
+ @rel_length = options.values_at(:node, :session, :source_object,
329
+ :starting_query, :optional,
330
+ :start_object, :query_proxy,
331
+ :chain_level, :association_labels,
332
+ :rel_length)
317
333
  end
318
334
 
319
335
  def build_deeper_query_proxy(method, args)