neo4j 3.0.4 → 4.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bf2546cbe829d338babc703fcecdeff5650e8fd5
4
- data.tar.gz: 094b2019bb62cb8e9f5e510eb54cf00f51c6131f
3
+ metadata.gz: 122872ebd58702ee7d868124092285e8907cfbfd
4
+ data.tar.gz: 11989c0af301ccb1305df5deb26e9c55a20313f6
5
5
  SHA512:
6
- metadata.gz: b7552de25d876bd2b98038637c517ae77c47c04ae5d3fbd0a14852710998a3549d1ce981a9c35eea94e84cf1dcc04f469497ae7c7eb9be45a76b77f813900bd8
7
- data.tar.gz: ba45f543a6cbce758ccfba472789cfb41ba78ce8ee6fe70db8e187371a1a638e83c86a6dae316a4ff786046a61b2ab3a984001993d4150b30e9eee57c4b89fcd
6
+ metadata.gz: e9433059f7d5dd25d4bd22be97af295f98cbd1340f5bdf1c1869c01111b22d61bf8cf6e260d08de528c3cb90735984d069234fafe32556cd6b200c415b0d7243
7
+ data.tar.gz: c894497b7e00362c8145bdbf61bdd8b50f772f108b839ae068971bd32e888f502897ecfc03c4284bfd1f838815b75fe8dc41d3222ea61b0d1157d371a3ff325e
data/CHANGELOG CHANGED
@@ -1,3 +1,25 @@
1
+ == 4.0.0.rc.1
2
+ This release introduces API changes that may be considered breaking under certain conditions. See See https://github.com/neo4jrb/neo4j/wiki/Neo4j.rb-v4-Introduction.
3
+ Please use https://github.com/neo4jrb/neo4j/issues for support regarding this update! You can also reach us on Twitter: @neo4jrb (Brian) and @subvertallmedia (Chris).
4
+ * Default behavior changed: relationship types default to all caps, no prepending of "#". This behavior can be changed.
5
+ * ActiveRel models no longer require explicit calling of `type`. When missing, the model will infer a type using the class name following the same rules used to determine automatic relationship types from ActiveNode models.
6
+ * _classname properties will not be added automatically if you are using a version Neo4j >= 2.1.5. Instead, models are found using labels or relationship type. This is a potentially breaking change, particularly where ActiveRel is concerned. See the link at the beginning of this message for the steps required to work around this.
7
+ * Query scopes are now chainable! Call `all` at the start of your scope or method to take advantage of this.
8
+ * Changes required for Neo4j 2.2.
9
+ * Support for custom typecasters.
10
+ * New method `rel_where`, expanded behavior of `match_to` and `first_rel_to`
11
+ * Implemented ActiveSupport load hooks.
12
+ * Assorted performance improvements and refactoring.
13
+
14
+ == 3.0.4
15
+ * Gemspec requires the latest neo4j-core.
16
+ * Fixed a pagination bug — thanks, @chrisgogreen!
17
+ * New QueryProxy methods `match_to` and `first_rel_to` are pretty cool.
18
+ * include_root_in_json is now configurable through config.neo4j.include_root_in_json or Neo4j::Config[:include_root_in_json]. Also cool.
19
+ * There's a new `delete_all` method for QueryProxy, too.
20
+ * @codebeige removed the `include?` class method, which was smart.
21
+ * Did I mention we won an award from Neo Tech? Check it out. https://github.com/neo4jrb/neo4j#welcome-to-neo4jrb
22
+
1
23
  == 3.0.3
2
24
  * Gemspec has been updated to require neo4j-core 3.0.5
3
25
  * Added `find_in_batches`
data/README.md CHANGED
@@ -6,6 +6,10 @@ Neo4j.rb is an Active Model compliant Ruby/JRuby wrapper for [the Neo4j graph da
6
6
  Winner of a 2014 Graphie for "Best Community Contribution" at Neo4j's [Graph Connect](http://graphconnect.com) conference!
7
7
  ![2014 Graphie](http://i.imgur.com/CkOoTTYm.jpg)
8
8
 
9
+ ## Coming Soon: v4!
10
+
11
+ The master branch of this repo now holds stable builds of the v4 version of the gem. Its changes are outlined at [here](https://github.com/neo4jrb/neo4j/wiki/Neo4j.rb-v4-Introduction). Unless otherwise noted, all 3.X documentation is totally valid. v4 discussion will happen [https://github.com/neo4jrb/neo4j/issues/596](https://github.com/neo4jrb/neo4j/issues/596) but if you have any concerns, please contact us directly.
12
+
9
13
  ## Modern (3.X) Documentation
10
14
 
11
15
  * [Wiki](https://github.com/neo4jrb/neo4j/wiki/Neo4j.rb-v3-Introduction)
@@ -30,4 +30,6 @@ timestamps: true
30
30
  # Alternatively, call class method Neo4j::ActiveNode:cache_class to set this on specific models.
31
31
  # By default, this property is called _classname, set as symbol to override.
32
32
  cache_class_names: true
33
- # class_name_property: :_classname
33
+ # class_name_property: :_classname
34
+
35
+ transform_rel_type: :upcase
@@ -4,6 +4,7 @@ module Neo4j
4
4
  module ActiveNode
5
5
  module HasN
6
6
  class Association
7
+ include Neo4j::Shared::RelTypeConverters
7
8
  attr_reader :type, :name, :relationship, :direction
8
9
 
9
10
  def initialize(type, direction, name, options = {})
@@ -60,7 +61,7 @@ module Neo4j
60
61
  when @origin
61
62
  origin_type
62
63
  else
63
- (create || exceptional_target_class?) && "##{@name}"
64
+ (create || exceptional_target_class?) && decorated_rel_type(@name)
64
65
  end
65
66
  end
66
67
 
@@ -136,8 +137,8 @@ module Neo4j
136
137
 
137
138
  def validate_option_combinations(options)
138
139
  raise ArgumentError, "Cannot specify both :type and :origin (#{base_declaration})" if options[:type] && options[:origin]
139
- # raise ArgumentError, "Cannot specify both :type and :rel_class (#{base_declaration})" if options[:type] && options[:rel_class] see issue #494
140
- # raise ArgumentError, "Cannot specify both :origin and :rel_class (#{base_declaration}" if options[:origin] && options[:rel_class]
140
+ raise ArgumentError, "Cannot specify both :type and :rel_class (#{base_declaration})" if options[:type] && options[:rel_class]
141
+ raise ArgumentError, "Cannot specify both :origin and :rel_class (#{base_declaration}" if options[:origin] && options[:rel_class]
141
142
  end
142
143
 
143
144
  # Determine if model class as derived from the association name would be different than the one specified via the model_class key
@@ -47,7 +47,7 @@ module Neo4j::ActiveNode
47
47
 
48
48
  clazz.module_eval(%Q{
49
49
  def id
50
- persisted? ? #{name} : nil
50
+ persisted? ? #{name.to_sym == :id ? 'attribute(\'id\')' : name} : nil
51
51
  end
52
52
 
53
53
  validates_uniqueness_of :#{name}
@@ -1,6 +1,6 @@
1
1
  module Neo4j::ActiveNode::Initialize
2
2
  extend ActiveSupport::Concern
3
- include Neo4j::TypeConverters
3
+ include Neo4j::Shared::TypeConverters
4
4
 
5
5
  # called when loading the node from the database
6
6
  # @param [Neo4j::Node] persisted_node the node this class wraps
@@ -13,17 +13,13 @@ module Neo4j
13
13
  # @return the labels
14
14
  # @see Neo4j-core
15
15
  def labels
16
- if @_persisted_obj.labels.nil?
17
- r = queried_labels
18
- @_persisted_obj.labels = r
19
- else
20
- @_persisted_obj.labels
21
- end
16
+ @_persisted_obj.labels
22
17
  end
23
18
 
24
- def queried_labels
25
- self.class.query_as(:result).where("ID(result)" => self.neo_id).return("LABELS(result) as result_labels").first.result_labels.map(&:to_sym)
26
- end
19
+ # this is handled by core, leaving it now for posterity
20
+ # def queried_labels
21
+ # self.class.query_as(:result).where("ID(result)" => self.neo_id).return("LABELS(result) as result_labels").first.result_labels.map(&:to_sym)
22
+ # end
27
23
 
28
24
  # adds one or more labels
29
25
  # @see Neo4j-core
@@ -72,7 +68,7 @@ module Neo4j
72
68
 
73
69
  # Find all nodes/objects of this class
74
70
  def all
75
- self.as(:n)
71
+ Neo4j::ActiveNode::Query::QueryProxy.new(self, nil, {})
76
72
  end
77
73
 
78
74
  # Returns the object with the specified neo4j id.
@@ -131,8 +127,7 @@ module Neo4j
131
127
  Neo4j::Session.on_session_available do |_|
132
128
  _index(property, conf)
133
129
  end
134
- @_indexed_properties ||= []
135
- @_indexed_properties.push property unless @_indexed_properties.include? property
130
+ indexed_properties.push property unless indexed_properties.include? property
136
131
  end
137
132
 
138
133
  # Creates a neo4j constraint on this class for given property
@@ -174,7 +169,19 @@ module Neo4j
174
169
  end
175
170
 
176
171
  def indexed_properties
177
- @_indexed_properties
172
+ @_indexed_properties ||= []
173
+ end
174
+
175
+ def base_class
176
+ unless self < Neo4j::ActiveNode
177
+ raise "#{name} doesn't belong in a hierarchy descending from ActiveNode"
178
+ end
179
+
180
+ if superclass == Object
181
+ self
182
+ else
183
+ superclass.base_class
184
+ end
178
185
  end
179
186
 
180
187
 
@@ -9,7 +9,7 @@ module Neo4j
9
9
 
10
10
  # The most recent node to start a QueryProxy chain.
11
11
  # Will be nil when using QueryProxy chains on class methods.
12
- attr_reader :caller
12
+ attr_reader :caller, :association, :model
13
13
 
14
14
  # QueryProxy is ActiveNode's Cypher DSL. While the name might imply that it creates queries in a general sense,
15
15
  # it is actually referring to <tt>Neo4j::Core::Query</tt>, which is a pure Ruby Cypher DSL provided by the <tt>neo4j-core</tt> gem.
@@ -50,6 +50,7 @@ module Neo4j
50
50
  def identity
51
51
  @node_var || :result
52
52
  end
53
+ alias_method :node_identity, :identity
53
54
 
54
55
  # The relationship identifier most recently used by the QueryProxy chain.
55
56
  def rel_identity
@@ -106,7 +107,7 @@ module Neo4j
106
107
  self.to_a == value
107
108
  end
108
109
 
109
- METHODS = %w[where order skip limit]
110
+ METHODS = %w[where rel_where order skip limit]
110
111
 
111
112
  METHODS.each do |method|
112
113
  module_eval(%Q{
@@ -114,7 +115,8 @@ module Neo4j
114
115
  build_deeper_query_proxy(:#{method}, args)
115
116
  end}, __FILE__, __LINE__)
116
117
  end
117
-
118
+ # Since there is a rel_where method, it seems only natural for there to be node_where
119
+ alias_method :node_where, :where
118
120
  alias_method :offset, :skip
119
121
  alias_method :order_by, :order
120
122
 
@@ -139,7 +141,6 @@ module Neo4j
139
141
  # @param [String,Symbol] var The identifier to use for node at this link of the QueryProxy chain.
140
142
  # student.lessons.query_as(:l).with('your cypher here...')
141
143
  def query_as(var)
142
- var = @node_var if @node_var
143
144
  query = if @association
144
145
  chain_var = _association_chain_var
145
146
  label_string = @model && ":`#{@model.mapped_label_name}`"
@@ -154,6 +155,25 @@ module Neo4j
154
155
  end
155
156
  end
156
157
 
158
+
159
+ # Scope all queries to the current scope.
160
+ #
161
+ # Comment.where(post_id: 1).scoping do
162
+ # Comment.first
163
+ # end
164
+ #
165
+ # TODO: unscoped
166
+ # Please check unscoped if you want to remove all previous scopes (including
167
+ # the default_scope) during the execution of a block.
168
+ def scoping
169
+ previous, @model.current_scope = @model.current_scope, self
170
+ yield
171
+ ensure
172
+ @model.current_scope = previous
173
+ end
174
+
175
+
176
+
157
177
  # Cypher string for the QueryProxy's query. This will not include params. For the full output, see <tt>to_cypher_with_params</tt>.
158
178
  def to_cypher
159
179
  query.to_cypher
@@ -203,7 +223,8 @@ module Neo4j
203
223
  start_object = @options[:start_object]
204
224
  start_object.clear_association_cache
205
225
  _session.query(context: @options[:context])
206
- .start(start: "node(#{start_object.neo_id})", end: "node(#{other_node.neo_id})")
226
+ .match("(start#{match_string(start_object)}), (end#{match_string(other_node)})").where("ID(start) = {start_id} AND ID(end) = {end_id}")
227
+ .params(start_id: start_object.neo_id, end_id: other_node.neo_id)
207
228
  .create("start#{_association_arrow(properties, true)}end").exec
208
229
 
209
230
  @association.perform_callback(@options[:start_object], other_node, :after)
@@ -213,9 +234,10 @@ module Neo4j
213
234
 
214
235
  # QueryProxy objects act as a representation of a model at the class level so we pass through calls
215
236
  # This allows us to define class functions for reusable query chaining or for end-of-query aggregation/summarizing
216
- def method_missing(method_name, *args)
237
+ def method_missing(method_name, *args, &block)
217
238
  if @model && @model.respond_to?(method_name)
218
- call_class_method(method_name, *args)
239
+ args[2] = self if @model.has_association?(method_name) || @model.has_scope?(method_name)
240
+ scoping { @model.public_send(method_name, *args, &block) }
219
241
  else
220
242
  super
221
243
  end
@@ -291,12 +313,6 @@ module Neo4j
291
313
 
292
314
  private
293
315
 
294
- def call_class_method(method_name, *args)
295
- args[2] = self
296
- result = @model.send(method_name, *args)
297
- result
298
- end
299
-
300
316
  def build_deeper_query_proxy(method, args)
301
317
  self.dup.tap do |new_query|
302
318
  args.each do |arg|
@@ -319,7 +335,7 @@ module Neo4j
319
335
  node_num = 1
320
336
  result = []
321
337
  if arg.is_a?(Hash)
322
- arg.map do |key, value|
338
+ arg.each do |key, value|
323
339
  if @model && @model.has_association?(key)
324
340
 
325
341
  neo_id = value.try(:neo_id) || value
@@ -341,14 +357,23 @@ module Neo4j
341
357
  end
342
358
  result
343
359
  end
360
+ alias_method :links_for_node_where_arg, :links_for_where_arg
361
+
362
+ # We don't accept strings here. If you want to use a string, just use where.
363
+ def links_for_rel_where_arg(arg)
364
+ arg.each_with_object([]) do |(key, value), result|
365
+ result << [:where, ->(v) {{ rel_identity => { key => value }}}]
366
+ end
367
+ end
344
368
 
345
369
  def links_for_order_arg(arg)
346
370
  [[:order, ->(v) { arg.is_a?(String) ? arg : {v => arg} }]]
347
371
  end
348
372
 
349
-
373
+ def match_string(node)
374
+ ":`#{node.class.mapped_label_name}`" if node.class.respond_to?(:mapped_label_name)
375
+ end
350
376
  end
351
-
352
377
  end
353
378
  end
354
379
  end
@@ -21,7 +21,7 @@ module Neo4j
21
21
  raise(InvalidParameterError, ':count accepts `distinct` or nil as a parameter') unless distinct.nil? || distinct == :distinct
22
22
  query_with_target(target) do |target|
23
23
  q = distinct.nil? ? target : "DISTINCT #{target}"
24
- self.query.pluck("count(#{q}) AS #{target}").first
24
+ self.query.reorder.pluck("count(#{q}) AS #{target}").first
25
25
  end
26
26
  end
27
27
 
@@ -64,15 +64,27 @@ module Neo4j
64
64
  end
65
65
 
66
66
  # Shorthand for `MATCH (start)-[r]-(other_node) WHERE ID(other_node) = #{other_node.neo_id}`
67
+ # The `node` param can be a persisted ActiveNode instance, any string or integer, or nil.
68
+ # When it's a node, it'll use the object's neo_id, which is fastest. When not nil, it'll figure out the
69
+ # primary key of that model. When nil, it uses `1 = 2` to prevent matching all records, which is the default
70
+ # behavior when nil is passed to `where` in QueryProxy.
67
71
  # @return [Neo4j::ActiveNode::Query::QueryProxy] A QueryProxy object upon which you can build.
68
72
  def match_to(node)
69
- self.where(neo_id: node.neo_id)
73
+ if node.respond_to?(:neo_id)
74
+ self.where(neo_id: node.neo_id)
75
+ elsif !node.nil?
76
+ id_key = self.association.nil? ? model.primary_key : self.association.target_class.primary_key
77
+ self.where(id_key => node)
78
+ else
79
+ # support for null object pattern
80
+ self.where('1 = 2')
81
+ end
70
82
  end
71
83
 
72
84
  # Gives you the first relationship between the last link of a QueryProxy chain and a given node
73
85
  # Shorthand for `MATCH (start)-[r]-(other_node) WHERE ID(other_node) = #{other_node.neo_id} RETURN r`
74
86
  def first_rel_to(node)
75
- self.where(neo_id: node.neo_id).limit(1).pluck(rel_identity).first
87
+ self.match_to(node).limit(1).pluck(rel_identity).first
76
88
  end
77
89
 
78
90
  private
@@ -1,3 +1,5 @@
1
+ require 'active_support/per_thread_registry'
2
+
1
3
  module Neo4j::ActiveNode
2
4
  module Scope
3
5
  extend ActiveSupport::Concern
@@ -53,7 +55,9 @@ module Neo4j::ActiveNode
53
55
  }, __FILE__, __LINE__)
54
56
  end
55
57
 
56
-
58
+ def has_scope?(name)
59
+ _scope.has_key?(name.to_sym)
60
+ end
57
61
 
58
62
  def _scope
59
63
  @_scope ||= {}
@@ -68,6 +72,23 @@ module Neo4j::ActiveNode
68
72
  end
69
73
 
70
74
 
75
+ def current_scope #:nodoc:
76
+ ScopeRegistry.value_for(:current_scope, base_class.to_s)
77
+ end
78
+
79
+ def current_scope=(scope) #:nodoc:
80
+ ScopeRegistry.set_value_for(:current_scope, base_class.to_s, scope)
81
+ end
82
+
83
+
84
+ def all
85
+ if current_scope
86
+ current_scope.clone
87
+ else
88
+ self.as(:n)
89
+ end
90
+ end
91
+
71
92
  end
72
93
 
73
94
  class ScopeEvalContext
@@ -79,9 +100,45 @@ module Neo4j::ActiveNode
79
100
  Neo4j::ActiveNode::Query::QueryProxy::METHODS.each do |method|
80
101
  module_eval(%Q{
81
102
  def #{method}(params={})
82
- (@query_proxy || @target).#{method}(params)
103
+ @target.all.scoping do
104
+ (@query_proxy || @target).#{method}(params)
105
+ end
83
106
  end}, __FILE__, __LINE__)
84
107
  end
85
108
  end
109
+
110
+
111
+ # Stolen from ActiveRecord
112
+ # https://github.com/rails/rails/blob/08754f12e65a9ec79633a605e986d0f1ffa4b251/activerecord/lib/active_record/scoping.rb#L57
113
+ class ScopeRegistry # :nodoc:
114
+ extend ActiveSupport::PerThreadRegistry
115
+
116
+ VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope]
117
+
118
+ def initialize
119
+ @registry = Hash.new { |hash, key| hash[key] = {} }
120
+ end
121
+
122
+ # Obtains the value for a given +scope_name+ and +variable_name+.
123
+ def value_for(scope_type, variable_name)
124
+ raise_invalid_scope_type!(scope_type)
125
+ @registry[scope_type][variable_name]
126
+ end
127
+
128
+ # Sets the +value+ for a given +scope_type+ and +variable_name+.
129
+ def set_value_for(scope_type, variable_name, value)
130
+ raise_invalid_scope_type!(scope_type)
131
+ @registry[scope_type][variable_name] = value
132
+ end
133
+
134
+ private
135
+
136
+ def raise_invalid_scope_type!(scope_type)
137
+ if !VALID_SCOPE_TYPES.include?(scope_type)
138
+ raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
139
+ end
140
+ end
141
+ end
142
+
86
143
  end
87
144
  end
@@ -1,6 +1,6 @@
1
1
  module Neo4j
2
2
 
3
- # Makes Neo4j nodes and relationships behave like active record objects.
3
+ # Makes Neo4j nodes and relationships behave like ActiveRecord objects.
4
4
  # By including this module in your class it will create a mapping for the node to your ruby class
5
5
  # by using a Neo4j Label with the same name as the class. When the node is loaded from the database it
6
6
  # will check if there is a ruby class for the labels it has.
@@ -28,7 +28,7 @@ module Neo4j
28
28
  include Neo4j::Shared::Identity
29
29
  include Neo4j::ActiveNode::Initialize
30
30
  include Neo4j::ActiveNode::IdProperty
31
- include Neo4j::ActiveNode::SerializedProperties
31
+ include Neo4j::Shared::SerializedProperties
32
32
  include Neo4j::ActiveNode::Property
33
33
  include Neo4j::ActiveNode::Reflection
34
34
  include Neo4j::ActiveNode::Persistence
@@ -48,21 +48,24 @@ module Neo4j
48
48
  def self.inherited(other)
49
49
  inherit_id_property(other) if self.has_id_property?
50
50
  inherited_indexes(other) if self.respond_to?(:indexed_properties)
51
- attributes.each_pair do |k,v|
52
- other.attributes[k] = v
53
- end
51
+ attributes.each_pair { |k,v| other.attributes[k] = v }
52
+ inherit_serialized_properties(other) if self.respond_to?(:serialized_properties)
54
53
  Neo4j::ActiveNode::Labels.add_wrapped_class(other)
55
54
  super
56
55
  end
57
56
 
58
57
  def self.inherited_indexes(other)
59
58
  return if indexed_properties.nil?
60
- self.indexed_properties.each {|property| other.index property }
59
+ self.indexed_properties.each { |property| other.index property }
60
+ end
61
+
62
+ def self.inherit_serialized_properties(other)
63
+ other.serialized_properties = self.serialized_properties
61
64
  end
62
65
 
63
66
  def self.inherit_id_property(other)
64
67
  id_prop = self.id_property_info
65
- conf = id_prop[:type].empty? ? {auto: :uuid} : id_prop[:type]
68
+ conf = id_prop[:type].empty? ? { auto: :uuid } : id_prop[:type]
66
69
  other.id_property id_prop[:name], conf
67
70
  end
68
71
 
@@ -72,11 +75,10 @@ module Neo4j
72
75
  name = Neo4j::Config[:id_property]
73
76
  type = Neo4j::Config[:id_property_type]
74
77
  value = Neo4j::Config[:id_property_type_value]
75
- if (name && type && value)
76
- id_property(name, type => value)
77
- end
78
+ id_property(name, type => value) if (name && type && value)
78
79
  end
79
-
80
80
  end
81
+
82
+ ActiveSupport.run_load_hooks(:active_node, self)
81
83
  end
82
84
  end
@@ -1,7 +1,7 @@
1
1
  module Neo4j::ActiveRel
2
2
  module Initialize
3
3
  extend ActiveSupport::Concern
4
- include Neo4j::TypeConverters
4
+ include Neo4j::Shared::TypeConverters
5
5
 
6
6
  attr_reader :_persisted_obj
7
7
 
@@ -24,6 +24,7 @@ module Neo4j::ActiveRel
24
24
  set_timestamps
25
25
  properties = convert_properties_to :db, props
26
26
  rel = _create_rel(from_node, to_node, properties)
27
+ return self unless rel.respond_to?(:_persisted_obj)
27
28
  init_on_load(rel._persisted_obj, from_node, to_node, @rel_type)
28
29
  true
29
30
  end
@@ -61,7 +62,7 @@ module Neo4j::ActiveRel
61
62
  def _create_rel(from_node, to_node, *args)
62
63
  props = self.class.default_property_values(self)
63
64
  props.merge!(args[0]) if args[0].is_a?(Hash)
64
- set_classname(props)
65
+ set_classname(props, true)
65
66
  _rel_creation_query(from_node, to_node, props)
66
67
  end
67
68
 
@@ -99,4 +100,4 @@ module Neo4j::ActiveRel
99
100
  end
100
101
 
101
102
  end
102
- end
103
+ end
@@ -42,16 +42,6 @@ module Neo4j::ActiveRel
42
42
  alias_method :start_class, :from_class
43
43
  alias_method :end_class, :to_class
44
44
 
45
- # @param type [String] sets the relationship type when creating relationships via this class
46
- def type(type = nil)
47
- @rel_type = type
48
- end
49
-
50
- # @return [String] a string representing the relationship type that will be created
51
- def _type
52
- @rel_type
53
- end
54
-
55
45
  def load_entity(id)
56
46
  Neo4j::Node.load(id)
57
47
  end
@@ -26,10 +26,6 @@ module Neo4j::ActiveRel
26
26
  where_query.where(where_string(args)).pluck(:r1)
27
27
  end
28
28
 
29
- def array_load(ids_array)
30
- where_query.where("ID(r1) IN {ids_array}").params(ids_array: ids_array).pluck(:r1)
31
- end
32
-
33
29
  # Performs a basic match on the relationship, returning all results.
34
30
  # This is not executed lazily, it will immediately return matching objects.
35
31
  def all
@@ -3,16 +3,27 @@ class Neo4j::Relationship
3
3
  module Wrapper
4
4
  def wrapper
5
5
  props.symbolize_keys!
6
- return self unless props.is_a?(Hash) && props.has_key?(Neo4j::Config.class_name_property)
6
+ # return self unless props.is_a?(Hash)
7
7
  begin
8
- found_class = props[Neo4j::Config.class_name_property].constantize
8
+ most_concrete_class = sorted_wrapper_classes
9
+ wrapped_rel = most_concrete_class.constantize.new
9
10
  rescue NameError
10
11
  return self
11
12
  end
12
- wrapped_rel = found_class.new
13
+
13
14
  wrapped_rel.init_on_load(self, self._start_node_id, self._end_node_id, self.rel_type)
14
15
  wrapped_rel
15
16
  end
17
+
18
+ private
19
+
20
+ def sorted_wrapper_classes
21
+ props[Neo4j::Config.class_name_property] || class_from_type
22
+ end
23
+
24
+ def class_from_type
25
+ Neo4j::ActiveRel::Types::WRAPPED_CLASSES[rel_type] || rel_type.camelize
26
+ end
16
27
  end
17
28
 
18
- end
29
+ end
@@ -0,0 +1,54 @@
1
+ module Neo4j
2
+ module ActiveRel
3
+
4
+ # provides mapping of type to model name
5
+ module Types
6
+ extend ActiveSupport::Concern
7
+
8
+ # WRAPPED_CLASSES maps relationship types to ActiveRel models.
9
+ #
10
+ # Typically, it's a 1:1 relationship, with a type having a model of the same name. Sometimes, someone needs to be a precious
11
+ # snowflake and have a model name that doesn't match the rel type, so this comes in handy.
12
+ #
13
+ # As an example, Chris often finds it easier to name models after the classes that use the relationship: `StudentLesson` instead of
14
+ # `EnrolledIn`, because it's easier to remember "A student has a relationship to lesson" than "the type of relationship between Student
15
+ # and Lesson is 'EnrolledIn'." After all, that is a big part of why we have models, right? To make our lives easier?
16
+ #
17
+ # A model is added to WRAPPED_CLASSES when it is initalized AND when the `type` class method is called within a model. This means that
18
+ # it's possible a model will be added twice: once with the rel_type version of the model name, again with the custom type. deal_with_it.gif.
19
+ #
20
+ # As an alternative to this, you can call the `set_classname` class method to insert a `_classname` property into your relationship,
21
+ # which will completely bypass this whole process.
22
+ WRAPPED_CLASSES = {}
23
+
24
+ included do |klazz|
25
+ type self.name, true
26
+ end
27
+
28
+ module ClassMethods
29
+ include Neo4j::Shared::RelTypeConverters
30
+
31
+ # @param type [String] sets the relationship type when creating relationships via this class
32
+ def type(given_type = self.name, auto = false)
33
+ use_type = auto ? decorated_rel_type(given_type) : given_type
34
+ add_wrapped_class use_type
35
+ @rel_type = use_type
36
+ end
37
+
38
+ # @return [String] a string representing the relationship type that will be created
39
+ def _type
40
+ @rel_type
41
+ end
42
+
43
+ def add_wrapped_class(type)
44
+ #_wrapped_classes[type.to_sym.downcase] = self.name
45
+ _wrapped_classes[type.to_sym] = self.name
46
+ end
47
+
48
+ def _wrapped_classes
49
+ Neo4j::ActiveRel::Types::WRAPPED_CLASSES
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end