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.
@@ -1,7 +1,9 @@
1
1
  module Neo4j::Shared
2
2
  # Contains methods related to the management
3
3
  class DeclaredProperty
4
- class IllegalPropertyError < StandardError; end
4
+ include Comparable
5
+
6
+ class IllegalPropertyError < Neo4j::Error; end
5
7
  include Neo4j::Shared::DeclaredProperty::Index
6
8
 
7
9
  ILLEGAL_PROPS = %w(from_node to_node start_node end_node)
@@ -9,12 +11,46 @@ module Neo4j::Shared
9
11
 
10
12
  def initialize(name, options = {})
11
13
  fail IllegalPropertyError, "#{name} is an illegal property" if ILLEGAL_PROPS.include?(name.to_s)
12
- @name = @name_sym = name
14
+ fail TypeError, "can't convert #{name.class} into Symbol" unless name.respond_to?(:to_sym)
15
+ @name = @name_sym = name.to_sym
13
16
  @name_string = name.to_s
14
17
  @options = options
15
18
  fail_invalid_options!
16
19
  end
17
20
 
21
+ # Compare attribute definitions
22
+ #
23
+ # @example
24
+ # attribute_definition <=> other
25
+ #
26
+ # @param [Neo4j::Shared::DeclaredProperty, Object] other The other
27
+ # attribute definition to compare with.
28
+ #
29
+ # @return [-1, 0, 1, nil]
30
+ def <=>(other)
31
+ return nil unless other.instance_of? self.class
32
+ return nil if name == other.name && options != other.options
33
+ self.to_s <=> other.to_s
34
+ end
35
+
36
+ def inspect
37
+ options_description = options.map { |key, value| "#{key.inspect} => #{value.inspect}" }.sort.join(', ')
38
+ inspected_options = ", #{options_description}" unless options_description.empty?
39
+ "attribute :#{name}#{inspected_options}"
40
+ end
41
+
42
+ def to_s
43
+ name.to_s
44
+ end
45
+
46
+ def to_sym
47
+ name
48
+ end
49
+
50
+ def [](key)
51
+ respond_to?(key) ? public_send(key) : nil
52
+ end
53
+
18
54
  def register
19
55
  register_magic_properties
20
56
  end
@@ -30,6 +66,7 @@ module Neo4j::Shared
30
66
  def default_value
31
67
  options[:default]
32
68
  end
69
+ alias_method :default, :default_value
33
70
 
34
71
  def fail_invalid_options!
35
72
  case
@@ -74,7 +111,7 @@ module Neo4j::Shared
74
111
  converter = options[:serializer]
75
112
  return unless converter
76
113
  options[:type] = converter.convert_type
77
- options[:typecaster] = ActiveAttr::Typecasting::ObjectTypecaster.new
114
+ options[:typecaster] = Neo4j::Shared::TypeConverters::ObjectConverter
78
115
  Neo4j::Shared::TypeConverters.register_converter(converter)
79
116
  end
80
117
  end
@@ -0,0 +1,148 @@
1
+ module Neo4j::Shared
2
+ module Enum
3
+ extend ActiveSupport::Concern
4
+
5
+ class ConflictingEnumMethodError < Neo4j::Error; end
6
+
7
+ module ClassMethods
8
+ attr_reader :neo4j_enum_data
9
+
10
+ # Similar to ActiveRecord enum, maps an integer value on the database to
11
+ # a set of enum keys.
12
+ #
13
+ # @example Base example
14
+ # class Media
15
+ # include Neo4j::ActiveNode
16
+ # enum type: [:image, :video, :unknown]
17
+ # end
18
+ # Media.types # => { :images => 0, :video => 1, :unknown => 2 }
19
+ #
20
+ # media.video!
21
+ # media.image? # => false
22
+ # media.type # => :video
23
+ #
24
+ # Media.videos # => All medias with type = 1 (:video)
25
+ # Media.where(type: :video) # => All medias with type = 1 (:video)
26
+ #
27
+ # @example Prefix-ing an enum
28
+ # Media.enum type: [:image, :video, :unknown], _prefix: :enum
29
+ #
30
+ # media.enum_video!
31
+ # media.enum_video? # => true
32
+ #
33
+ # @example Suffix-ing an enum
34
+ # Media.enum type: [:image, :video, :unknown], _suffix: true
35
+ #
36
+ # media.video_type!
37
+ # media.video_type? # => true
38
+ #
39
+ # @example Disable index: :exact for enum elements
40
+ # Media.enum type: [:image, :video, :unknown], _index: false
41
+ #
42
+ # @example Define a custom mapping for keys-numbers
43
+ # Media.enum type: { image: 1, video: 2, unknown: 3 }
44
+ #
45
+ # @see http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html
46
+ def enum(parameters = {})
47
+ options, parameters = *split_options_and_parameters(parameters)
48
+ parameters.each do |property_name, enum_keys|
49
+ enum_keys = normalize_key_list enum_keys
50
+ @neo4j_enum_data ||= {}
51
+ @neo4j_enum_data[property_name] = enum_keys
52
+ define_property(property_name, enum_keys, options)
53
+ define_enum_methods(property_name, enum_keys, options)
54
+ end
55
+ end
56
+
57
+ protected
58
+
59
+ def normalize_key_list(enum_keys)
60
+ case enum_keys
61
+ when Hash
62
+ enum_keys
63
+ when Array
64
+ Hash[enum_keys.each_with_index.to_a]
65
+ else
66
+ fail ArgumentError, 'Invalid parameter for enum. Please provide an Array or an Hash.'
67
+ end
68
+ end
69
+
70
+ VALID_OPTIONS_FOR_ENUMS = [:_index, :_prefix, :_suffix]
71
+ DEFAULT_OPTIONS_FOR_ENUMS = {
72
+ _index: true
73
+ }
74
+
75
+ def split_options_and_parameters(parameters)
76
+ options = DEFAULT_OPTIONS_FOR_ENUMS.clone
77
+ new_parameters = {}
78
+ parameters.each do |k, v|
79
+ if VALID_OPTIONS_FOR_ENUMS.include? k
80
+ options[k] = v
81
+ else
82
+ new_parameters[k] = v
83
+ end
84
+ end
85
+ [options, new_parameters]
86
+ end
87
+
88
+ def define_property(property_name, enum_keys, options)
89
+ property_options = build_property_options(enum_keys, options)
90
+ property property_name, property_options
91
+ serialize property_name, Neo4j::Shared::TypeConverters::EnumConverter.new(enum_keys)
92
+ end
93
+
94
+ def build_property_options(enum_keys, _options = {})
95
+ {
96
+ default: enum_keys.keys.first
97
+ }
98
+ end
99
+
100
+ def define_enum_methods(property_name, enum_keys, options)
101
+ define_enum_methods_?(property_name, enum_keys, options)
102
+ define_enum_methods_!(property_name, enum_keys, options)
103
+ define_class_methods(property_name, enum_keys)
104
+ end
105
+
106
+ def define_class_methods(property_name, enum_keys)
107
+ plural_property_name = property_name.to_s.pluralize.to_sym
108
+ define_singleton_method(plural_property_name) do
109
+ enum_keys
110
+ end
111
+ end
112
+
113
+ def define_enum_methods_?(property_name, enum_keys, options)
114
+ enum_keys.keys.each do |enum_value|
115
+ method_name = build_method_name(enum_value, property_name, options)
116
+ check_enum_method_conflicts! property_name, :"#{method_name}?"
117
+ define_method("#{method_name}?") do
118
+ __send__(property_name).to_s.to_sym == enum_value
119
+ end
120
+ end
121
+ end
122
+
123
+ def define_enum_methods_!(property_name, enum_keys, options)
124
+ enum_keys.keys.each do |enum_value|
125
+ method_name = build_method_name(enum_value, property_name, options)
126
+ check_enum_method_conflicts! property_name, :"#{method_name}!"
127
+ define_method("#{method_name}!") do
128
+ __send__("#{property_name}=", enum_value)
129
+ end
130
+ end
131
+ end
132
+
133
+ def build_method_name(base_name, property_name, options)
134
+ method_name = base_name
135
+ method_name = "#{method_name}_#{property_name}" if options[:_suffix]
136
+ method_name = "#{options[:_prefix]}_#{method_name}" if options[:_prefix]
137
+ method_name
138
+ end
139
+
140
+ def check_enum_method_conflicts!(property_name, method_name)
141
+ fail ConflictingEnumMethodError,
142
+ "The enum `#{property_name}` is trying to define a `#{method_name}` method, "\
143
+ 'that is already defined. Try to use options `:prefix` or `:suffix` '\
144
+ 'to avoid conflicts.' if instance_methods(false).include?(method_name)
145
+ end
146
+ end
147
+ end
148
+ end
@@ -1,6 +1,6 @@
1
1
  module Neo4j::Shared
2
2
  class FilteredHash
3
- class InvalidHashFilterType < Neo4j::Neo4jrbError; end
3
+ class InvalidHashFilterType < Neo4j::Error; end
4
4
  VALID_SYMBOL_INSTRUCTIONS = [:all, :none]
5
5
  VALID_HASH_INSTRUCTIONS = [:on]
6
6
  VALID_INSTRUCTIONS_TYPES = [Hash, Symbol]
@@ -0,0 +1,58 @@
1
+ module Neo4j::Shared
2
+ # MassAssignment allows you to bulk set and update attributes
3
+ #
4
+ # Including MassAssignment into your model gives it a set of mass assignment
5
+ # methods, similar to those found in ActiveRecord.
6
+ #
7
+ # @example Usage
8
+ # class Person
9
+ # include Neo4j::Shared::MassAssignment
10
+ # end
11
+ #
12
+ # Originally part of ActiveAttr, https://github.com/cgriego/active_attr
13
+ module MassAssignment
14
+ extend ActiveSupport::Concern
15
+ # Mass update a model's attributes
16
+ #
17
+ # @example Assigning a hash
18
+ # person.assign_attributes(:first_name => "Chris", :last_name => "Griego")
19
+ # person.first_name #=> "Chris"
20
+ # person.last_name #=> "Griego"
21
+ #
22
+ # @param [Hash{#to_s => Object}, #each] attributes Attributes used to
23
+ # populate the model
24
+ # @param [Hash, #[]] options Options that affect mass assignment
25
+ def assign_attributes(new_attributes = nil)
26
+ return unless new_attributes.present?
27
+ new_attributes.each do |name, value|
28
+ writer = :"#{name}="
29
+ send(writer, value) if respond_to?(writer)
30
+ end
31
+ end
32
+
33
+ # Mass update a model's attributes
34
+ #
35
+ # @example Assigning a hash
36
+ # person.attributes = { :first_name => "Chris", :last_name => "Griego" }
37
+ # person.first_name #=> "Chris"
38
+ # person.last_name #=> "Griego"
39
+ #
40
+ # @param (see #assign_attributes)
41
+ def attributes=(new_attributes)
42
+ assign_attributes(new_attributes)
43
+ end
44
+
45
+ # Initialize a model with a set of attributes
46
+ #
47
+ # @example Initializing with a hash
48
+ # person = Person.new(:first_name => "Chris", :last_name => "Griego")
49
+ # person.first_name #=> "Chris"
50
+ # person.last_name #=> "Griego"
51
+ #
52
+ # @param (see #assign_attributes)
53
+ def initialize(attributes = nil)
54
+ assign_attributes(attributes)
55
+ super()
56
+ end
57
+ end
58
+ end
@@ -2,15 +2,12 @@ module Neo4j::Shared
2
2
  module Property
3
3
  extend ActiveSupport::Concern
4
4
 
5
- include ActiveAttr::Attributes
6
- include ActiveAttr::MassAssignment
7
- include ActiveAttr::TypecastedAttributes
8
- include ActiveAttr::AttributeDefaults
9
- include ActiveAttr::QueryAttributes
5
+ include Neo4j::Shared::MassAssignment
6
+ include Neo4j::Shared::TypecastedAttributes
10
7
  include ActiveModel::Dirty
11
8
 
12
- class UndefinedPropertyError < RuntimeError; end
13
- class MultiparameterAssignmentError < StandardError; end
9
+ class UndefinedPropertyError < Neo4j::Error; end
10
+ class MultiparameterAssignmentError < Neo4j::Error; end
14
11
 
15
12
  attr_reader :_persisted_obj
16
13
 
@@ -23,11 +20,9 @@ module Neo4j::Shared
23
20
  "#<#{Neo4j::ANSI::YELLOW}#{self.class.name}#{Neo4j::ANSI::CLEAR}#{separator}#{attribute_descriptions}>"
24
21
  end
25
22
 
26
- # TODO: Remove the commented :super entirely once this code is part of a release.
27
- # It calls an init method in active_attr that has a very negative impact on performance.
28
23
  def initialize(attributes = nil)
29
24
  attributes = process_attributes(attributes)
30
- @relationship_props = {} # self.class.extract_association_attributes!(attributes)
25
+ @relationship_props = self.class.extract_association_attributes!(attributes)
31
26
  modded_attributes = inject_defaults!(attributes)
32
27
  validate_attributes!(modded_attributes)
33
28
  writer_method_props = extract_writer_methods!(modded_attributes)
@@ -40,11 +35,8 @@ module Neo4j::Shared
40
35
  self.class.declared_properties.inject_defaults!(self, starting_props || {})
41
36
  end
42
37
 
43
- # Returning nil when we get ActiveAttr::UnknownAttributeError from ActiveAttr
44
38
  def read_attribute(name)
45
- super(name)
46
- rescue ActiveAttr::UnknownAttributeError
47
- nil
39
+ respond_to?(name) ? send(name) : nil
48
40
  end
49
41
  alias_method :[], :read_attribute
50
42
 
@@ -58,16 +50,6 @@ module Neo4j::Shared
58
50
  convert_and_assign_attributes(properties)
59
51
  end
60
52
 
61
- protected
62
-
63
- # This method is defined in ActiveModel.
64
- # When each node is loaded, it is called once in pursuit of 'sanitize_for_mass_assignment', which this gem does not implement.
65
- # In the course of doing that, it calls :attributes, which is quite expensive, so we return immediately.
66
- def attribute_method?(attr_name) #:nodoc:
67
- return false if attr_name == 'sanitize_for_mass_assignment'
68
- super(attr_name)
69
- end
70
-
71
53
  private
72
54
 
73
55
  # Changes attributes hash to remove relationship keys
@@ -123,7 +105,7 @@ module Neo4j::Shared
123
105
  def instantiate_object(field, values_with_empty_parameters)
124
106
  return nil if values_with_empty_parameters.all?(&:nil?)
125
107
  values = values_with_empty_parameters.collect { |v| v.nil? ? 1 : v }
126
- klass = field[:type]
108
+ klass = field.type
127
109
  klass ? klass.new(*values) : values
128
110
  end
129
111
 
@@ -162,54 +144,61 @@ module Neo4j::Shared
162
144
  # end
163
145
  def property(name, options = {})
164
146
  build_property(name, options) do |prop|
165
- attribute(name, prop.options)
147
+ attribute(prop)
166
148
  end
167
149
  end
168
150
 
169
151
  # @param [Symbol] name The property name
170
- # @param [ActiveAttr::AttributeDefinition] active_attr A cloned AttributeDefinition to reuse
152
+ # @param [Neo4j::Shared::AttributeDefinition] attr_def A cloned AttributeDefinition to reuse
171
153
  # @param [Hash] options An options hash to use in the new property definition
172
- def inherit_property(name, active_attr, options = {})
173
- build_property(name, options) do |prop|
174
- attributes[prop.name.to_s] = active_attr
154
+ def inherit_property(name, attr_def, options = {})
155
+ build_property(name, options) do |prop_name|
156
+ attributes[prop_name] = attr_def
175
157
  end
176
158
  end
177
159
 
178
160
  def build_property(name, options)
179
- prop = DeclaredProperty.new(name, options)
180
- prop.register
181
- declared_properties.register(prop)
182
- yield prop
183
- constraint_or_index(name, options)
161
+ DeclaredProperty.new(name, options).tap do |prop|
162
+ prop.register
163
+ declared_properties.register(prop)
164
+ yield name
165
+ constraint_or_index(name, options)
166
+ end
184
167
  end
185
168
 
186
169
  def undef_property(name)
170
+ undef_constraint_or_index(name)
187
171
  declared_properties.unregister(name)
188
172
  attribute_methods(name).each { |method| undef_method(method) }
189
- undef_constraint_or_index(name)
190
173
  end
191
174
 
192
175
  def declared_properties
193
176
  @_declared_properties ||= DeclaredProperties.new(self)
194
177
  end
195
178
 
196
- def attribute!(name, options = {})
197
- super(name, options)
198
- define_method("#{name}=") do |value|
199
- typecast_value = typecast_attribute(_attribute_typecaster(name), value)
200
- send("#{name}_will_change!") unless typecast_value == read_attribute(name)
201
- super(value)
202
- end
203
- end
204
-
205
179
  # @return [Hash] A frozen hash of all model properties with nil values. It is used during node loading and prevents
206
180
  # an extra call to a slow dependency method.
207
181
  def attributes_nil_hash
208
182
  declared_properties.attributes_nil_hash
209
183
  end
210
184
 
185
+ def extract_association_attributes!(props)
186
+ props
187
+ end
188
+
211
189
  private
212
190
 
191
+ def attribute!(name)
192
+ remove_instance_variable('@attribute_methods_generated') if instance_variable_defined?('@attribute_methods_generated')
193
+ define_attribute_methods([name]) unless attribute_names.include?(name)
194
+ attributes[name.to_s] = declared_properties[name]
195
+ define_method("#{name}=") do |value|
196
+ typecast_value = typecast_attribute(_attribute_typecaster(name), value)
197
+ send("#{name}_will_change!") unless typecast_value == read_attribute(name)
198
+ super(value)
199
+ end
200
+ end
201
+
213
202
  def constraint_or_index(name, options)
214
203
  # either constraint or index, do not set both
215
204
  if options[:constraint]
@@ -220,6 +209,13 @@ module Neo4j::Shared
220
209
  index(name) if options[:index] == :exact
221
210
  end
222
211
  end
212
+
213
+ def undef_constraint_or_index(name)
214
+ prop = declared_properties[name]
215
+ return unless prop.index_or_constraint?
216
+ type = prop.constraint? ? :constraint : :index
217
+ send(:"drop_#{type}", name)
218
+ end
223
219
  end
224
220
  end
225
221
  end