neo4j 1.0.0.beta.15 → 1.0.0.beta.16

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,6 +18,12 @@ module Neo4j
18
18
 
19
19
  def shutdown
20
20
  if @running
21
+ # since we might keep a reference to indexes we must clear them so
22
+ # that we can start neo4j with a fresh new lucene indexes
23
+ Neo4j::Transaction.run do
24
+ Neo4j::Index::IndexerRegistry.clear_all_indexes
25
+ end
26
+
21
27
  @graph.unregister_transaction_event_handler(@event_handler)
22
28
  @event_handler.neo4j_shutdown(self)
23
29
  @graph.shutdown
@@ -54,7 +54,7 @@ module Neo4j
54
54
 
55
55
  def print
56
56
  puts "Listeners #{@listeners.size}"
57
- @listeners.each_key {|li| puts " Listener '#{li}'"}
57
+ @listeners.each {|li| puts " Listener '#{li}'"}
58
58
  end
59
59
 
60
60
  def neo4j_started(db)
@@ -9,8 +9,9 @@ module Neo4j
9
9
 
10
10
  def create_for(this_clazz, using_other_clazz, type)
11
11
  @@indexers ||= {}
12
- @@indexers[this_clazz.to_s] = @@indexers[using_other_clazz.to_s] || Indexer.new(this_clazz, type)
13
- @@indexers[this_clazz.to_s]
12
+ index = Indexer.new(this_clazz, type)
13
+ index.inherit_fields_from(@@indexers[using_other_clazz.to_s])
14
+ @@indexers[this_clazz.to_s] = index
14
15
  end
15
16
 
16
17
  def find_by_class(classname)
@@ -1,18 +1,29 @@
1
1
  module Neo4j
2
2
  module Index
3
3
  class Indexer #:nodoc:
4
- attr_reader :indexer_for
4
+ attr_reader :indexer_for, :field_types, :via_relationships
5
5
 
6
6
  def initialize(clazz, type)
7
7
  # part of the unique name of the index
8
- @indexer_for = clazz
8
+ @indexer_for = clazz
9
9
 
10
10
  # do we want to index nodes or relationships ?
11
- @type = type
11
+ @type = type
12
12
 
13
- @indexes = {} # key = type, value = java neo4j index
14
- @field_types = {} # key = field, value = type (e.g. :exact or :fulltext)
13
+ @indexes = {} # key = type, value = java neo4j index
14
+ @field_types = {} # key = field, value = type (e.g. :exact or :fulltext)
15
15
  @via_relationships = {} # key = field, value = relationship
16
+
17
+ # to enable subclass indexing to work properly, store a list of parent indexers and
18
+ # whenever an operation is performed on this one, perform it on all
19
+ @parent_indexers = []
20
+ end
21
+
22
+ def inherit_fields_from(parent_index)
23
+ return unless parent_index
24
+ @field_types.reverse_merge!(parent_index.field_types) if parent_index.respond_to?(:field_types)
25
+ @via_relationships.reverse_merge!(parent_index.via_relationships) if parent_index.respond_to?(:via_relationships)
26
+ @parent_indexers << parent_index
16
27
  end
17
28
 
18
29
  def to_s
@@ -87,12 +98,15 @@ module Neo4j
87
98
  dsl = @via_relationships[field]
88
99
  to_class = dsl.to_class
89
100
 
90
- dsl.all_relationships(node).each do |rel|
101
+ dsl._all_relationships(node).each do |rel|
91
102
  other = rel._start_node
92
- to_class._indexer.update_index_on(other, field, old_val, new_val)
103
+ to_class._indexer.update_single_index_on(other, field, old_val, new_val)
93
104
  end
94
105
  end
106
+ update_single_index_on(node, field, old_val, new_val)
107
+ end
95
108
 
109
+ def update_single_index_on(node, field, old_val, new_val)
96
110
  if @field_types.include?(field)
97
111
  rm_index(node, field, old_val) if old_val
98
112
  add_index(node, field, new_val) if new_val
@@ -113,11 +127,15 @@ module Neo4j
113
127
  end
114
128
 
115
129
  def add_index(entity, field, value)
130
+ return false unless @field_types.has_key?(field)
116
131
  index_for_field(field.to_s).add(entity, field, value)
132
+ @parent_indexers.each { |i| i.add_index(entity, field, value) }
117
133
  end
118
134
 
119
135
  def rm_index(entity, field, value)
136
+ return false unless @field_types.has_key?(field)
120
137
  index_for_field(field).remove(entity, field, value)
138
+ @parent_indexers.each { |i| i.rm_index(entity, field, value) }
121
139
  end
122
140
 
123
141
  def find(query, params = {})
@@ -33,10 +33,10 @@ module Neo4j::Mapping
33
33
  module_eval(%Q{
34
34
  def #{rel_type}_rels
35
35
  dsl = #{clazz}._decl_rels[:'#{rel_type.to_s}']
36
- dsl.all_relationships(self).wrapped
36
+ dsl.all_relationships(self)
37
37
  end}, __FILE__, __LINE__)
38
38
 
39
- _decl_rels[rel_type.to_sym] = Neo4j::Mapping::DeclRelationshipDsl.new(rel_type, false, params)
39
+ _decl_rels[rel_type.to_sym] = Neo4j::Mapping::DeclRelationshipDsl.new(rel_type, false, clazz, params)
40
40
  end
41
41
 
42
42
 
@@ -80,7 +80,7 @@ module Neo4j::Mapping
80
80
  dsl.single_relationship(self)
81
81
  end}, __FILE__, __LINE__)
82
82
 
83
- _decl_rels[rel_type.to_sym] = Neo4j::Mapping::DeclRelationshipDsl.new(rel_type, true, params)
83
+ _decl_rels[rel_type.to_sym] = Neo4j::Mapping::DeclRelationshipDsl.new(rel_type, true, clazz, params)
84
84
  end
85
85
 
86
86
  end
@@ -18,13 +18,22 @@ module Neo4j::Mapping
18
18
  trigger = props[:trigger].nil? ? [] : props[:trigger]
19
19
  @triggers[clazz][field] = trigger.respond_to?(:each) ? trigger : [trigger]
20
20
  end
21
-
22
- def trigger_other_rules(node, field)
23
- clazz = node[:_classname]
24
- rel_types = @triggers[clazz][field]
25
- rel_types.each do |rel_type|
26
- node.incoming(rel_type).each { |n| n.trigger_rules }
27
- end
21
+
22
+ def inherit(parent_class, subclass)
23
+ # copy all the rules
24
+ @rules[parent_class.to_s].each_pair do |field, filter|
25
+ subclass.rule field, &filter
26
+ end if @rules[parent_class.to_s]
27
+ end
28
+
29
+ def trigger_other_rules(node)
30
+ clazz = node[:_classname]
31
+ @rules[clazz].keys.each do |field|
32
+ rel_types = @triggers[clazz][field]
33
+ rel_types.each do |rel_type|
34
+ node.incoming(rel_type).each { |n| n.trigger_rules }
35
+ end
36
+ end
28
37
  end
29
38
 
30
39
  def fields_for(clazz)
@@ -61,12 +70,17 @@ module Neo4j::Mapping
61
70
  end
62
71
 
63
72
  def rule_for(clazz)
64
- Neo4j.ref_node._rel(:outgoing, clazz)._end_node
73
+ if Neo4j.ref_node.rel?(clazz)
74
+ Neo4j.ref_node._rel(:outgoing, clazz)._end_node
75
+ else
76
+ # this should be called if the rule node gets deleted
77
+ create_rule_node_for(clazz)
78
+ end
65
79
  end
66
80
 
67
81
 
68
82
  def on_relationship_created(rel, *)
69
- trigger_start_node = trigger?(rel._start_node)
83
+ trigger_start_node = trigger?(rel._start_node)
70
84
  trigger_end_node = trigger?(rel._end_node)
71
85
  # end or start node must be triggered by this event
72
86
  return unless trigger_start_node || trigger_end_node
@@ -75,28 +89,52 @@ module Neo4j::Mapping
75
89
 
76
90
 
77
91
  def on_property_changed(node, *)
78
- trigger_rules(node) if trigger?(node)
92
+ trigger_rules(node) if trigger?(node)
79
93
  end
80
94
 
81
95
  def trigger_rules(node)
82
- clazz = node[:_classname]
83
- return if @rules[clazz].nil?
96
+ trigger_rules_for_class(node, node[:_classname])
97
+ trigger_other_rules(node)
98
+ end
99
+
100
+ def trigger_rules_for_class(node, clazz)
101
+ return if @rules[clazz].nil?
84
102
 
85
- agg_node = rule_for(node[:_classname])
103
+ agg_node = rule_for(clazz)
86
104
  @rules[clazz].each_pair do |field, rule|
87
105
  if run_rule(rule, node)
88
106
  # is this node already included ?
89
- if !node.rel?(field)
107
+ unless connected?(field, agg_node, node)
90
108
  agg_node.outgoing(field) << node
91
- trigger_other_rules(node, field)
92
109
  end
93
110
  else
94
111
  # remove old ?
95
- node.rels(field).incoming.each { |x| x.del }
96
- trigger_other_rules(node, field)
112
+ break_connection(field, agg_node, node)
97
113
  end
98
114
  end
99
- end
115
+
116
+ # recursively add relationships for all the parent classes with rules that also pass for this node
117
+ if clazz = eval("#{clazz}.superclass")
118
+ trigger_rules_for_class(node, clazz.to_s)
119
+ end
120
+ end
121
+
122
+ # work out if two nodes are connected by a particular relationship
123
+ # uses the end_node to start with because it's more likely to have less relationships to go through
124
+ # (just the number of superclasses it has really)
125
+ def connected?(relationship, start_node, end_node)
126
+ end_node.incoming(relationship).each do |n|
127
+ return true if n == start_node
128
+ end
129
+ false
130
+ end
131
+
132
+ # sever a direct one-to-one relationship if it exists
133
+ def break_connection(relationship, start_node, end_node)
134
+ end_node.rels(relationship).incoming.each do |r|
135
+ return r.del if r.start_node == start_node
136
+ end
137
+ end
100
138
 
101
139
  def run_rule(rule, node)
102
140
  if rule.arity != 1
@@ -130,10 +168,10 @@ module Neo4j::Mapping
130
168
  # p1.young? # => true
131
169
  #
132
170
  def rule(name, props = {}, &block)
133
- singelton = class << self;
171
+ singelton = class << self;
134
172
  self;
135
173
  end
136
-
174
+
137
175
  # define class methods
138
176
  singelton.send(:define_method, name) do
139
177
  agg_node = Rules.rule_for(self)
@@ -145,16 +183,20 @@ module Neo4j::Mapping
145
183
  end
146
184
  end
147
185
  traversal
148
- end
186
+ end unless respond_to?(name)
149
187
 
150
188
  # define instance methods
151
189
  self.send(:define_method, "#{name}?") do
152
190
  instance_eval &block
153
- end
191
+ end
154
192
 
155
193
  Rules.add(self, name, props, &block)
156
194
  end
157
-
195
+
196
+ def inherit_rules_from(clazz)
197
+ Rules.inherit(clazz, self)
198
+ end
199
+
158
200
  # This is typically used for RSpecs to clean up rule nodes created by the #rule method.
159
201
  # It also remove the given class method.
160
202
  def delete_rules
@@ -34,37 +34,25 @@ module Neo4j::Mapping
34
34
  include Neo4j::ToJava
35
35
 
36
36
  attr_reader :to_type, :to_class, :cascade_delete_prop_name, :counter, :rel_id, :direction
37
- CASCADE_DELETE_PROP_NAMES = {:outgoing => :_cascade_delete_outgoing, :incoming => :_cascade_delete_incoming}
38
37
 
39
- def initialize(rel_id, has_one, params)
38
+ def initialize(rel_id, has_one, to_class, params)
40
39
  @direction = :outgoing
41
40
  @rel_id = rel_id
42
41
  @to_type = rel_id
43
42
  @has_one = has_one
44
43
  @namespace_type = rel_id
45
- @cascade_delete_prop_name = CASCADE_DELETE_PROP_NAMES[params[:cascade_delete]]
46
- @counter = params[:counter] == true
44
+ @to_class = to_class
47
45
  end
48
46
 
49
47
  def has_one?
50
48
  @has_one
51
49
  end
52
50
 
53
- # If a counter was specified in the dsl for counting number of nodes in this relationship.
54
- #
55
- def counter?
56
- @counter
57
- end
58
-
59
- # If cascade delete was specified for this relationship
60
- #
61
- def cascade_delete?
62
- !@cascade_delete_prop_name.nil?
63
- end
64
-
65
51
  def class_and_type_from_args(args) # :nodoc:
66
52
  if (args.size > 1)
67
53
  return args[0], args[1]
54
+ elsif (Symbol === args[0])
55
+ return @to_class, args[0]
68
56
  else
69
57
  return args[0], @rel_id
70
58
  end
@@ -73,7 +61,7 @@ module Neo4j::Mapping
73
61
 
74
62
  # The actual relationship type that this DSL will use
75
63
  def namespace_type
76
- @to_class.nil? ? @to_type.to_s : "#{@to_class.to_s}##{@to_type.to_s}"
64
+ @namespace_type
77
65
  end
78
66
 
79
67
  def each_node(node, direction, &block)
@@ -90,7 +78,7 @@ module Neo4j::Mapping
90
78
  end
91
79
 
92
80
  def incoming_dsl
93
- dsl = to_class._decl_rels[to_type]
81
+ dsl = @to_class._decl_rels[to_type]
94
82
  raise "Unspecified outgoing relationship '#{to_type}' for incoming relationship '#{rel_id}' on class #{to_class}" if dsl.nil?
95
83
  dsl
96
84
  end
@@ -106,13 +94,18 @@ module Neo4j::Mapping
106
94
  node._java_node.rel(dir, dsl.namespace_type)
107
95
  end
108
96
 
109
- def all_relationships(node)
97
+ def _all_relationships(node)
110
98
  dsl = incoming? ? incoming_dsl : self
111
99
  type = type_to_java(dsl.namespace_type)
112
100
  dir = dir_to_java(direction)
113
101
  node._java_node.getRelationships(type, dir)
114
102
  end
115
103
 
104
+ def all_relationships(node)
105
+ dsl = incoming? ? incoming_dsl : self
106
+ Neo4j::RelationshipTraverser.new(node._java_node, [dsl.namespace_type], direction)
107
+ end
108
+
116
109
  def create_relationship_to(node, other)
117
110
  dsl = incoming? ? incoming_dsl : self
118
111
 
@@ -156,6 +149,7 @@ module Neo4j::Mapping
156
149
  def to(*args)
157
150
  @direction = :outgoing
158
151
  @to_class, @to_type = class_and_type_from_args(args)
152
+ @namespace_type = "#{@to_class.to_s}##{@to_type.to_s}"
159
153
  self
160
154
  end
161
155
 
@@ -43,7 +43,7 @@ module Neo4j
43
43
 
44
44
  # Returns true if there are no node in this type of relationship
45
45
  def empty?
46
- first != nil
46
+ first == nil
47
47
  end
48
48
 
49
49
 
@@ -6,7 +6,7 @@ module Neo4j::Mapping
6
6
 
7
7
  def_delegators :@_java_node, :[]=, :[], :property?, :props, :attributes, :update, :neo_id, :id, :rels, :rel?, :to_param, :getId,
8
8
  :rel, :del, :list?, :print, :print_sub, :outgoing, :incoming, :both,
9
- :equal?, :eql?, :==, :exist?, :getRelationships, :getSingleRelationship, :rels_raw, :rel
9
+ :equal?, :eql?, :==, :exist?, :getRelationships, :getSingleRelationship, :_rels, :rel
10
10
 
11
11
 
12
12
  # --------------------------------------------------------------------------
@@ -59,8 +59,14 @@ module Neo4j::Mapping
59
59
  c.extend Neo4j::Index::ClassMethods
60
60
 
61
61
  def c.inherited(subclass)
62
- subclass.node_indexer subclass
63
62
  subclass.root_class subclass
63
+
64
+ # inherit the index properties
65
+ subclass.node_indexer self
66
+
67
+ # inherit the rules too
68
+ subclass.inherit_rules_from self
69
+
64
70
  super
65
71
  end
66
72
 
@@ -5,7 +5,7 @@ module Neo4j::Mapping
5
5
  include Neo4j::Index
6
6
 
7
7
  def_delegators :@_java_rel, :[]=, :[], :property?, :props, :attributes, :update, :neo_id, :id, :to_param, :getId,
8
- :equal?, :eql?, :==, :delete, :getStartNode, :getEndNode, :getOtherNode
8
+ :equal?, :eql?, :==, :delete, :getStartNode, :getEndNode, :getOtherNode, :exist?
9
9
 
10
10
 
11
11
 
@@ -59,7 +59,7 @@ module Neo4j::Mapping
59
59
  end
60
60
 
61
61
  def exist?
62
- Neo4j::Relationship.load(neo_id) != nil
62
+ Neo4j::Relationship.exist?(self)
63
63
  end
64
64
 
65
65
  # A convenience operation that, given a node that is attached to this relationship, returns the other node.
@@ -118,6 +118,7 @@ module Neo4j
118
118
  # Same as load but does not return the node as a wrapped Ruby object.
119
119
  #
120
120
  def _load(node_id, db)
121
+ return nil if node_id.nil?
121
122
  db.graph.get_node_by_id(node_id.to_i)
122
123
  rescue java.lang.IllegalStateException
123
124
  nil # the node has been deleted
@@ -111,7 +111,7 @@ module Neo4j
111
111
  end
112
112
 
113
113
  # Returns the raw java neo4j relationship object.
114
- def rels_raw(dir=:both, *types)
114
+ def _rels(dir=:both, *types)
115
115
  if types.size > 1
116
116
  java_types = types.inject([]) { |result, type| result << type_to_java(type) }.to_java(:'org.neo4j.graphdb.RelationshipType')
117
117
  get_relationships(java_types)
@@ -2,6 +2,7 @@ module Neo4j
2
2
  module Rails
3
3
  class Model
4
4
  include Neo4j::NodeMixin
5
+ include ActiveModel::Serializers::Xml
5
6
  include ActiveModel::Validations
6
7
  include ActiveModel::Dirty
7
8
  include ActiveModel::MassAssignmentSecurity
@@ -13,7 +14,8 @@ module Neo4j
13
14
 
14
15
  define_model_callbacks :create, :save, :update, :destroy
15
16
 
16
-
17
+ rule :all
18
+
17
19
  UniquenessValidator = Neo4j::Validations::UniquenessValidator
18
20
 
19
21
  class RecordInvalidError < RuntimeError
@@ -35,7 +37,6 @@ module Neo4j
35
37
  def init_on_create(*args) # :nodoc:
36
38
  super()
37
39
  self.attributes=args[0] if args[0].respond_to?(:each_pair)
38
- @_created_record = true
39
40
  end
40
41
 
41
42
  # --------------------------------------
@@ -119,7 +120,7 @@ module Neo4j
119
120
  if new?
120
121
  # We are updating a node that was created with the 'new' method.
121
122
  # The relationship will only be kept in the Value object.
122
- outgoing(rel_type)<<clazz.new(attr) unless reject_if?(reject_if, attr)
123
+ outgoing(rel_type) << clazz.new(attr) unless reject_if?(reject_if, attr) || (allow_destroy && attr[:_destroy] && attr[:_destroy] != '0')
123
124
  else
124
125
  # We have a node that was created with the #create method - has real Neo4j relationships
125
126
  # does it exist ?
@@ -167,36 +168,40 @@ module Neo4j
167
168
  end
168
169
 
169
170
  def save
170
- valid = valid?
171
- if valid
172
- # if we are trying to save a value then we should create a real node
173
- valid = _run_save_callbacks { create_or_update_node }
174
- @_created_record = false
175
- true
176
- else
177
- # if not valid we should rollback the transaction so that the changes does not take place.
178
- # no point failing the transaction if we have created a model with 'new'
179
- Neo4j::Rails::Transaction.fail if Neo4j::Rails::Transaction.running? && !_java_node.kind_of?(Neo4j::Rails::Value)
180
- false
171
+ _run_save_callbacks do
172
+ if create_or_update_node
173
+ true
174
+ else
175
+ # if not valid we should rollback the transaction so that the changes does not take place.
176
+ # no point failing the transaction if we have created a model with 'new'
177
+ Neo4j::Rails::Transaction.fail if Neo4j::Rails::Transaction.running? #&& !_java_node.kind_of?(Neo4j::Rails::Value)
178
+ false
179
+ end
181
180
  end
182
- valid
183
181
  end
184
182
 
185
183
  def create_or_update_node
186
- valid = true
187
- if _java_node.kind_of?(Neo4j::Rails::Value)
188
- node = Neo4j::Node.new(props)
189
- valid = _java_node.save_nested(node)
190
- init_on_load(node)
191
- init_on_create
192
- end
193
-
194
- if new_record?
195
- _run_create_callbacks { clear_changes }
196
- else
197
- _run_update_callbacks { clear_changes }
184
+ if valid?(:save)
185
+ if new_record?
186
+ _run_create_callbacks do
187
+ if valid?(:create)
188
+ node = Neo4j::Node.new(props)
189
+ return false unless _java_node.save_nested(node)
190
+ init_on_load(node)
191
+ init_on_create
192
+ clear_changes
193
+ true
194
+ end
195
+ end
196
+ else
197
+ _run_update_callbacks do
198
+ if valid?(:update)
199
+ clear_changes
200
+ true
201
+ end
202
+ end
203
+ end
198
204
  end
199
- valid
200
205
  end
201
206
 
202
207
  def clear_changes
@@ -204,6 +209,12 @@ module Neo4j
204
209
  @changed_attributes.clear
205
210
  end
206
211
 
212
+ def reload(options = nil)
213
+ clear_changes
214
+ attributes = self.class.load(self.id.to_s).attributes
215
+ self
216
+ end
217
+
207
218
  def save!
208
219
  raise RecordInvalidError.new(self) unless save
209
220
  end
@@ -217,15 +228,11 @@ module Neo4j
217
228
  self
218
229
  end
219
230
 
220
- # Returns true if this object hasn’t been saved yet — that is, a record for the object doesn’t exist yet; otherwise, returns false.
221
- def new_record?()
222
- # it is new if the model has been created with either the new or create method
223
- new? || @_created_record == true
224
- end
225
-
226
231
  def new?
227
232
  _java_node.kind_of?(Neo4j::Rails::Value)
228
233
  end
234
+
235
+ alias :new_record? :new?
229
236
 
230
237
  def del
231
238
  @_deleted = true
@@ -258,12 +265,12 @@ module Neo4j
258
265
  wrapped
259
266
  end
260
267
 
261
- # Handle Model.find(params[:id])
262
- def find(*args)
263
- if args.length == 1 && String === args[0] && args[0].to_i != 0
264
- load(*args)
268
+ # Behave like ActiveModel
269
+ def all_with_args(*args)
270
+ if args.empty?
271
+ all_without_args
265
272
  else
266
- hits = super
273
+ hits = find_without_checking_for_id(*args)
267
274
  # We need to save this so that the Rack Neo4j::Rails:LuceneConnection::Closer can close it
268
275
  Thread.current[:neo4j_lucene_connection] ||= []
269
276
  Thread.current[:neo4j_lucene_connection] << hits
@@ -271,6 +278,19 @@ module Neo4j
271
278
  end
272
279
  end
273
280
 
281
+ alias_method_chain :all, :args
282
+
283
+ # Handle Model.find(params[:id])
284
+ def find_with_checking_for_id(*args)
285
+ if args.length == 1 && String === args[0] && args[0].to_i != 0
286
+ load(*args)
287
+ else
288
+ all_with_args(*args).first
289
+ end
290
+ end
291
+
292
+ alias_method_chain :find, :checking_for_id
293
+
274
294
  def load(*ids)
275
295
  result = ids.map { |id| Neo4j::Node.load(id) }
276
296
  if ids.length == 1
@@ -283,16 +303,12 @@ module Neo4j
283
303
 
284
304
  alias_method :_orig_create, :create
285
305
 
286
- def create(*)
287
- model = super
288
- model.save
289
- model
306
+ def create(*args)
307
+ new(*args).tap { |o| o.save }
290
308
  end
291
309
 
292
310
  def create!(*args)
293
- model = _orig_create(*args)
294
- model.save!
295
- model
311
+ new(*args).tap { |o| o.save! }
296
312
  end
297
313
 
298
314
  tx_methods :create, :create!
@@ -338,4 +354,3 @@ module Neo4j
338
354
 
339
355
  end
340
356
  end
341
-
@@ -52,13 +52,14 @@ module Neo4j
52
52
  def run
53
53
  begin
54
54
  new
55
- yield
55
+ ret = yield
56
56
  rescue
57
57
  fail
58
58
  raise
59
59
  ensure
60
60
  finish
61
61
  end
62
+ ret
62
63
  end
63
64
  end
64
65
  end
@@ -5,16 +5,16 @@ module Neo4j
5
5
  super(options.reverse_merge(:case_sensitive => true))
6
6
  end
7
7
 
8
- def validate_each(record, attribute, value)
9
- clazz = record.class
10
-
11
- # TODO is it possible to move this to setup instead so that we don't have to do this always ?
12
- if clazz.index_type_for(attribute) != :exact
13
- raise "Can't validate property #{attribute} on class #{clazz} since there is no :exact lucene index on that property"
8
+ def setup(klass)
9
+ @attributes.each do |attribute|
10
+ if klass.index_type_for(attribute) != :exact
11
+ raise "Can't validate property #{attribute} on class #{klass} since there is no :exact lucene index on that property or the index declaration #{attribute} comes after the validation declaration in #{klass} (try to move it before the validation rules)"
12
+ end
14
13
  end
14
+ end
15
15
 
16
- query = "#{attribute}: #{value}"
17
- if !clazz.find(query).empty?
16
+ def validate_each(record, attribute, value)
17
+ if record.class.find("#{attribute}: #{value}")
18
18
  record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
19
19
  end
20
20
  end
@@ -75,6 +75,11 @@ module Neo4j::Rails
75
75
  end
76
76
  valid
77
77
  end
78
+
79
+ # this node doesn't exist in the DB yet
80
+ def exist?
81
+ false
82
+ end
78
83
 
79
84
  class Relationship
80
85
  include org.neo4j.graphdb.Relationship
@@ -36,6 +36,12 @@ module Neo4j
36
36
  self
37
37
  end
38
38
 
39
+
40
+ # Returns true if the relationship exists
41
+ def exist?
42
+ Neo4j::Relationship.exist?(self)
43
+ end
44
+
39
45
  # Loads the Ruby wrapper for this node
40
46
  # If there is no _classname property for this node then it will simply return itself.
41
47
  # Same as Neo4j::Node.load_wrapper(node)
@@ -130,8 +136,24 @@ module Neo4j
130
136
  # create is the same as new
131
137
  alias_method :create, :new
132
138
 
139
+ # Loads a relationship or wrapped relationship given a native java relationship or an id.
140
+ # If there is a Ruby wrapper for the node then it will create a Ruby object that will
141
+ # wrap the java node (see Neo4j::RelationshipMixin).
142
+ #
143
+ # If the relationship does not exist it will return nil
144
+ #
133
145
  def load(rel_id, db = Neo4j.started_db)
134
- wrapper(db.graph.get_relationship_by_id(rel_id.to_i))
146
+ rel = _load(rel_id, db)
147
+ return nil if rel.nil?
148
+ rel.wrapper
149
+ end
150
+
151
+ # Same as load but does not return the node as a wrapped Ruby object.
152
+ #
153
+ def _load(rel_id, db)
154
+ rel = db.graph.get_relationship_by_id(rel_id.to_i)
155
+ rel.hasProperty('_classname') # since we want a IllegalStateException which is otherwise not triggered
156
+ rel
135
157
  rescue java.lang.IllegalStateException
136
158
  nil # the node has been deleted
137
159
  rescue org.neo4j.graphdb.NotFoundException
@@ -4,59 +4,83 @@ module Neo4j
4
4
  include Enumerable
5
5
  include ToJava
6
6
 
7
- def initialize(node, types, dir)
8
- @node = node
7
+ def initialize(node, types, direction)
8
+ @node = node
9
9
  if types.size > 1
10
10
  @types = types.inject([]) { |result, type| result << type_to_java(type) }.to_java(:'org.neo4j.graphdb.RelationshipType')
11
11
  elsif types.size == 1
12
12
  @type = type_to_java(types[0])
13
13
  end
14
-
15
- @dir = dir_to_java(dir)
14
+ @direction = direction
16
15
  end
17
16
 
18
17
  def to_s
19
18
  if @type
20
- "#{self.class} [type: #{@type} dir:#{@dir}]"
19
+ "#{self.class} [type: #{@type} dir:#{@direction}]"
21
20
  elsif @types
22
- "#{self.class} [types: #{@types.join(',')} dir:#{@dir}]"
21
+ "#{self.class} [types: #{@types.join(',')} dir:#{@direction}]"
23
22
  else
24
- "#{self.class} [types: ANY dir:#{@dir}]"
23
+ "#{self.class} [types: ANY dir:#{@direction}]"
25
24
  end
26
25
  end
27
26
 
28
27
  def each
29
- iterator.each {|i| yield i.wrapper}
28
+ iter = iterator
29
+ while (iter.hasNext())
30
+ rel = iter.next
31
+ yield rel.wrapper if match_to_other?(rel)
32
+ end
30
33
  end
31
34
 
32
35
  def iterator
33
36
  if @types
34
37
  @node.get_relationships(@types).iterator
35
38
  elsif @type
36
- @node.get_relationships(@type, @dir).iterator
39
+ @node.get_relationships(@type, dir_to_java(@direction))
40
+ else
41
+ @node.get_relationships(dir_to_java(@direction))
42
+ end
43
+ end
44
+
45
+ def match_to_other?(rel)
46
+ if @to_other.nil?
47
+ true
48
+ elsif @direction == :outgoing
49
+ rel._end_node == @to_other
50
+ elsif @direction == :incoming
51
+ rel._start_node == @to_other
37
52
  else
38
- @node.get_relationships(@dir).iterator
53
+ rel._start_node == @to_other || rel._end_node == @to_other
39
54
  end
40
55
  end
41
56
 
57
+ def to_other(to_other)
58
+ @to_other = to_other
59
+ self
60
+ end
61
+
62
+ def del
63
+ each { |rel| rel.del }
64
+ end
65
+
42
66
  def size
43
67
  [*self].size
44
68
  end
45
69
 
46
70
  def both
47
- @dir = dir_to_java(:both)
71
+ @direction = :both
48
72
  self
49
73
  end
50
74
 
51
75
  def incoming
52
76
  raise "Not allowed calling incoming when finding several relationships types" if @types
53
- @dir = dir_to_java(:incoming)
77
+ @direction = :incoming
54
78
  self
55
79
  end
56
80
 
57
81
  def outgoing
58
82
  raise "Not allowed calling outgoing when finding several relationships types" if @types
59
- @dir = dir_to_java(:outgoing)
83
+ @direction = :outgoing
60
84
  self
61
85
  end
62
86
 
@@ -1,3 +1,3 @@
1
1
  module Neo4j
2
- VERSION = "1.0.0.beta.15"
3
- end
2
+ VERSION = "1.0.0.beta.16"
3
+ end
metadata CHANGED
@@ -7,8 +7,8 @@ version: !ruby/object:Gem::Version
7
7
  - 0
8
8
  - 0
9
9
  - beta
10
- - 15
11
- version: 1.0.0.beta.15
10
+ - 16
11
+ version: 1.0.0.beta.16
12
12
  platform: ruby
13
13
  authors:
14
14
  - Andreas Ronge
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-10-26 00:00:00 +02:00
19
+ date: 2010-10-28 00:00:00 +02:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency