neo4j 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +10 -0
  3. data/Gemfile +6 -16
  4. data/README.md +5 -2
  5. data/bin/neo4j-jars +6 -6
  6. data/lib/neo4j.rb +13 -9
  7. data/lib/neo4j/active_node.rb +9 -9
  8. data/lib/neo4j/active_node/dependent.rb +11 -0
  9. data/lib/neo4j/active_node/dependent/association_methods.rb +28 -0
  10. data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +48 -0
  11. data/lib/neo4j/active_node/has_n.rb +124 -112
  12. data/lib/neo4j/active_node/has_n/association.rb +45 -30
  13. data/lib/neo4j/active_node/id_property.rb +22 -19
  14. data/lib/neo4j/active_node/initialize.rb +2 -4
  15. data/lib/neo4j/active_node/labels.rb +23 -22
  16. data/lib/neo4j/active_node/node_wrapper.rb +5 -8
  17. data/lib/neo4j/active_node/orm_adapter.rb +2 -4
  18. data/lib/neo4j/active_node/persistence.rb +5 -10
  19. data/lib/neo4j/active_node/property.rb +3 -4
  20. data/lib/neo4j/active_node/query.rb +27 -6
  21. data/lib/neo4j/active_node/query/query_proxy.rb +65 -110
  22. data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +67 -0
  23. data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +0 -1
  24. data/lib/neo4j/active_node/query/query_proxy_methods.rb +29 -28
  25. data/lib/neo4j/active_node/query_methods.rb +6 -6
  26. data/lib/neo4j/active_node/reflection.rb +3 -2
  27. data/lib/neo4j/active_node/rels.rb +1 -1
  28. data/lib/neo4j/active_node/scope.rb +13 -8
  29. data/lib/neo4j/active_node/validations.rb +5 -6
  30. data/lib/neo4j/active_rel.rb +1 -2
  31. data/lib/neo4j/active_rel/callbacks.rb +3 -3
  32. data/lib/neo4j/active_rel/persistence.rb +9 -7
  33. data/lib/neo4j/active_rel/property.rb +12 -4
  34. data/lib/neo4j/active_rel/query.rb +6 -8
  35. data/lib/neo4j/active_rel/rel_wrapper.rb +0 -2
  36. data/lib/neo4j/active_rel/related_node.rb +4 -5
  37. data/lib/neo4j/active_rel/types.rb +4 -6
  38. data/lib/neo4j/active_rel/validations.rb +0 -1
  39. data/lib/neo4j/config.rb +11 -23
  40. data/lib/neo4j/core/query.rb +1 -1
  41. data/lib/neo4j/migration.rb +17 -18
  42. data/lib/neo4j/paginated.rb +4 -4
  43. data/lib/neo4j/railtie.rb +19 -19
  44. data/lib/neo4j/shared.rb +7 -3
  45. data/lib/neo4j/shared/callbacks.rb +15 -4
  46. data/lib/neo4j/shared/identity.rb +2 -2
  47. data/lib/neo4j/shared/persistence.rb +10 -21
  48. data/lib/neo4j/shared/property.rb +17 -30
  49. data/lib/neo4j/shared/rel_type_converters.rb +1 -3
  50. data/lib/neo4j/shared/type_converters.rb +13 -25
  51. data/lib/neo4j/shared/validations.rb +3 -3
  52. data/lib/neo4j/tasks/migration.rake +7 -7
  53. data/lib/neo4j/type_converters.rb +1 -1
  54. data/lib/neo4j/version.rb +1 -1
  55. data/lib/rails/generators/neo4j/model/model_generator.rb +16 -12
  56. data/lib/rails/generators/neo4j_generator.rb +18 -18
  57. data/neo4j.gemspec +22 -18
  58. metadata +103 -1
@@ -5,16 +5,16 @@ module Neo4j
5
5
  module HasN
6
6
  class Association
7
7
  include Neo4j::Shared::RelTypeConverters
8
- attr_reader :type, :name, :relationship, :direction
8
+ include Neo4j::ActiveNode::Dependent::AssociationMethods
9
+ attr_reader :type, :name, :relationship, :direction, :dependent
9
10
 
10
11
  def initialize(type, direction, name, options = {})
11
- check_valid_type_and_dir(type, direction)
12
+ validate_init_arguments(type, direction, options)
12
13
  @type = type.to_sym
13
14
  @name = name
14
15
  @direction = direction.to_sym
15
16
  @target_class_name_from_name = name.to_s.classify
16
-
17
- set_vars_from_options(options)
17
+ apply_vars_from_options(options)
18
18
  end
19
19
 
20
20
  def target_class_option(options)
@@ -42,7 +42,11 @@ module Neo4j
42
42
  end
43
43
 
44
44
  def target_class_name
45
- @target_class_option.to_s if @target_class_option
45
+ @target_class_name ||= @target_class_option.to_s if @target_class_option
46
+ end
47
+
48
+ def target_class_name_or_nil
49
+ @target_class_name_or_nil ||= target_class_name || 'nil'
46
50
  end
47
51
 
48
52
  def target_class
@@ -75,9 +79,7 @@ module Neo4j
75
79
  end
76
80
  end
77
81
 
78
- def relationship_class
79
- @relationship_class
80
- end
82
+ attr_reader :relationship_class
81
83
 
82
84
  def relationship_class_type
83
85
  @relationship_class = @relationship_class.constantize if @relationship_class.class == String || @relationship_class == Symbol
@@ -104,6 +106,10 @@ module Neo4j
104
106
  properties
105
107
  end
106
108
 
109
+ def unique?
110
+ @origin ? origin_association.unique? : !!@unique
111
+ end
112
+
107
113
  private
108
114
 
109
115
  def get_direction(relationship_cypher, create)
@@ -129,19 +135,24 @@ module Neo4j
129
135
  p.size == 0 ? '' : " {#{p}}"
130
136
  end
131
137
 
138
+ def origin_association
139
+ target_class.associations[@origin]
140
+ end
141
+
132
142
  def origin_type
133
- target_class.associations[@origin].relationship_type
143
+ origin_association.relationship_type
134
144
  end
135
145
 
136
146
  private
137
147
 
138
- def set_vars_from_options(options)
139
- validate_option_combinations(options)
148
+ def apply_vars_from_options(options)
140
149
  @target_class_option = target_class_option(options)
141
150
  @callbacks = {before: options[:before], after: options[:after]}
142
151
  @origin = options[:origin] && options[:origin].to_sym
143
152
  @relationship_class = options[:rel_class]
144
153
  @relationship_type = options[:type] && options[:type].to_sym
154
+ @dependent = options[:dependent]
155
+ @unique = options[:unique]
145
156
  end
146
157
 
147
158
  # Return basic details about association as declared in the model
@@ -151,15 +162,25 @@ module Neo4j
151
162
  "#{type} #{direction.inspect}, #{name.inspect}"
152
163
  end
153
164
 
165
+ def validate_init_arguments(type, direction, options)
166
+ validate_option_combinations(options)
167
+ check_valid_type_and_dir(type, direction)
168
+ end
169
+
154
170
  def check_valid_type_and_dir(type, direction)
155
- raise ArgumentError, "Invalid association type: #{type.inspect} (valid value: :has_many and :has_one)" if not [:has_many, :has_one].include?(type.to_sym)
156
- raise ArgumentError, "Invalid direction: #{direction.inspect} (valid value: :out, :in, and :both)" if not [:out, :in, :both].include?(direction.to_sym)
171
+ fail ArgumentError, "Invalid association type: #{type.inspect} (valid value: :has_many and :has_one)" if not [:has_many, :has_one].include?(type.to_sym)
172
+ fail ArgumentError, "Invalid direction: #{direction.inspect} (valid value: :out, :in, and :both)" if not [:out, :in, :both].include?(direction.to_sym)
157
173
  end
158
174
 
159
175
  def validate_option_combinations(options)
160
- raise ArgumentError, "Cannot specify both :type and :origin (#{base_declaration})" if options[:type] && options[:origin]
161
- raise ArgumentError, "Cannot specify both :type and :rel_class (#{base_declaration})" if options[:type] && options[:rel_class]
162
- raise ArgumentError, "Cannot specify both :origin and :rel_class (#{base_declaration}" if options[:origin] && options[:rel_class]
176
+ fail ArgumentError, "Cannot specify both :type and :origin (#{base_declaration})" if options[:type] && options[:origin]
177
+ fail ArgumentError, "Cannot specify both :type and :rel_class (#{base_declaration})" if options[:type] && options[:rel_class]
178
+ fail ArgumentError, "Cannot specify both :origin and :rel_class (#{base_declaration}" if options[:origin] && options[:rel_class]
179
+ end
180
+
181
+ VALID_DEPENDENT_TYPES = [:delete, :delete_orphans, :destroy_orphans, :destroy, nil]
182
+ def validate_dependent(value)
183
+ fail ArgumentError, "Invalid dependent value: #{value.inspect}" if not VALID_DEPENDENT_TYPES.include?(value)
163
184
  end
164
185
 
165
186
  # Determine if model class as derived from the association name would be different than the one specified via the model_class key
@@ -174,22 +195,16 @@ module Neo4j
174
195
  end
175
196
 
176
197
  def validate_origin!
177
- if @origin
178
- if target_class
179
- if association = target_class.associations[@origin]
180
- if @direction == association.direction
181
- raise ArgumentError, "Origin `#{@origin.inspect}` (specified in #{base_declaration}) has same direction `#{@direction}`)"
182
- end
183
- else
184
- raise ArgumentError, "Origin `#{@origin.inspect}` association not found for #{target_class} (specified in #{base_declaration})"
185
- end
186
- else
187
- raise ArgumentError, "Cannot use :origin without a model_class (implied or explicit)"
188
- end
189
- end
198
+ return if not @origin
199
+
200
+ fail ArgumentError, 'Cannot use :origin without a model_class (implied or explicit)' if not target_class
201
+
202
+ association = origin_association
203
+ fail ArgumentError, "Origin `#{@origin.inspect}` association not found for #{target_class} (specified in #{base_declaration})" if not association
204
+
205
+ fail ArgumentError, "Origin `#{@origin.inspect}` (specified in #{base_declaration}) has same direction `#{@direction}`)" if @direction == association.direction
190
206
  end
191
207
  end
192
208
  end
193
209
  end
194
210
  end
195
-
@@ -1,5 +1,4 @@
1
1
  module Neo4j::ActiveNode
2
-
3
2
  # This module makes it possible to use other IDs than the build it neo4j id (neo_id)
4
3
  #
5
4
  # @example using generated UUIDs
@@ -27,16 +26,16 @@ module Neo4j::ActiveNode
27
26
 
28
27
  module TypeMethods
29
28
  def define_id_methods(clazz, name, conf)
30
- raise "Expected a Hash, got #{conf.class} (#{conf.to_s}) for id_property" unless conf.is_a?(Hash)
29
+ fail "Expected a Hash, got #{conf.class} (#{conf}) for id_property" unless conf.is_a?(Hash)
31
30
  if conf[:on]
32
31
  define_custom_method(clazz, name, conf[:on])
33
32
  elsif conf[:auto]
34
- raise "only :uuid auto id_property allowed, got #{conf[:auto]}" unless conf[:auto] == :uuid
33
+ fail "only :uuid auto id_property allowed, got #{conf[:auto]}" unless conf[:auto] == :uuid
35
34
  define_uuid_method(clazz, name)
36
35
  elsif conf.empty?
37
36
  define_property_method(clazz, name)
38
37
  else
39
- raise "Illegal value #{conf.inspect} for id_property, expected :on or :auto"
38
+ fail "Illegal value #{conf.inspect} for id_property, expected :on or :auto"
40
39
  end
41
40
  end
42
41
 
@@ -45,23 +44,22 @@ module Neo4j::ActiveNode
45
44
  def define_property_method(clazz, name)
46
45
  clear_methods(clazz, name)
47
46
 
48
- clazz.module_eval(%Q{
47
+ clazz.module_eval(%(
49
48
  def id
50
- persisted? ? #{name.to_sym == :id ? 'attribute(\'id\')' : name} : nil
49
+ _persisted_obj ? #{name.to_sym == :id ? 'attribute(\'id\')' : name} : nil
51
50
  end
52
51
 
53
52
  validates_uniqueness_of :#{name}
54
53
 
55
54
  property :#{name}
56
- }, __FILE__, __LINE__)
57
-
55
+ ), __FILE__, __LINE__)
58
56
  end
59
57
 
60
58
 
61
59
  def define_uuid_method(clazz, name)
62
60
  clear_methods(clazz, name)
63
61
 
64
- clazz.module_eval(%Q{
62
+ clazz.module_eval(%(
65
63
  default_property :#{name} do
66
64
  ::SecureRandom.uuid
67
65
  end
@@ -71,13 +69,13 @@ module Neo4j::ActiveNode
71
69
  end
72
70
 
73
71
  alias_method :id, :#{name}
74
- }, __FILE__, __LINE__)
72
+ ), __FILE__, __LINE__)
75
73
  end
76
74
 
77
75
  def define_custom_method(clazz, name, on)
78
76
  clear_methods(clazz, name)
79
77
 
80
- clazz.module_eval(%Q{
78
+ clazz.module_eval(%{
81
79
  default_property :#{name} do |instance|
82
80
  raise "Specifying custom id_property #{name} on none existing method #{on}" unless instance.respond_to?(:#{on})
83
81
  instance.#{on}
@@ -93,15 +91,15 @@ module Neo4j::ActiveNode
93
91
 
94
92
  def clear_methods(clazz, name)
95
93
  if clazz.method_defined?(name)
96
- clazz.module_eval(%Q{
94
+ clazz.module_eval(%(
97
95
  undef_method :#{name}
98
- }, __FILE__, __LINE__)
96
+ ), __FILE__, __LINE__)
99
97
  end
100
98
 
101
99
  if clazz.attribute_names.include?(name.to_s)
102
- clazz.module_eval(%Q{
100
+ clazz.module_eval(%(
103
101
  undef_property :#{name}
104
- }, __FILE__, __LINE__)
102
+ ), __FILE__, __LINE__)
105
103
  end
106
104
  end
107
105
 
@@ -110,7 +108,6 @@ module Neo4j::ActiveNode
110
108
 
111
109
 
112
110
  module ClassMethods
113
-
114
111
  def find_by_neo_id(id)
115
112
  Neo4j::Node.load(id)
116
113
  end
@@ -125,7 +122,7 @@ module Neo4j::ActiveNode
125
122
 
126
123
  def id_property(name, conf = {})
127
124
  begin
128
- if has_id_property?
125
+ if id_property?
129
126
  unless mapped_label.uniqueness_constraints[:property_keys].include?([name])
130
127
  drop_constraint(id_property_name, type: :unique)
131
128
  end
@@ -142,7 +139,15 @@ module Neo4j::ActiveNode
142
139
  end
143
140
  end
144
141
 
142
+ # rubocop:disable Style/PredicateName
145
143
  def has_id_property?
144
+ ActiveSupport::Deprecation.warn 'has_id_property? is deprecated and may be removed from future releases, use id_property? instead.', caller
145
+
146
+ id_property?
147
+ end
148
+ # rubocop:enable Style/PredicateName
149
+
150
+ def id_property?
146
151
  id_property_info && !id_property_info.empty?
147
152
  end
148
153
 
@@ -155,8 +160,6 @@ module Neo4j::ActiveNode
155
160
  end
156
161
 
157
162
  alias_method :primary_key, :id_property_name
158
-
159
163
  end
160
164
  end
161
-
162
165
  end
@@ -1,6 +1,7 @@
1
1
  module Neo4j::ActiveNode::Initialize
2
2
  extend ActiveSupport::Concern
3
3
  include Neo4j::Shared::TypeConverters
4
+ attr_reader :called_by
4
5
 
5
6
  # called when loading the node from the database
6
7
  # @param [Neo4j::Node] persisted_node the node this class wraps
@@ -11,7 +12,7 @@ module Neo4j::ActiveNode::Initialize
11
12
  @association_cache = {}
12
13
  changed_attributes && changed_attributes.clear
13
14
  @attributes = attributes.merge(properties.stringify_keys)
14
- self.default_properties=properties
15
+ self.default_properties = properties
15
16
  @attributes = convert_properties_to :ruby, @attributes
16
17
  end
17
18
 
@@ -21,7 +22,4 @@ module Neo4j::ActiveNode::Initialize
21
22
  def wrapper
22
23
  self
23
24
  end
24
-
25
25
  end
26
-
27
-
@@ -1,7 +1,5 @@
1
1
  module Neo4j
2
2
  module ActiveNode
3
-
4
-
5
3
  # Provides a mapping between neo4j labels and Ruby classes
6
4
  module Labels
7
5
  extend ActiveSupport::Concern
@@ -52,7 +50,7 @@ module Neo4j
52
50
  # Only for testing purpose
53
51
  # @private
54
52
  def self._wrapped_labels=(wl)
55
- @_wrapped_labels=(wl)
53
+ @_wrapped_labels = (wl)
56
54
  end
57
55
 
58
56
  def self._wrapped_labels
@@ -74,25 +72,24 @@ module Neo4j
74
72
  # Returns the object with the specified neo4j id.
75
73
  # @param [String,Fixnum] id of node to find
76
74
  def find(id)
77
- map_id = Proc.new {|object| object.respond_to?(:id) ? object.send(:id) : object }
75
+ map_id = proc { |object| object.respond_to?(:id) ? object.send(:id) : object }
78
76
 
79
77
  if id.is_a?(Array)
80
- find_by_ids(id.map {|o| map_id.call(o) })
78
+ find_by_ids(id.map { |o| map_id.call(o) })
81
79
  else
82
80
  find_by_id(map_id.call(id))
83
81
  end
84
82
  end
85
83
 
86
84
  # Finds the first record matching the specified conditions. There is no implied ordering so if order matters, you should specify it yourself.
87
- # @param [Hash] args of arguments to find
88
- def find_by(*args)
89
- self.query_as(:n).where(n: eval(args.join)).limit(1).pluck(:n).first
85
+ # @param Hash args of arguments to find
86
+ def find_by(values)
87
+ self.query_as(:n).where(n: values).limit(1).pluck(:n).first
90
88
  end
91
89
 
92
90
  # Like find_by, except that if no record is found, raises a RecordNotFound error.
93
- def find_by!(*args)
94
- a = eval(args.join)
95
- find_by(args) or raise RecordNotFound, "#{self.query_as(:n).where(n: a).limit(1).to_cypher} returned no results"
91
+ def find_by!(values)
92
+ find_by(values) || fail(RecordNotFound, "#{self.query_as(:n).where(n: values).limit(1).to_cypher} returned no results")
96
93
  end
97
94
 
98
95
  # Deletes all nodes and connected relationships from Cypher.
@@ -104,7 +101,7 @@ module Neo4j
104
101
  # Returns each node to Ruby and calls `destroy`. Be careful, as this can be a very slow operation if you have many nodes. It will generate at least
105
102
  # one database query per node in the database, more if callbacks require them.
106
103
  def destroy_all
107
- self.all.each { |n| n.destroy }
104
+ self.all.each(&:destroy)
108
105
  end
109
106
 
110
107
  # Creates a Neo4j index on given property
@@ -141,14 +138,14 @@ module Neo4j
141
138
  # @example
142
139
  # Person.constraint :name, type: :unique
143
140
  #
144
- def constraint(property, constraints, session = Neo4j::Session.current)
141
+ def constraint(property, constraints)
145
142
  Neo4j::Session.on_session_available do |session|
146
143
  label = Neo4j::Label.create(mapped_label_name)
147
144
  label.create_constraint(property, constraints, session)
148
145
  end
149
146
  end
150
147
 
151
- def drop_constraint(property, constraint, session = Neo4j::Session.current)
148
+ def drop_constraint(property, constraint)
152
149
  Neo4j::Session.on_session_available do |session|
153
150
  label = Neo4j::Label.create(mapped_label_name)
154
151
  label.drop_constraint(property, constraint, session)
@@ -166,7 +163,7 @@ module Neo4j
166
163
 
167
164
  # @return [Symbol] the label that this class has which corresponds to a Ruby class
168
165
  def mapped_label_name
169
- @_label_name || (self.name.nil? ? object_id.to_s.to_sym : self.name.to_sym)
166
+ @mapped_label_name || (self.name.nil? ? object_id.to_s.to_sym : self.name.to_sym)
170
167
  end
171
168
 
172
169
  # @return [Neo4j::Label] the label for this class
@@ -180,7 +177,7 @@ module Neo4j
180
177
 
181
178
  def base_class
182
179
  unless self < Neo4j::ActiveNode
183
- raise "#{name} doesn't belong in a hierarchy descending from ActiveNode"
180
+ fail "#{name} doesn't belong in a hierarchy descending from ActiveNode"
184
181
  end
185
182
 
186
183
  if superclass == Object
@@ -204,21 +201,25 @@ module Neo4j
204
201
  else
205
202
  label.create_index(property) unless existing.flatten.include?(property)
206
203
  end
207
-
208
204
  end
209
205
  end
210
206
 
211
207
  def mapped_labels
212
- mapped_label_names.map{|label_name| Neo4j::Label.create(label_name)}
208
+ mapped_label_names.map { |label_name| Neo4j::Label.create(label_name) }
213
209
  end
214
210
 
215
- def set_mapped_label_name(name)
216
- @_label_name = name.to_sym
211
+ def mapped_label_name=(name)
212
+ @mapped_label_name = name.to_sym
217
213
  end
218
214
 
219
- end
215
+ # rubocop:disable Style/AccessorMethodName
216
+ def set_mapped_label_name(name)
217
+ ActiveSupport::Deprecation.warn 'set_mapped_label_name is deprecated and may be removed from future releases, use self.mapped_label_name= instead.', caller
220
218
 
219
+ self.mapped_label_name = name
220
+ end
221
+ # rubocop:enable Style/AccessorMethodName
222
+ end
221
223
  end
222
-
223
224
  end
224
225
  end
@@ -1,7 +1,6 @@
1
1
  class Neo4j::Node
2
2
  # The wrapping process is what transforms a raw CypherNode or EmbeddedNode from Neo4j::Core into a healthy ActiveNode (or ActiveRel) object.
3
3
  module Wrapper
4
-
5
4
  # this is a plugin in the neo4j-core so that the Ruby wrapper will be wrapped around the Neo4j::Node objects
6
5
  def wrapper
7
6
  self.props.symbolize_keys!
@@ -26,22 +25,20 @@ class Neo4j::Node
26
25
 
27
26
  # Makes the determination of whether to use <tt>_classname</tt> (or whatever is defined by config) or the node's labels.
28
27
  def sorted_wrapper_classes
29
- if self.props.is_a?(Hash) && self.props.has_key?(Neo4j::Config.class_name_property)
28
+ if self.props.is_a?(Hash) && self.props.key?(Neo4j::Config.class_name_property)
30
29
  self.props[Neo4j::Config.class_name_property].constantize
31
30
  else
32
31
  wrappers = _class_wrappers
33
32
  return self if wrappers.nil?
34
- wrapper_classes = wrappers.map{|w| Neo4j::ActiveNode::Labels._wrapped_labels[w]}
33
+ wrapper_classes = wrappers.map { |w| Neo4j::ActiveNode::Labels._wrapped_labels[w] }
35
34
  wrapper_classes.sort.first
36
35
  end
37
36
  end
38
37
 
39
38
  def load_class_from_label(label_name)
40
- begin
41
- label_name.to_s.split("::").inject(Kernel) {|container, name| container.const_get(name.to_s) }
42
- rescue NameError
43
- nil
44
- end
39
+ label_name.to_s.split('::').inject(Kernel) { |container, name| container.const_get(name.to_s) }
40
+ rescue NameError
41
+ nil
45
42
  end
46
43
 
47
44
  def _class_wrappers