neo4j 3.0.0.alpha.9 → 3.0.0.alpha.10

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,183 +1,10 @@
1
1
  module Neo4j::ActiveNode
2
2
  module Property
3
3
  extend ActiveSupport::Concern
4
-
5
- include ActiveAttr::Attributes
6
- include ActiveAttr::MassAssignment
7
- include ActiveAttr::TypecastedAttributes
8
- include ActiveAttr::AttributeDefaults
9
- include ActiveAttr::QueryAttributes
10
- include ActiveModel::Dirty
11
-
12
- class UndefinedPropertyError < RuntimeError; end
13
- class MultiparameterAssignmentError < StandardError; end
14
-
15
- def initialize(attributes={}, options={})
16
- attributes = process_attributes(attributes)
17
-
18
- writer_method_props = extract_writer_methods!(attributes)
19
- validate_attributes!(attributes)
20
- writer_method_props.each do |key, value|
21
- self.send("#{key}=", value)
22
- end
23
-
24
- super(attributes, options)
25
- end
26
-
27
- # Returning nil when we get ActiveAttr::UnknownAttributeError from ActiveAttr
28
- def read_attribute(name)
29
- super(name)
30
- rescue ActiveAttr::UnknownAttributeError
31
- nil
32
- end
33
- alias_method :[], :read_attribute
34
-
35
- def default_properties=(properties)
36
- keys = self.class.default_properties.keys
37
- @default_properties = properties.reject{|key| !keys.include?(key)}
38
- end
39
-
40
- def default_property(key)
41
- keys = self.class.default_properties.keys
42
- keys.include?(key.to_sym) ? default_properties[key.to_sym] : nil
43
- end
44
-
45
- def default_properties
46
- @default_properties ||= {}
47
- # keys = self.class.default_properties.keys
48
- # _persisted_node.props.reject{|key| !keys.include?(key)}
49
- end
50
-
51
-
52
- private
53
-
54
- # Changes attributes hash to remove relationship keys
55
- # Raises an error if there are any keys left which haven't been defined as properties on the model
56
- def validate_attributes!(attributes)
57
- invalid_properties = attributes.keys.map(&:to_s) - self.attributes.keys
58
- raise UndefinedPropertyError, "Undefined properties: #{invalid_properties.join(',')}" if invalid_properties.size > 0
59
- end
60
-
61
- def extract_writer_methods!(attributes)
62
- attributes.keys.inject({}) do |writer_method_props, key|
63
- writer_method_props[key] = attributes.delete(key) if self.respond_to?("#{key}=")
64
-
65
- writer_method_props
66
- end
67
- end
68
-
69
- # Gives support for Rails date_select, datetime_select, time_select helpers.
70
- def process_attributes(attributes = nil)
71
- multi_parameter_attributes = {}
72
- new_attributes = {}
73
- attributes.each_pair do |key, value|
74
- if key =~ /\A([^\(]+)\((\d+)([if])\)$/
75
- found_key, index = $1, $2.to_i
76
- (multi_parameter_attributes[found_key] ||= {})[index] = value.empty? ? nil : value.send("to_#{$3}")
77
- else
78
- new_attributes[key] = value
79
- end
80
- end
81
-
82
- multi_parameter_attributes.empty? ? new_attributes : process_multiparameter_attributes(multi_parameter_attributes, new_attributes)
83
- end
84
-
85
- def process_multiparameter_attributes(multi_parameter_attributes, new_attributes)
86
- multi_parameter_attributes.each_pair do |key, values|
87
- begin
88
- values = (values.keys.min..values.keys.max).map { |i| values[i] }
89
- field = self.class.attributes[key.to_sym]
90
- new_attributes[key] = instantiate_object(field, values)
91
- rescue => e
92
- raise MultiparameterAssignmentError, "error on assignment #{values.inspect} to #{key}"
93
- end
94
- end
95
- new_attributes
96
- end
97
-
98
- def instantiate_object(field, values_with_empty_parameters)
99
- return nil if values_with_empty_parameters.all? { |v| v.nil? }
100
- values = values_with_empty_parameters.collect { |v| v.nil? ? 1 : v }
101
- klass = field[:type]
102
- if klass
103
- klass.new(*values)
104
- else
105
- values
106
- end
107
- end
4
+ include Neo4j::Shared::Property
108
5
 
109
6
  module ClassMethods
110
7
 
111
- # Defines a property on the class
112
- #
113
- # See active_attr gem for allowed options, e.g which type
114
- # Notice, in Neo4j you don't have to declare properties before using them, see the neo4j-core api.
115
- #
116
- # @example Without type
117
- # class Person
118
- # # declare a property which can have any value
119
- # property :name
120
- # end
121
- #
122
- # @example With type and a default value
123
- # class Person
124
- # # declare a property which can have any value
125
- # property :score, type: Integer, default: 0
126
- # end
127
- #
128
- # @example With an index
129
- # class Person
130
- # # declare a property which can have any value
131
- # property :name, index: :exact
132
- # end
133
- #
134
- # @example With a constraint
135
- # class Person
136
- # # declare a property which can have any value
137
- # property :name, constraint: :unique
138
- # end
139
- def property(name, options={})
140
- magic_properties(name, options)
141
- attribute(name, options)
142
-
143
- # either constraint or index, do not set both
144
- if options[:constraint]
145
- raise "unknown constraint type #{options[:constraint]}, only :unique supported" if options[:constraint] != :unique
146
- constraint(name, type: :unique)
147
- elsif options[:index]
148
- raise "unknown index type #{options[:index]}, only :exact supported" if options[:index] != :exact
149
- index(name, options) if options[:index] == :exact
150
- end
151
- end
152
-
153
- def default_property(name, &block)
154
- default_properties[name] = block
155
- end
156
-
157
- # @return [Hash<Symbol,Proc>]
158
- def default_properties
159
- @default_property ||= {}
160
- end
161
-
162
- def default_property_values(instance)
163
- default_properties.inject({}) do |result,pair|
164
- result.tap{|obj| obj[pair[0]] = pair[1].call(instance)}
165
- end
166
- end
167
-
168
- def attribute!(name, options={})
169
- super(name, options)
170
- define_method("#{name}=") do |value|
171
- typecast_value = typecast_attribute(typecaster_for(self.class._attribute_type(name)), value)
172
- send("#{name}_will_change!") unless typecast_value == read_attribute(name)
173
- super(value)
174
- end
175
- end
176
-
177
- def cached_class?
178
- !!Neo4j::Config[:cache_class_names]
179
- end
180
-
181
8
  # Extracts keys from attributes hash which are relationships of the model
182
9
  # TODO: Validate separately that relationships are getting the right values? Perhaps also store the values and persist relationships on save?
183
10
  def extract_association_attributes!(attributes)
@@ -187,26 +14,6 @@ module Neo4j::ActiveNode
187
14
  association_props
188
15
  end
189
16
  end
190
-
191
- private
192
-
193
- # Tweaks properties
194
- def magic_properties(name, options)
195
- set_stamp_type(name, options)
196
- set_time_as_datetime(options)
197
- end
198
-
199
- def set_stamp_type(name, options)
200
- options[:type] = DateTime if (name.to_sym == :created_at || name.to_sym == :updated_at)
201
- end
202
-
203
- # ActiveAttr does not handle "Time", Rails and Neo4j.rb 2.3 did
204
- # Convert it to DateTime in the interest of consistency
205
- def set_time_as_datetime(options)
206
- options[:type] = DateTime if options[:type] == Time
207
- end
208
-
209
17
  end
210
18
  end
211
-
212
19
  end
@@ -4,8 +4,8 @@ module Neo4j::ActiveNode
4
4
  def_delegators :_rels_delegator, :rel?, :rel, :rels, :node, :nodes, :create_rel
5
5
 
6
6
  def _rels_delegator
7
- raise "Can't access relationship on a non persisted node" unless _persisted_node
8
- _persisted_node
7
+ raise "Can't access relationship on a non persisted node" unless _persisted_obj
8
+ _persisted_obj
9
9
  end
10
10
  end
11
11
  end
@@ -3,33 +3,8 @@ module Neo4j
3
3
  # This mixin replace the original save method and performs validation before the save.
4
4
  module Validations
5
5
  extend ActiveSupport::Concern
6
+ include Neo4j::Shared::Validations
6
7
 
7
- include ActiveModel::Validations
8
-
9
- module ClassMethods
10
- def validates_uniqueness_of(*attr_names)
11
- validates_with UniquenessValidator, _merge_attributes(attr_names)
12
- end
13
- end
14
-
15
- # Implements the ActiveModel::Validation hook method.
16
- # @see http://rubydoc.info/docs/rails/ActiveModel/Validations:read_attribute_for_validation
17
- def read_attribute_for_validation(key)
18
- respond_to?(key) ? send(key) : self[key]
19
- end
20
-
21
- # The validation process on save can be skipped by passing false. The regular Model#save method is
22
- # replaced with this when the validations module is mixed in, which it is by default.
23
- # @param [Hash] options the options to create a message with.
24
- # @option options [true, false] :validate if false no validation will take place
25
- # @return [Boolean] true if it saved it successfully
26
- def save(options={})
27
- result = perform_validations(options) ? super : false
28
- if !result
29
- Neo4j::Transaction.current.failure if Neo4j::Transaction.current
30
- end
31
- result
32
- end
33
8
 
34
9
  # @return [Boolean] true if valid
35
10
  def valid?(context = nil)
@@ -38,6 +13,13 @@ module Neo4j
38
13
  errors.empty?
39
14
  end
40
15
 
16
+ module ClassMethods
17
+ def validates_uniqueness_of(*attr_names)
18
+ validates_with UniquenessValidator, _merge_attributes(attr_names)
19
+ end
20
+ end
21
+
22
+
41
23
  class UniquenessValidator < ::ActiveModel::EachValidator
42
24
  def initialize(options)
43
25
  super(options.reverse_merge(:case_sensitive => true))
@@ -57,8 +39,7 @@ module Neo4j
57
39
 
58
40
  # prevent that same object is returned
59
41
  # TODO: add negative condtion to not return current record
60
- found = record.class.where(conditions).to_a
61
- found.delete(record)
42
+ found = record.class.where(conditions).to_a.select{|f| f.neo_id != record.neo_id}
62
43
 
63
44
  if found.count > 0
64
45
  record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
@@ -76,19 +57,6 @@ module Neo4j
76
57
  end
77
58
  end
78
59
 
79
- private
80
- def perform_validations(options={})
81
- perform_validation = case options
82
- when Hash
83
- options[:validate] != false
84
- end
85
-
86
- if perform_validation
87
- valid?(options.is_a?(Hash) ? options[:context] : nil)
88
- else
89
- true
90
- end
91
- end
92
60
  end
93
61
  end
94
62
  end
@@ -0,0 +1,34 @@
1
+ module Neo4j
2
+
3
+ # Makes Neo4j Relationships more or less act like ActiveRecord objects.
4
+ module ActiveRel
5
+ extend ActiveSupport::Concern
6
+
7
+ include Neo4j::Shared
8
+ include Neo4j::ActiveRel::Initialize
9
+ include Neo4j::ActiveRel::Property
10
+ include Neo4j::ActiveRel::Persistence
11
+ include Neo4j::ActiveRel::Validations
12
+ include Neo4j::ActiveRel::Callbacks
13
+ include Neo4j::ActiveRel::Query
14
+
15
+ class FrozenRelError < StandardError; end
16
+
17
+ def initialize(*args)
18
+ load_nodes
19
+ super
20
+ end
21
+
22
+ def neo4j_obj
23
+ _persisted_obj || raise("Tried to access native neo4j object on a non persisted object")
24
+ end
25
+
26
+ included do
27
+ def self.inherited(other)
28
+ super
29
+ end
30
+
31
+ cache_class unless cached_class?
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,13 @@
1
+ module Neo4j
2
+ module ActiveRel
3
+ module Callbacks #:nodoc:
4
+ extend ActiveSupport::Concern
5
+ include Neo4j::Shared::Callbacks
6
+
7
+ def save(*args)
8
+ raise Neo4j::ActiveRel::Persistence::RelInvalidError.new(self) unless self.persisted? || (from_node.respond_to?(:neo_id) && to_node.respond_to?(:neo_id))
9
+ super(*args)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,30 @@
1
+ module Neo4j::ActiveRel
2
+ module Initialize
3
+ extend ActiveSupport::Concern
4
+ include Neo4j::TypeConverters
5
+
6
+ attr_reader :_persisted_obj
7
+
8
+ # called when loading the rel from the database
9
+ # @param [Hash] properties properties of this relationship
10
+ # @param [Neo4j::Relationship] start_node the starting node in the relationship.
11
+ # @param [Neo4j::Relationship] end_node the ending node in the relationship
12
+ # @param [String] type the relationship type
13
+ def init_on_load(persisted_rel, start_node_id, end_node_id, type)
14
+ @_persisted_obj = persisted_rel
15
+ @rel_type = type
16
+ changed_attributes && changed_attributes.clear
17
+ @attributes = attributes.merge(persisted_rel.props.stringify_keys)
18
+ load_nodes(start_node_id, end_node_id)
19
+ self.default_properties = persisted_rel.props
20
+ @attributes = convert_properties_to :ruby, @attributes
21
+ end
22
+
23
+ # Implements the Neo4j::Node#wrapper and Neo4j::Relationship#wrapper method
24
+ # so that we don't have to care if the node is wrapped or not.
25
+ # @return self
26
+ def wrapper
27
+ self
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,80 @@
1
+ module Neo4j::ActiveRel
2
+ module Persistence
3
+ extend ActiveSupport::Concern
4
+ include Neo4j::Shared::Persistence
5
+
6
+ class RelInvalidError < RuntimeError
7
+ attr_reader :record
8
+
9
+ def initialize(record)
10
+ @record = record
11
+ super(@record.errors.full_messages.join(", "))
12
+ end
13
+ end
14
+
15
+ class ModelClassInvalidError < RuntimeError; end
16
+
17
+ def save(*)
18
+ update_magic_properties
19
+ create_or_update
20
+ end
21
+
22
+ def save!(*args)
23
+ unless save(*args)
24
+ raise RelInvalidError.new(self)
25
+ end
26
+ end
27
+
28
+ def create_model(*)
29
+ confirm_node_classes
30
+ create_magic_properties
31
+ set_timestamps
32
+ properties = convert_properties_to :db, props
33
+ rel = _create_rel(properties)
34
+ init_on_load(rel, to_node, from_node, @rel_type)
35
+ true
36
+ end
37
+
38
+ module ClassMethods
39
+
40
+ # Creates a new relationship between objects
41
+ # @param [Hash] props the properties the new relationship should have
42
+ def create(props = {})
43
+ relationship_props = extract_association_attributes!(props) || {}
44
+ new(props).tap do |obj|
45
+ relationship_props.each do |prop, value|
46
+ obj.send("#{prop}=", value)
47
+ end
48
+ obj.save
49
+ end
50
+ end
51
+
52
+ # Same as #create, but raises an error if there is a problem during save.
53
+ def create!(*args)
54
+ unless create(*args)
55
+ raise RelInvalidError.new(self)
56
+ end
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def confirm_node_classes
63
+ [from_node, to_node].each do |node|
64
+ check = from_node == node ? :_from_class : :_to_class
65
+ next if self.class.send(check) == :any
66
+ unless self.class.send(check) == node.class
67
+ raise ModelClassInvalidError, "Node class was #{node.class}, expected #{self.class.send(check)}"
68
+ end
69
+ end
70
+ end
71
+
72
+ def _create_rel(*args)
73
+ session = self.class.neo4j_session
74
+ props = self.class.default_property_values(self)
75
+ props.merge!(args[0]) if args[0].is_a?(Hash)
76
+ set_classname(props)
77
+ from_node.create_rel(type, to_node, props)
78
+ end
79
+ end
80
+ end