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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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