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.
@@ -0,0 +1,64 @@
1
+
2
+ module Neo4j::ActiveRel
3
+ module Property
4
+ extend ActiveSupport::Concern
5
+ include Neo4j::Shared::Property
6
+
7
+ %w[to_node from_node].each do |direction|
8
+ define_method("#{direction}") { instance_variable_get("@#{direction}") }
9
+ define_method("#{direction}=") do |argument|
10
+ raise FrozenRelError, "Relationship start/end nodes cannot be changed once persisted" if self.persisted?
11
+ instance_variable_set("@#{direction}", argument)
12
+ end
13
+ end
14
+
15
+ alias_method :start_node, :from_node
16
+ alias_method :end_node, :to_node
17
+
18
+ def type
19
+ self.class._type
20
+ end
21
+
22
+ module ClassMethods
23
+
24
+ # Extracts keys from attributes hash which are relationships of the model
25
+ # TODO: Validate separately that relationships are getting the right values? Perhaps also store the values and persist relationships on save?
26
+ def extract_association_attributes!(attributes)
27
+ attributes.keys.inject({}) do |relationship_props, key|
28
+ relationship_props[key] = attributes.delete(key) if key == :from_node || key == :to_node
29
+
30
+ relationship_props
31
+ end
32
+ end
33
+
34
+ %w[to_class from_class].each do |direction|
35
+ define_method("#{direction}") { |argument| instance_variable_set("@#{direction}", argument) }
36
+ define_method("_#{direction}") { instance_variable_get "@#{direction}" }
37
+ end
38
+
39
+ alias_method :start_class, :from_class
40
+ alias_method :end_class, :to_class
41
+
42
+ def type(type = nil)
43
+ @rel_type = type
44
+ end
45
+
46
+ def _type
47
+ @rel_type
48
+ end
49
+
50
+ def load_entity(id)
51
+ Neo4j::Node.load(id)
52
+ end
53
+
54
+ end
55
+
56
+ private
57
+
58
+ def load_nodes(start_node=nil, end_node=nil)
59
+ @from_node = RelatedNode.new(end_node)
60
+ @to_node = RelatedNode.new(start_node)
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,66 @@
1
+ module Neo4j::ActiveRel
2
+ module Query
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ include Enumerable
7
+
8
+ # Returns the object with the specified neo4j id.
9
+ # @param [String,Fixnum] id of node to find
10
+ # @param [Neo4j::Session] session optional
11
+ def find(id, session = self.neo4j_session)
12
+ raise "Unknown argument #{id.class} in find method (expected String or Fixnum)" if not [String, Fixnum].include?(id.class)
13
+ find_by_id(id, session)
14
+ end
15
+
16
+ def find_by_id(key, session = Neo4j::Session.current!)
17
+ Neo4j::Relationship.load(key.to_i, session)
18
+ end
19
+
20
+ # TODO make this not awful
21
+ def where(args={})
22
+ @query = if self._from_class == :any
23
+ Neo4j::Session.query("MATCH n1-[r1:`#{self._type}`]->(#{cypher_node_string(:inbound)}) WHERE #{where_string(args)} RETURN r1")
24
+ else
25
+ self._from_class.query_as(:n1).match("(#{cypher_node_string(:outbound)})-[r1:`#{self._type}`]->(#{cypher_node_string(:inbound)})").where(Hash["r1" => args])
26
+ end
27
+ return self
28
+ end
29
+
30
+ def each
31
+ if self._from_class == :any
32
+ @query.map(&:r1)
33
+ else
34
+ @query.pluck(:r1)
35
+ end.each {|r| yield r }
36
+ end
37
+
38
+ def first
39
+ if self._from_class == :any
40
+ @query.map(&:r1)
41
+ else
42
+ @query.pluck(:r1)
43
+ end.first
44
+ end
45
+
46
+ def cypher_node_string(dir)
47
+ case dir
48
+ when :outbound
49
+ node_identifier, dir_class = 'n1', self._from_class
50
+ when :inbound
51
+ node_identifier, dir_class = 'n2', self._to_class
52
+ end
53
+ dir_class == :any ? node_identifier : "#{node_identifier}:`#{dir_class.name}`"
54
+ end
55
+
56
+ private
57
+
58
+ def where_string(args)
59
+ args.map do |k, v|
60
+ v.is_a?(Integer) ? "r1.#{k} = #{v}" : "r1.#{k} = '#{v}'"
61
+ end.join(', ')
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,18 @@
1
+ class Neo4j::Relationship
2
+
3
+ module Wrapper
4
+ def wrapper
5
+ props.symbolize_keys!
6
+ return self unless props.is_a?(Hash) && props.has_key?(Neo4j::Config.class_name_property)
7
+ begin
8
+ found_class = props[Neo4j::Config.class_name_property].constantize
9
+ rescue NameError
10
+ return self
11
+ end
12
+ wrapped_rel = found_class.new
13
+ wrapped_rel.init_on_load(self, self._start_node_id, self._end_node_id, self.rel_type)
14
+ wrapped_rel
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,42 @@
1
+ module Neo4j::ActiveRel
2
+ # A container for ActiveRel's :inbound and :outbound methods. It provides lazy loading of nodes.
3
+ class RelatedNode
4
+
5
+ class InvalidParameterError < StandardError
6
+ def message
7
+ 'RelatedNode must be initialized with either a node ID or node'
8
+ end
9
+ end
10
+
11
+ def initialize(node = nil)
12
+ @node = valid_node_param?(node) ? node : (raise InvalidParameterError.new(self))
13
+ end
14
+
15
+ def == (obj)
16
+ loaded if @node.is_a?(Fixnum)
17
+ @node == obj
18
+ end
19
+
20
+ def loaded
21
+ @node = @node.respond_to?(:neo_id) ? @node : Neo4j::Node.load(@node)
22
+ end
23
+
24
+ def loaded?
25
+ @node.respond_to?(:neo_id)
26
+ end
27
+
28
+ def method_missing(*args, &block)
29
+ loaded.send(*args, &block)
30
+ end
31
+
32
+ def class
33
+ loaded.send(:class)
34
+ end
35
+
36
+ private
37
+
38
+ def valid_node_param?(node)
39
+ node.nil? || node.is_a?(Integer) || node.respond_to?(:neo_id)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,9 @@
1
+ module Neo4j
2
+ module ActiveRel
3
+ module Validations
4
+ extend ActiveSupport::Concern
5
+ include Neo4j::Shared::Validations
6
+
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,34 @@
1
+ module Neo4j
2
+ module Shared
3
+ extend ActiveSupport::Concern
4
+ extend ActiveModel::Naming
5
+
6
+ include ActiveModel::Conversion
7
+ include ActiveModel::Serializers::Xml
8
+ include ActiveModel::Serializers::JSON
9
+
10
+ include Neo4j::Shared::Identity
11
+
12
+ module ClassMethods
13
+ def neo4j_session_name (name)
14
+ @neo4j_session_name = name
15
+ end
16
+
17
+ def neo4j_session
18
+ if @neo4j_session_name
19
+ Neo4j::Session.named(@neo4j_session_name) || raise("#{self.name} is configured to use a neo4j session named #{@neo4j_session_name}, but no such session is registered with Neo4j::Session")
20
+ else
21
+ Neo4j::Session.current
22
+ end
23
+ end
24
+ end
25
+
26
+ included do
27
+ self.include_root_in_json = true
28
+
29
+ def self.i18n_scope
30
+ :neo4j
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,40 @@
1
+ module Neo4j
2
+ module Shared
3
+ module Callbacks #:nodoc:
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ include ActiveModel::Callbacks
8
+ end
9
+
10
+ included do
11
+ include ActiveModel::Validations::Callbacks
12
+ define_model_callbacks :initialize, :find, :only => :after
13
+ define_model_callbacks :save, :create, :update, :destroy
14
+ end
15
+
16
+ def destroy #:nodoc:
17
+ run_callbacks(:destroy) { super }
18
+ end
19
+
20
+ def touch(*) #:nodoc:
21
+ run_callbacks(:touch) { super }
22
+ end
23
+
24
+ private
25
+
26
+ def create_or_update #:nodoc:
27
+ run_callbacks(:save) { super }
28
+ end
29
+
30
+ def create_model #:nodoc:
31
+ run_callbacks(:create) { super }
32
+ end
33
+
34
+ def update_model(*) #:nodoc:
35
+ run_callbacks(:update) { super }
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -1,4 +1,4 @@
1
- module Neo4j::ActiveNode
1
+ module Neo4j::Shared
2
2
  module Identity
3
3
  def ==(o)
4
4
  o.class == self.class && o.id == id
@@ -13,12 +13,16 @@ module Neo4j::ActiveNode
13
13
 
14
14
  # @return [Fixnum, nil] the neo4j id of the node if persisted or nil
15
15
  def neo_id
16
- _persisted_node ? _persisted_node.neo_id : nil
16
+ _persisted_obj ? _persisted_obj.neo_id : nil
17
17
  end
18
18
 
19
19
  def id
20
20
  id = neo_id
21
21
  id.is_a?(Integer) ? id : nil
22
22
  end
23
+
24
+ def hash
25
+ id.hash
26
+ end
23
27
  end
24
28
  end
@@ -0,0 +1,214 @@
1
+ module Neo4j::Shared
2
+ module Persistence
3
+
4
+ class RecordInvalidError < RuntimeError
5
+ attr_reader :record
6
+
7
+ def initialize(record)
8
+ @record = record
9
+ super(@record.errors.full_messages.join(", "))
10
+ end
11
+ end
12
+
13
+ extend ActiveSupport::Concern
14
+ include Neo4j::TypeConverters
15
+
16
+ # Saves the model.
17
+ #
18
+ # If the model is new a record gets created in the database, otherwise the existing record gets updated.
19
+ # If perform_validation is true validations run.
20
+ # If any of them fail the action is cancelled and save returns false. If the flag is false validations are bypassed altogether. See ActiveRecord::Validations for more information.
21
+ # There’s a series of callbacks associated with save. If any of the before_* callbacks return false the action is cancelled and save returns false.
22
+ def save(*)
23
+ update_magic_properties
24
+ create_or_update
25
+ end
26
+
27
+ # Persist the object to the database. Validations and Callbacks are included
28
+ # by default but validation can be disabled by passing :validate => false
29
+ # to #save! Creates a new transaction.
30
+ #
31
+ # @raise a RecordInvalidError if there is a problem during save.
32
+ # @param (see Neo4j::Rails::Validations#save)
33
+ # @return nil
34
+ # @see #save
35
+ # @see Neo4j::Rails::Validations Neo4j::Rails::Validations - for the :validate parameter
36
+ # @see Neo4j::Rails::Callbacks Neo4j::Rails::Callbacks - for callbacks
37
+ def save!(*args)
38
+ unless save(*args)
39
+ raise RecordInvalidError.new(self)
40
+ end
41
+ end
42
+
43
+ def update_model
44
+ if changed_attributes && !changed_attributes.empty?
45
+ changed_props = attributes.select{|k,v| changed_attributes.include?(k)}
46
+ changed_props = convert_properties_to :db, changed_props
47
+ _persisted_obj.update_props(changed_props)
48
+ changed_attributes.clear
49
+ end
50
+ end
51
+
52
+
53
+ # Convenience method to set attribute and #save at the same time
54
+ # @param [Symbol, String] attribute of the attribute to update
55
+ # @param [Object] value to set
56
+ def update_attribute(attribute, value)
57
+ send("#{attribute}=", value)
58
+ self.save
59
+ end
60
+
61
+ # Convenience method to set attribute and #save! at the same time
62
+ # @param [Symbol, String] attribute of the attribute to update
63
+ # @param [Object] value to set
64
+ def update_attribute!(attribute, value)
65
+ send("#{attribute}=", value)
66
+ self.save!
67
+ end
68
+
69
+ # Convenience method to set multiple attributes and #save at the same time
70
+ # @param [Hash] attributes of names and values of attributes to set
71
+ def update_attributes(attributes)
72
+ assign_attributes(attributes)
73
+ self.save
74
+ end
75
+
76
+ def create_or_update
77
+ # since the same model can be created or updated twice from a relationship we have to have this guard
78
+ @_create_or_updating = true
79
+ result = persisted? ? update_model : create_model
80
+ unless result != false
81
+ Neo4j::Transaction.current.fail if Neo4j::Transaction.current
82
+ false
83
+ else
84
+ true
85
+ end
86
+ rescue => e
87
+ Neo4j::Transaction.current.fail if Neo4j::Transaction.current
88
+ raise e
89
+ ensure
90
+ @_create_or_updating = nil
91
+ end
92
+
93
+ # Returns +true+ if the record is persisted, i.e. it’s not a new record and it was not destroyed
94
+ def persisted?
95
+ !new_record? && !destroyed?
96
+ end
97
+
98
+ # Returns +true+ if the record hasn't been saved to Neo4j yet.
99
+ def new_record?
100
+ !_persisted_obj
101
+ end
102
+
103
+ alias :new? :new_record?
104
+
105
+ def destroy
106
+ _persisted_obj && _persisted_obj.del
107
+ @_deleted = true
108
+ end
109
+
110
+ def exist?
111
+ _persisted_obj && _persisted_obj.exist?
112
+ end
113
+
114
+ # Returns +true+ if the object was destroyed.
115
+ def destroyed?
116
+ @_deleted || (!new_record? && !exist?)
117
+ end
118
+
119
+
120
+ # @return [Hash] all defined and none nil properties
121
+ def props
122
+ attributes.reject{|k,v| v.nil?}.symbolize_keys
123
+ end
124
+
125
+ # @return true if the attributes hash has been frozen
126
+ def frozen?
127
+ freeze_if_deleted
128
+ @attributes.frozen?
129
+ end
130
+
131
+ def freeze
132
+ @attributes.freeze
133
+ self
134
+ end
135
+
136
+ def freeze_if_deleted
137
+ unless new_record?
138
+ # TODO - Neo4j::IdentityMap.remove_node_by_id(neo_id)
139
+ unless self.class.load_entity(neo_id)
140
+ @_deleted = true
141
+ freeze
142
+ end
143
+ end
144
+ end
145
+
146
+ def reload
147
+ return self if new_record?
148
+ changed_attributes && changed_attributes.clear
149
+ unless reload_from_database
150
+ @_deleted = true
151
+ freeze
152
+ end
153
+ self
154
+ end
155
+
156
+ def reload_from_database
157
+ # TODO - Neo4j::IdentityMap.remove_node_by_id(neo_id)
158
+ if reloaded = self.class.load_entity(neo_id)
159
+ send(:attributes=, reloaded.attributes)
160
+ end
161
+ reloaded
162
+ end
163
+
164
+ # Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved.
165
+ # If saving fails because the resource is invalid then false will be returned.
166
+ def update(attributes)
167
+ self.attributes = attributes
168
+ save
169
+ end
170
+ alias_method :update_attributes, :update
171
+
172
+ # Same as {#update_attributes}, but raises an exception if saving fails.
173
+ def update!(attributes)
174
+ self.attributes = attributes
175
+ save!
176
+ end
177
+ alias_method :update_attributes!, :update!
178
+
179
+ def cache_key
180
+ if self.new_record?
181
+ "#{self.class.model_name.cache_key}/new"
182
+ elsif self.respond_to?(:updated_at) && !self.updated_at.blank?
183
+ "#{self.class.model_name.cache_key}/#{neo_id}-#{self.updated_at.utc.to_s(:number)}"
184
+ else
185
+ "#{self.class.model_name.cache_key}/#{neo_id}"
186
+ end
187
+ end
188
+
189
+ private
190
+
191
+ def create_magic_properties
192
+ end
193
+
194
+ def update_magic_properties
195
+ self.updated_at = DateTime.now if respond_to?(:updated_at=) && changed?
196
+ end
197
+
198
+ def set_classname(props)
199
+ props[:_classname] = self.class.name if self.class.cached_class?
200
+ end
201
+
202
+ # def assign_attributes(attributes)
203
+ # attributes.each do |attribute, value|
204
+ # send("#{attribute}=", value)
205
+ # end
206
+ # end
207
+
208
+ def set_timestamps
209
+ self.created_at = DateTime.now if respond_to?(:created_at=)
210
+ self.updated_at = self.created_at if respond_to?(:updated_at=)
211
+ end
212
+
213
+ end
214
+ end