neo4j 3.0.4 → 4.0.0.rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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