neo4j 6.1.12 → 7.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.
@@ -0,0 +1,83 @@
1
+ module Neo4j
2
+ module ActiveNode
3
+ module Query
4
+ module QueryProxyMethodsOfMassUpdating
5
+ # Updates some attributes of a group of nodes within a QP chain.
6
+ # The optional argument makes sense only of `updates` is a string.
7
+ # @param [Hash,String] updates An hash or a string of parameters to be updated.
8
+ # @param [Hash] params An hash of parameters for the update string. It's ignored if `updates` is an Hash.
9
+ def update_all(updates, params = {})
10
+ # Move this to ActiveNode module?
11
+ update_all_with_query(identity, updates, params)
12
+ end
13
+
14
+ # Updates some attributes of a group of relationships within a QP chain.
15
+ # The optional argument makes sense only of `updates` is a string.
16
+ # @param [Hash,String] updates An hash or a string of parameters to be updated.
17
+ # @param [Hash] params An hash of parameters for the update string. It's ignored if `updates` is an Hash.
18
+ def update_all_rels(updates, params = {})
19
+ fail 'Cannot update rels without a relationship variable.' unless @rel_var
20
+ update_all_with_query(@rel_var, updates, params)
21
+ end
22
+
23
+ # Deletes a group of nodes and relationships within a QP chain. When identifier is omitted, it will remove the last link in the chain.
24
+ # The optional argument must be a node identifier. A relationship identifier will result in a Cypher Error
25
+ # @param identifier [String,Symbol] the optional identifier of the link in the chain to delete.
26
+ def delete_all(identifier = nil)
27
+ query_with_target(identifier) do |target|
28
+ begin
29
+ self.query.with(target).optional_match("(#{target})-[#{target}_rel]-()").delete("#{target}, #{target}_rel").exec
30
+ rescue Neo4j::Session::CypherError
31
+ self.query.delete(target).exec
32
+ end
33
+ clear_source_object_cache
34
+ end
35
+ end
36
+
37
+ # Deletes the relationship between a node and its last link in the QueryProxy chain. Executed in the database, callbacks will not run.
38
+ def delete(node)
39
+ self.match_to(node).query.delete(rel_var).exec
40
+ clear_source_object_cache
41
+ end
42
+
43
+ # Deletes the relationships between all nodes for the last step in the QueryProxy chain. Executed in the database, callbacks will not be run.
44
+ def delete_all_rels
45
+ return unless start_object && start_object._persisted_obj
46
+ self.query.delete(rel_var).exec
47
+ end
48
+
49
+ # Deletes the relationships between all nodes for the last step in the QueryProxy chain and replaces them with relationships to the given nodes.
50
+ # Executed in the database, callbacks will not be run.
51
+ def replace_with(node_or_nodes)
52
+ nodes = Array(node_or_nodes)
53
+
54
+ self.delete_all_rels
55
+ nodes.each { |node| self << node }
56
+ end
57
+
58
+ # Returns all relationships between a node and its last link in the QueryProxy chain, destroys them in Ruby. Callbacks will be run.
59
+ def destroy(node)
60
+ self.rels_to(node).map!(&:destroy)
61
+ clear_source_object_cache
62
+ end
63
+
64
+ private
65
+
66
+ def update_all_with_query(var_name, updates, params)
67
+ query = all.query
68
+
69
+ case updates
70
+ when Hash then query.set(var_name => updates).pluck("count(#{var_name})").first
71
+ when String then query.set(updates).params(params).pluck("count(#{var_name})").first
72
+ else
73
+ fail ArgumentError, "Invalid parameter type #{updates.class} for `updates`."
74
+ end
75
+ end
76
+
77
+ def clear_source_object_cache
78
+ self.source_object.clear_association_cache if self.source_object.respond_to?(:clear_association_cache)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -1,11 +1,9 @@
1
1
  module Neo4j
2
2
  module ActiveNode
3
3
  module QueryMethods
4
- class InvalidParameterError < StandardError; end
5
-
6
4
  def exists?(node_condition = nil)
7
5
  unless node_condition.is_a?(Integer) || node_condition.is_a?(Hash) || node_condition.nil?
8
- fail(InvalidParameterError, ':exists? only accepts ids or conditions')
6
+ fail(Neo4j::InvalidParameterError, ':exists? only accepts ids or conditions')
9
7
  end
10
8
  query_start = exists_query_start(node_condition)
11
9
  start_q = query_start.respond_to?(:query_as) ? query_start.query_as(:n) : query_start
@@ -24,7 +22,7 @@ module Neo4j
24
22
 
25
23
  # @return [Integer] number of nodes of this class
26
24
  def count(distinct = nil)
27
- fail(InvalidParameterError, ':count accepts `distinct` or nil as a parameter') unless distinct.nil? || distinct == :distinct
25
+ fail(Neo4j::InvalidParameterError, ':count accepts `distinct` or nil as a parameter') unless distinct.nil? || distinct == :distinct
28
26
  q = distinct.nil? ? 'n' : 'DISTINCT n'
29
27
  self.query_as(:n).return("count(#{q}) AS count").first.count
30
28
  end
@@ -35,13 +35,13 @@ module Neo4j::ActiveNode
35
35
  #
36
36
  # @see http://guides.rubyonrails.org/active_record_querying.html#scopes
37
37
  def scope(name, proc)
38
- scopes[name.to_sym] = proc
38
+ _scope[name.to_sym] = proc
39
39
 
40
40
  klass = class << self; self; end
41
41
  klass.instance_eval do
42
42
  define_method(name) do |query_params = nil, _ = nil|
43
43
  eval_context = ScopeEvalContext.new(self, current_scope || self.query_proxy)
44
- proc = full_scopes[name.to_sym]
44
+ proc = _scope[name.to_sym]
45
45
  _call_scope_context(eval_context, query_params, proc)
46
46
  end
47
47
  end
@@ -56,15 +56,11 @@ module Neo4j::ActiveNode
56
56
  # rubocop:enable Style/PredicateName
57
57
 
58
58
  def scope?(name)
59
- full_scopes.key?(name.to_sym)
59
+ _scope.key?(name.to_sym)
60
60
  end
61
61
 
62
- def scopes
63
- @scopes ||= {}
64
- end
65
-
66
- def full_scopes
67
- scopes.merge(self.superclass.respond_to?(:scopes) ? self.superclass.scopes : {})
62
+ def _scope
63
+ @_scope ||= {}
68
64
  end
69
65
 
70
66
  def _call_scope_context(eval_context, query_params, proc)
@@ -17,8 +17,9 @@ module Neo4j
17
17
  include Neo4j::ActiveRel::Callbacks
18
18
  include Neo4j::ActiveRel::Query
19
19
  include Neo4j::ActiveRel::Types
20
+ include Neo4j::Shared::Enum
20
21
 
21
- class FrozenRelError < StandardError; end
22
+ class FrozenRelError < Neo4j::Error; end
22
23
 
23
24
  def initialize(from_node = nil, to_node = nil, args = nil)
24
25
  load_nodes(node_or_nil(from_node), node_or_nil(to_node))
@@ -3,8 +3,7 @@ module Neo4j::ActiveRel
3
3
  # It's important (or maybe not really IMPORTANT, but at least worth mentioning) that calling method_missing
4
4
  # will result in a query to load the node if the node is not already loaded.
5
5
  class RelatedNode
6
- class InvalidParameterError < StandardError; end
7
- class UnsetRelatedNodeError < StandardError; end
6
+ class UnsetRelatedNodeError < Neo4j::Error; end
8
7
 
9
8
  # ActiveRel's related nodes can be initialized with nothing, an integer, or a fully wrapped node.
10
9
  #
@@ -13,7 +12,7 @@ module Neo4j::ActiveRel
13
12
  # Initialization with an integer happens when a relationship is loaded from the database. It loads using the ID
14
13
  # because that is provided by the Cypher response and does not require an extra query.
15
14
  def initialize(node = nil)
16
- @node = valid_node_param?(node) ? node : (fail InvalidParameterError, 'RelatedNode must be initialized with either a node ID or node')
15
+ @node = valid_node_param?(node) ? node : (fail Neo4j::InvalidParameterError, 'RelatedNode must be initialized with either a node ID or node')
17
16
  end
18
17
 
19
18
  # Loads the node if needed, then conducts comparison.
data/lib/neo4j/errors.rb CHANGED
@@ -1,12 +1,28 @@
1
1
  module Neo4j
2
2
  # Neo4j.rb Errors
3
3
  # Generic Neo4j.rb exception class.
4
- class Neo4jrbError < StandardError
4
+ class Error < StandardError
5
5
  end
6
6
 
7
7
  # Raised when Neo4j.rb cannot find record by given id.
8
- class RecordNotFound < Neo4jrbError
8
+ class RecordNotFound < Error
9
+ attr_reader :model, :primary_key, :id
10
+
11
+ def initialize(message = nil, model = nil, primary_key = nil, id = nil)
12
+ @primary_key = primary_key
13
+ @model = model
14
+ @id = id
15
+
16
+ super(message)
17
+ end
9
18
  end
10
19
 
11
- class InvalidPropertyOptionsError < Neo4jrbError; end
20
+ class InvalidPropertyOptionsError < Error; end
21
+
22
+ class InvalidParameterError < Error; end
23
+
24
+ class UnknownTypeConverterError < Error; end
25
+
26
+ class DangerousAttributeError < ScriptError; end
27
+ class UnknownAttributeError < NoMethodError; end
12
28
  end
data/lib/neo4j/railtie.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require 'active_support/notifications'
2
2
  require 'rails/railtie'
3
+ # Need the action_dispatch railtie to have action_dispatch.rescue_responses initialized correctly
4
+ require 'action_dispatch/railtie'
3
5
 
4
6
  module Neo4j
5
7
  class Railtie < ::Rails::Railtie
@@ -11,6 +13,17 @@ module Neo4j
11
13
  end
12
14
  end
13
15
 
16
+ # Rescue responses similar to ActiveRecord.
17
+ # For rails 3.2 and 4.0
18
+ if config.action_dispatch.respond_to?(:rescue_responses)
19
+ config.action_dispatch.rescue_responses.merge!(
20
+ 'Neo4j::RecordNotFound' => :not_found
21
+ )
22
+ else
23
+ # For rails 3.0 and 3.1
24
+ ActionDispatch::ShowExceptions.rescue_responses['Neo4j::RecordNotFound'] = :not_found
25
+ end
26
+
14
27
  # Add ActiveModel translations to the I18n load_path
15
28
  initializer 'i18n' do
16
29
  config.i18n.load_path += Dir[File.join(File.dirname(__FILE__), '..', '..', '..', 'config', 'locales', '*.{rb,yml}')]
@@ -0,0 +1,207 @@
1
+ module Neo4j::Shared
2
+ # Attributes provides a set of class methods for defining an attributes
3
+ # schema and instance methods for reading and writing attributes.
4
+ #
5
+ # @example Usage
6
+ # class Person
7
+ # include Neo4j::Shared::Attributes
8
+ # attribute :name
9
+ # end
10
+ #
11
+ # person = Person.new
12
+ # person.name = "Ben Poweski"
13
+ #
14
+ # Originally part of ActiveAttr, https://github.com/cgriego/active_attr
15
+ module Attributes
16
+ extend ActiveSupport::Concern
17
+ include ActiveModel::AttributeMethods
18
+
19
+ # Methods deprecated on the Object class which can be safely overridden
20
+ DEPRECATED_OBJECT_METHODS = %w(id type)
21
+
22
+ included do
23
+ attribute_method_suffix '' if attribute_method_matchers.none? { |matcher| matcher.prefix == '' && matcher.suffix == '' }
24
+ attribute_method_suffix '='
25
+ end
26
+
27
+ # Performs equality checking on the result of attributes and its type.
28
+ #
29
+ # @example Compare for equality.
30
+ # model == other
31
+ #
32
+ # @param [ActiveAttr::Attributes, Object] other The other model to compare
33
+ #
34
+ # @return [true, false] True if attributes are equal and other is instance
35
+ # of the same Class, false if not.
36
+ def ==(other)
37
+ return false unless other.instance_of? self.class
38
+ attributes == other.attributes
39
+ end
40
+
41
+ # Returns a Hash of all attributes
42
+ #
43
+ # @example Get attributes
44
+ # person.attributes # => {"name"=>"Ben Poweski"}
45
+ #
46
+ # @return [Hash{String => Object}] The Hash of all attributes
47
+ def attributes
48
+ attributes_map { |name| send name }
49
+ end
50
+
51
+ # Write a single attribute to the model's attribute hash.
52
+ #
53
+ # @example Write the attribute with write_attribute
54
+ # person.write_attribute(:name, "Benjamin")
55
+ # @example Write an attribute with bracket syntax
56
+ # person[:name] = "Benjamin"
57
+ #
58
+ # @param [String, Symbol, #to_s] name The name of the attribute to update.
59
+ # @param [Object] value The value to set for the attribute.
60
+ #
61
+ # @raise [UnknownAttributeError] if the attribute is unknown
62
+ def write_attribute(name, value)
63
+ if respond_to? "#{name}="
64
+ send "#{name}=", value
65
+ else
66
+ fail Neo4j::UnknownAttributeError, "unknown attribute: #{name}"
67
+ end
68
+ end
69
+ alias_method :[]=, :write_attribute
70
+
71
+ private
72
+
73
+ # Read an attribute from the attributes hash
74
+ def attribute(name)
75
+ @attributes ||= {}
76
+ @attributes[name]
77
+ end
78
+
79
+ # Write an attribute to the attributes hash
80
+ def attribute=(name, value)
81
+ @attributes ||= {}
82
+ @attributes[name] = value
83
+ end
84
+
85
+ # Maps all attributes using the given block
86
+ #
87
+ # @example Stringify attributes
88
+ # person.attributes_map { |name| send(name).to_s }
89
+ #
90
+ # @yield [name] block called to return hash value
91
+ # @yieldparam [String] name The name of the attribute to map.
92
+ #
93
+ # @return [Hash{String => Object}] The Hash of mapped attributes
94
+ def attributes_map
95
+ Hash[self.class.attribute_names.map { |name| [name, yield(name)] }]
96
+ end
97
+
98
+ module ClassMethods
99
+ # Defines an attribute
100
+ #
101
+ # For each attribute that is defined, a getter and setter will be
102
+ # added as an instance method to the model. An
103
+ # {AttributeDefinition} instance will be added to result of the
104
+ # attributes class method.
105
+ #
106
+ # @example Define an attribute.
107
+ # attribute :name
108
+ #
109
+ # @param (see AttributeDefinition#initialize)
110
+ #
111
+ # @raise [DangerousAttributeError] if the attribute name conflicts with
112
+ # existing methods
113
+ #
114
+ # @return [AttributeDefinition] Attribute's definition
115
+ def attribute(name)
116
+ if dangerous_attribute?(name)
117
+ fail Neo4j::DangerousAttributeError, %(an attribute method named "#{name}" would conflict with an existing method)
118
+ else
119
+ attribute!(name)
120
+ end
121
+ end
122
+
123
+ # Returns an Array of attribute names as Strings
124
+ #
125
+ # @example Get attribute names
126
+ # Person.attribute_names
127
+ #
128
+ # @return [Array<String>] The attribute names
129
+ def attribute_names
130
+ attributes.keys
131
+ end
132
+
133
+ # Returns a Hash of AttributeDefinition instances
134
+ #
135
+ # @example Get attribute definitions
136
+ # Person.attributes
137
+ #
138
+ # @return [ActiveSupport::HashWithIndifferentAccess{String => Neo4j::Shared::AttributeDefinition}]
139
+ # The Hash of AttributeDefinition instances
140
+ def attributes
141
+ @attributes ||= ActiveSupport::HashWithIndifferentAccess.new
142
+ end
143
+
144
+ # Determine if a given attribute name is dangerous
145
+ #
146
+ # Some attribute names can cause conflicts with existing methods
147
+ # on an object. For example, an attribute named "timeout" would
148
+ # conflict with the timeout method that Ruby's Timeout library
149
+ # mixes into Object.
150
+ #
151
+ # @example Testing a harmless attribute
152
+ # Person.dangerous_attribute? :name #=> false
153
+ #
154
+ # @example Testing a dangerous attribute
155
+ # Person.dangerous_attribute? :nil #=> "nil?"
156
+ #
157
+ # @param name Attribute name
158
+ #
159
+ # @return [false, String] False or the conflicting method name
160
+ def dangerous_attribute?(name)
161
+ attribute_methods(name).detect do |method_name|
162
+ !DEPRECATED_OBJECT_METHODS.include?(method_name.to_s) && allocate.respond_to?(method_name, true)
163
+ end unless attribute_names.include? name.to_s
164
+ end
165
+
166
+ # Returns the class name plus its attribute names
167
+ #
168
+ # @example Inspect the model's definition.
169
+ # Person.inspect
170
+ #
171
+ # @return [String] Human-readable presentation of the attributes
172
+ def inspect
173
+ inspected_attributes = attribute_names.sort
174
+ attributes_list = "(#{inspected_attributes.join(', ')})" unless inspected_attributes.empty?
175
+ "#{name}#{attributes_list}"
176
+ end
177
+
178
+ protected
179
+
180
+ # Assign a set of attribute definitions, used when subclassing models
181
+ #
182
+ # @param [Array<Neo4j::Shared::DeclaredProperties>] The Array of
183
+ # AttributeDefinition instances
184
+ def attributes=(attributes)
185
+ @attributes = attributes
186
+ end
187
+
188
+ # Overrides ActiveModel::AttributeMethods to backport 3.2 fix
189
+ def instance_method_already_implemented?(method_name)
190
+ generated_attribute_methods.method_defined?(method_name)
191
+ end
192
+
193
+ private
194
+
195
+ # Expand an attribute name into its generated methods names
196
+ def attribute_methods(name)
197
+ attribute_method_matchers.map { |matcher| matcher.method_name name }
198
+ end
199
+
200
+ # Ruby inherited hook to assign superclass attributes to subclasses
201
+ def inherited(subclass)
202
+ super
203
+ subclass.attributes = attributes.dup
204
+ end
205
+ end
206
+ end
207
+ end
@@ -101,8 +101,7 @@ module Neo4j::Shared
101
101
  def unregister(name)
102
102
  # might need to be include?(name.to_s)
103
103
  fail ArgumentError, "Argument `#{name}` not an attribute" if not registered_properties[name]
104
- declared_prop = registered_properties[name]
105
- registered_properties.delete(declared_prop)
104
+ registered_properties.delete(name)
106
105
  unregister_magic_typecaster(name)
107
106
  unregister_property_default(name)
108
107
  end
@@ -133,11 +132,6 @@ module Neo4j::Shared
133
132
  @magic_typecast_properties ||= {}
134
133
  end
135
134
 
136
- # The known mappings of declared properties and their primitive types.
137
- def upstream_primitives
138
- @upstream_primitives ||= {}
139
- end
140
-
141
135
  EXCLUDED_TYPES = [Array, Range, Regexp]
142
136
  def value_for_where(key, value)
143
137
  return value unless prop = registered_properties[key]
@@ -166,7 +160,7 @@ module Neo4j::Shared
166
160
 
167
161
  # Prevents repeated calls to :_attribute_type, which isn't free and never changes.
168
162
  def fetch_upstream_primitive(attr)
169
- upstream_primitives[attr] || upstream_primitives[attr] = klass._attribute_type(attr)
163
+ registered_properties[attr].type
170
164
  end
171
165
 
172
166
  private