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

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.
@@ -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