neo4j 1.2.6-java → 2.0.0.alpha.3-java

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.
Files changed (78) hide show
  1. data/CHANGELOG +30 -0
  2. data/CONTRIBUTORS +1 -0
  3. data/Gemfile +16 -4
  4. data/README.rdoc +25 -3
  5. data/bin/neo4j-jars +15 -8
  6. data/bin/neo4j-shell +0 -1
  7. data/bin/neo4j-upgrade +72 -0
  8. data/config/neo4j/config.yml +5 -4
  9. data/lib/neo4j/algo/algo.rb +0 -1
  10. data/lib/neo4j/batch/inserter.rb +5 -0
  11. data/lib/neo4j/database.rb +18 -12
  12. data/lib/neo4j/event_handler.rb +76 -9
  13. data/lib/neo4j/has_list/class_methods.rb +1 -1
  14. data/lib/neo4j/has_list/mapping.rb +13 -33
  15. data/lib/neo4j/has_n/decl_relationship_dsl.rb +17 -13
  16. data/lib/neo4j/has_n/mapping.rb +6 -23
  17. data/lib/neo4j/identity_map.rb +0 -3
  18. data/lib/neo4j/index/class_methods.rb +9 -3
  19. data/lib/neo4j/index/index.rb +2 -1
  20. data/lib/neo4j/index/indexer.rb +3 -2
  21. data/lib/neo4j/index/indexer_registry.rb +1 -1
  22. data/lib/neo4j/index/lucene_query.rb +52 -33
  23. data/lib/neo4j/neo4j.rb +19 -0
  24. data/lib/neo4j/node.rb +42 -36
  25. data/lib/neo4j/node_mixin/node_mixin.rb +9 -5
  26. data/lib/neo4j/paginated.rb +23 -0
  27. data/lib/neo4j/rails/accept_id.rb +66 -0
  28. data/lib/neo4j/rails/attributes.rb +14 -9
  29. data/lib/neo4j/rails/compositions.rb +9 -1
  30. data/lib/neo4j/rails/finders.rb +5 -1
  31. data/lib/neo4j/rails/mapping/property.rb +41 -28
  32. data/lib/neo4j/rails/model.rb +2 -0
  33. data/lib/neo4j/rails/observer.rb +61 -2
  34. data/lib/neo4j/rails/persistence.rb +57 -57
  35. data/lib/neo4j/rails/rails.rb +1 -0
  36. data/lib/neo4j/rails/railtie.rb +4 -1
  37. data/lib/neo4j/rails/rel_persistence.rb +11 -12
  38. data/lib/neo4j/rails/relationship.rb +9 -4
  39. data/lib/neo4j/rails/relationships/node_dsl.rb +15 -5
  40. data/lib/neo4j/rails/relationships/relationships.rb +42 -2
  41. data/lib/neo4j/rails/relationships/rels_dsl.rb +60 -3
  42. data/lib/neo4j/rails/relationships/storage.rb +43 -5
  43. data/lib/neo4j/rails/validations/uniqueness.rb +1 -0
  44. data/lib/neo4j/rails/versioning/versioning.rb +64 -9
  45. data/lib/neo4j/relationship.rb +79 -73
  46. data/lib/neo4j/rule/event_listener.rb +7 -1
  47. data/lib/neo4j/rule/functions/count.rb +6 -0
  48. data/lib/neo4j/rule/rule.rb +20 -5
  49. data/lib/neo4j/rule/rule_node.rb +33 -20
  50. data/lib/neo4j/to_java.rb +5 -0
  51. data/lib/neo4j/traversal/traverser.rb +38 -1
  52. data/lib/neo4j/type_converters/type_converters.rb +56 -5
  53. data/lib/neo4j/version.rb +1 -1
  54. data/lib/neo4j.rb +3 -49
  55. data/neo4j.gemspec +2 -2
  56. metadata +191 -216
  57. data/bin/neo4j-shell~ +0 -108
  58. data/lib/Gemfile~ +0 -3
  59. data/lib/config2.yml~ +0 -86
  60. data/lib/neo4j/jars/core/geronimo-jta_1.1_spec-1.1.1.jar +0 -0
  61. data/lib/neo4j/jars/core/lucene-core-3.1.0.jar +0 -0
  62. data/lib/neo4j/jars/core/neo4j-backup-1.4.1.jar +0 -0
  63. data/lib/neo4j/jars/core/neo4j-graph-algo-1.4.1.jar +0 -0
  64. data/lib/neo4j/jars/core/neo4j-index-1.3-1.3.M01.jar +0 -0
  65. data/lib/neo4j/jars/core/neo4j-kernel-1.4.1.jar +0 -0
  66. data/lib/neo4j/jars/core/neo4j-lucene-index-1.4.1.jar +0 -0
  67. data/lib/neo4j/jars/ha/log4j-1.2.16.jar +0 -0
  68. data/lib/neo4j/jars/ha/neo4j-com-1.4.1.jar +0 -0
  69. data/lib/neo4j/jars/ha/neo4j-ha-1.4.1.jar +0 -0
  70. data/lib/neo4j/jars/ha/neo4j-jmx-1.4.1.jar +0 -0
  71. data/lib/neo4j/jars/ha/neo4j-management-1.4.1.jar +0 -0
  72. data/lib/neo4j/jars/ha/neo4j-shell-1.4.1.jar +0 -0
  73. data/lib/neo4j/jars/ha/netty-3.2.1.Final.jar +0 -0
  74. data/lib/neo4j/jars/ha/org.apache.servicemix.bundles.jline-0.9.94_1.jar +0 -0
  75. data/lib/neo4j/jars/ha/zookeeper-3.3.2.jar +0 -0
  76. data/lib/neo4j/paginate.rb +0 -25
  77. data/lib/perf.rb~ +0 -36
  78. data/lib/test.rb~ +0 -2
@@ -206,6 +206,14 @@ module Neo4j
206
206
  # :constructor => Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
207
207
  # :converter => Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
208
208
  #
209
+ def is_composed_property?(property)
210
+ composed_properties.contains(property)
211
+ end
212
+
213
+ def composed_properties
214
+ @composed_properties ||= java.util.HashSet.new
215
+ end
216
+
209
217
  def composed_of(part_id, options = {})
210
218
  options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
211
219
 
@@ -216,7 +224,7 @@ module Neo4j
216
224
  allow_nil = options[:allow_nil] || false
217
225
  constructor = options[:constructor] || :new
218
226
  converter = options[:converter]
219
-
227
+ composed_properties.add(name.to_sym)
220
228
  reader_method(name, class_name, mapping, allow_nil, constructor)
221
229
  writer_method(name, class_name, mapping, allow_nil, converter)
222
230
  end
@@ -76,7 +76,7 @@ module Neo4j
76
76
  when "0", 0, nil
77
77
  nil
78
78
  else
79
- if ((args.first.is_a?(Integer) || args.first.is_a?(String)) && args.first.to_i > 0)
79
+ if convertable_to_id?(args.first)
80
80
  find_with_ids(*args)
81
81
  else
82
82
  first(*args)
@@ -167,6 +167,10 @@ module Neo4j
167
167
  condition.is_a?(Hash) && condition[:id]
168
168
  end
169
169
 
170
+ def convertable_to_id?(value)
171
+ return false unless value
172
+ value.is_a?(Integer) || (value.is_a?(String) && value =~ /^[+-]?\d+$/)
173
+ end
170
174
 
171
175
  def conditions_in?(*args)
172
176
  return false if args.empty?
@@ -55,8 +55,7 @@ module Neo4j
55
55
  def #{rel_type}=(other)
56
56
  dsl = _decl_rels_for(:'#{rel_type}')
57
57
  storage = _create_or_get_storage_for_decl_rels(dsl)
58
- rel = storage.single_relationship(dsl.dir)
59
- rel && rel.destroy
58
+ storage.destroy_single_relationship(dsl.dir)
60
59
  storage.create_relationship_to(other, dsl.dir)
61
60
  end
62
61
  RUBY
@@ -76,9 +75,21 @@ module Neo4j
76
75
  end
77
76
 
78
77
  unless method_defined?("#{rel_type}=".to_sym)
78
+
79
+ # TODO: This is a temporary fix for allowing running neo4j with Formtastic, issue 109
80
+ # A better solution might be to implement accept_ids for has_n relationship and
81
+ # make sure (somehow) that Formtastic uses the _ids methods.
82
+
79
83
  class_eval <<-RUBY, __FILE__, __LINE__
80
84
  def #{rel_type}=(nodes)
81
- self.#{rel_type}_rels.destroy_all
85
+ if nodes.is_a?(Array) && nodes.first.is_a?(String)
86
+ if nodes.first.blank?
87
+ self.#{rel_type}_rels.destroy_all
88
+ nodes.shift
89
+ end
90
+ else
91
+ self.#{rel_type}_rels.destroy_all
92
+ end
82
93
  association = self.#{rel_type}
83
94
  nodes.each { |node| association << node }
84
95
  end
@@ -106,10 +117,10 @@ module Neo4j
106
117
 
107
118
  # Handles options for the property
108
119
  #
109
- # Set the property type :type => Time
110
- # Set a default :default => "default"
111
- # Property must be there :null => false
112
- # Property has a length limit :limit => 128
120
+ # Set the property type :type => Time
121
+ # Set a default :default => "default"
122
+ # Property must be there :null => false
123
+ # Property has a length limit :limit => 128
113
124
  def property(*args)
114
125
  options = args.extract_options!
115
126
  args.each do |property_sym|
@@ -132,39 +143,41 @@ module Neo4j
132
143
  validates(property, :non_nil => true, :on => :create)
133
144
  validates(property, :non_nil => true, :on => :update)
134
145
  end
135
- validates(property, :length => { :maximum => options[:limit] }) if options[:limit]
136
- end
137
-
138
- def define_property_methods_for(property, options)
139
- unless method_defined?(property)
140
- class_eval <<-RUBY, __FILE__, __LINE__
141
- def #{property}
142
- send(:[], "#{property}")
143
- end
146
+ validates(property, :length => { :maximum => options[:limit] }) if options[:limit]
147
+ end
148
+
149
+ def define_property_methods_for(property, options)
150
+ unless method_defined?(property)
151
+ class_eval <<-RUBY, __FILE__, __LINE__
152
+ def #{property}
153
+ send(:[], "#{property}")
154
+ end
144
155
  RUBY
145
156
  end
146
157
 
147
158
  unless method_defined?("#{property}=".to_sym)
148
159
  class_eval <<-RUBY, __FILE__, __LINE__
149
- def #{property}=(value)
150
- send(:[]=, "#{property}", value)
151
- end
152
- RUBY
153
- end
154
- end
160
+ def #{property}=(value)
161
+ send(:[]=, "#{property}", value)
162
+ end
163
+ RUBY
164
+ end
165
+ end
155
166
 
156
167
  def define_property_before_type_cast_methods_for(property, options)
157
168
  property_before_type_cast = "#{property}_before_type_cast"
158
169
  class_eval <<-RUBY, __FILE__, __LINE__
159
- attr_writer :#{property_before_type_cast}
170
+ def #{property_before_type_cast}=(value)
171
+ @properties_before_type_cast[:#{property}]=value
172
+ end
160
173
 
161
174
  def #{property_before_type_cast}
162
- instance_variable_defined?(:@#{property_before_type_cast}) ? @#{property_before_type_cast} : self.#{property}
175
+ @properties_before_type_cast.has_key?(:#{property}) ? @properties_before_type_cast[:#{property}] : self.#{property}
163
176
  end
164
177
  RUBY
165
178
  end
166
- end
167
- end
168
- end
169
- end
179
+ end
180
+ end
181
+ end
182
+ end
170
183
  end
@@ -48,6 +48,7 @@ module Neo4j
48
48
 
49
49
  # Initialize a Node with a set of properties (or empty if nothing is passed)
50
50
  def initialize(attributes = {})
51
+ @properties_before_type_cast=java.util.HashMap.new
51
52
  reset_attributes
52
53
  clear_relationships
53
54
  self.attributes = attributes if attributes.is_a?(Hash)
@@ -269,6 +270,7 @@ module Neo4j
269
270
  include Finders # ActiveRecord style find
270
271
  include Relationships # for none persisted relationships
271
272
  include Compositions
273
+ include AcceptId
272
274
  end
273
275
  end
274
276
  end
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/class/attribute_accessors'
2
+
1
3
  module Neo4j
2
4
  module Rails
3
5
  # Observer classes respond to life cycle callbacks to implement trigger-like
@@ -86,11 +88,26 @@ module Neo4j
86
88
  # In order to activate an observer, list it in the +config.neo4j.observers+
87
89
  # configuration setting in your +config/application.rb+ file.
88
90
  #
89
- # config.neo4j.observers = :comment_observer, :signup_observer
91
+ # config.neo4j.observers = [:comment_observer, :signup_observer]
90
92
  #
91
93
  # Observers will not be invoked unless you define them in your
92
94
  # application configuration.
93
95
  #
96
+ # During testing you may want (and probably should) to disable all the observers.
97
+ # Most of the time you don't want any kind of emails to be sent when creating objects.
98
+ # This should improve the speed of your tests and isolate the models and observer logic.
99
+ #
100
+ # For example, the following will disable the observers in RSpec:
101
+ #
102
+ # config.before(:each) { Neo4j::Rails::Observer.disable_observers }
103
+ #
104
+ # But if you do want to run a particular observer(s) as part of the test,
105
+ # you can temporarily enable it:
106
+ #
107
+ # Neo4j::Rails::Observer.with_observers(:user_recorder, :account_observer) do
108
+ # # Any code here will work with observers enabled
109
+ # end
110
+ #
94
111
  # == Loading
95
112
  #
96
113
  # Observers register themselves with the model class that they observe,
@@ -113,6 +130,48 @@ module Neo4j
113
130
  super and observed_descendants.each { |klass| add_observer!(klass) }
114
131
  end
115
132
 
133
+ cattr_accessor :default_observers_enabled, :observers_enabled
134
+
135
+ # TODO: Add docs
136
+ class << self
137
+ # Enables all observers (default behavior)
138
+ def enable_observers
139
+ self.default_observers_enabled = true
140
+ end
141
+
142
+ # Disables all observers
143
+ def disable_observers
144
+ self.default_observers_enabled = false
145
+ end
146
+
147
+ # Run a block with a specific set of observers enabled
148
+ def with_observers(*observer_syms)
149
+ self.observers_enabled = Array(observer_syms).map do |o|
150
+ o.respond_to?(:instance) ? o.instance : o.to_s.classify.constantize.instance
151
+ end
152
+ yield
153
+ ensure
154
+ self.observers_enabled = []
155
+ end
156
+
157
+ # Determines whether an observer is enabled. Either:
158
+ # - All observers are enabled OR
159
+ # - The observer is in the whitelist
160
+ def observer_enabled?(observer)
161
+ default_observers_enabled or self.observers_enabled.include?(observer)
162
+ end
163
+ end
164
+
165
+
166
+ # Determines whether this observer should be run
167
+ def observer_enabled?
168
+ self.class.observer_enabled?(self)
169
+ end
170
+
171
+ # By default, enable all observers
172
+ enable_observers
173
+ self.observers_enabled = []
174
+
116
175
  protected
117
176
 
118
177
  # Get all the child observers.
@@ -149,7 +208,7 @@ module Neo4j
149
208
  callback_meth = :"_notify_#{observer_name}_for_#{callback}"
150
209
  unless klass.respond_to?(callback_meth)
151
210
  klass.send(:define_method, callback_meth) do |&block|
152
- observer.send(callback, self, &block)
211
+ observer.send(callback, self, &block) if observer.observer_enabled?
153
212
  end
154
213
  klass.send(callback, callback_meth)
155
214
  end
@@ -1,62 +1,62 @@
1
1
  module Neo4j
2
- module Rails
3
- module Persistence
4
- extend ActiveSupport::Concern
5
-
6
- included do
7
- extend TxMethods
8
- tx_methods :destroy, :create, :update, :update_nested_attributes, :delete, :update_attributes, :update_attributes!
9
- end
10
-
11
- # Persist the object to the database. Validations and Callbacks are included
12
- # by default but validation can be disabled by passing :validate => false
13
- # to #save.
2
+ module Rails
3
+ module Persistence
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ extend TxMethods
8
+ tx_methods :destroy, :create, :update, :update_nested_attributes, :delete, :update_attributes, :update_attributes!
9
+ end
10
+
11
+ # Persist the object to the database. Validations and Callbacks are included
12
+ # by default but validation can be disabled by passing :validate => false
13
+ # to #save.
14
14
  def save(*)
15
- create_or_update
15
+ create_or_update
16
16
  end
17
17
 
18
18
  # Persist the object to the database. Validations and Callbacks are included
19
- # by default but validation can be disabled by passing :validate => false
20
- # to #save!.
21
- #
22
- # Raises a RecordInvalidError if there is a problem during save.
19
+ # by default but validation can be disabled by passing :validate => false
20
+ # to #save!.
21
+ #
22
+ # Raises a RecordInvalidError if there is a problem during save.
23
23
  def save!(*args)
24
- unless save(*args)
25
- raise RecordInvalidError.new(self)
26
- end
27
- end
28
-
29
- # Updates a single attribute and saves the record.
30
- # This is especially useful for boolean flags on existing records. Also note that
31
- #
32
- # * Validation is skipped.
33
- # * Callbacks are invoked.
34
- # * Updates all the attributes that are dirty in this object.
35
- #
36
- def update_attribute(name, value)
37
- respond_to?("#{name}=") ? send("#{name}=", value) : self[name] = value
38
- save(:validate => false)
39
- end
40
-
41
- # Removes the node from Neo4j and freezes the object.
42
- def destroy
43
- delete
44
- freeze
45
- end
46
-
47
- # Same as #destroy but doesn't run destroy callbacks and doesn't freeze
48
- # the object
49
- def delete
50
- del unless new_record?
51
- set_deleted_properties
52
- end
53
-
54
- # Returns true if the object was destroyed.
55
- def destroyed?()
24
+ unless save(*args)
25
+ raise RecordInvalidError.new(self)
26
+ end
27
+ end
28
+
29
+ # Updates a single attribute and saves the record.
30
+ # This is especially useful for boolean flags on existing records. Also note that
31
+ #
32
+ # * Validation is skipped.
33
+ # * Callbacks are invoked.
34
+ # * Updates all the attributes that are dirty in this object.
35
+ #
36
+ def update_attribute(name, value)
37
+ respond_to?("#{name}=") ? send("#{name}=", value) : self[name] = value
38
+ save(:validate => false)
39
+ end
40
+
41
+ # Removes the node from Neo4j and freezes the object.
42
+ def destroy
43
+ delete
44
+ freeze
45
+ end
46
+
47
+ # Same as #destroy but doesn't run destroy callbacks and doesn't freeze
48
+ # the object
49
+ def delete
50
+ del unless new_record?
51
+ set_deleted_properties
52
+ end
53
+
54
+ # Returns true if the object was destroyed.
55
+ def destroyed?()
56
56
  @_deleted || Neo4j::Node._load(id).nil?
57
57
  end
58
58
 
59
- # Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved.
59
+ # Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved.
60
60
  # If saving fails because the resource is invalid then false will be returned.
61
61
  def update_attributes(attributes)
62
62
  self.attributes = attributes
@@ -95,9 +95,9 @@ module Neo4j
95
95
  alias :new? :new_record?
96
96
 
97
97
  # Freeze the properties hash.
98
- def freeze
99
- @properties.freeze; self
100
- end
98
+ def freeze
99
+ @properties.freeze; self
100
+ end
101
101
 
102
102
  # Returns +true+ if the properties hash has been frozen.
103
103
  def frozen?
@@ -162,7 +162,7 @@ module Neo4j
162
162
  end
163
163
 
164
164
  def init_on_create(*)
165
- self["_classname"] = self.class.to_s
165
+ self._classname = self.class.to_s
166
166
  write_default_attributes
167
167
  write_changed_attributes
168
168
  write_changed_relationships
@@ -190,7 +190,7 @@ module Neo4j
190
190
  # Ensure any defaults are stored in the DB
191
191
  def write_default_attributes
192
192
  attribute_defaults.each do |attribute, value|
193
- write_attribute(attribute, Neo4j::TypeConverters.convert(value, attribute, self.class)) unless changed_attributes.has_key?(attribute) || _java_node.has_property?(attribute)
193
+ write_attribute(attribute, Neo4j::TypeConverters.convert(value, attribute, self.class, false)) unless changed_attributes.has_key?(attribute) || _java_node.has_property?(attribute)
194
194
  end
195
195
  end
196
196
 
@@ -262,7 +262,7 @@ module Neo4j
262
262
  super(@record.errors.full_messages.join(", "))
263
263
  end
264
264
  end
265
- end
266
- end
265
+ end
266
+ end
267
267
  end
268
268
 
@@ -11,6 +11,7 @@ require 'neo4j/rails/validations'
11
11
  require 'neo4j/rails/callbacks'
12
12
  require 'neo4j/rails/observer'
13
13
  require 'neo4j/rails/compositions'
14
+ require 'neo4j/rails/accept_id'
14
15
  require 'neo4j/rails/timestamps'
15
16
  require 'neo4j/rails/serialization'
16
17
  require 'neo4j/rails/attributes'
@@ -15,7 +15,10 @@ module Neo4j
15
15
  # Starting Neo after :load_config_initializers allows apps to
16
16
  # register migrations in config/initializers
17
17
  initializer "neo4j.start", :after => :load_config_initializers do |app|
18
- Neo4j::Config.setup.merge!(app.config.neo4j.to_hash)
18
+ cfg = app.config.neo4j
19
+ # Set Rails specific defaults
20
+ cfg.storage_path = "#{app.config.root}/db/neo4j-#{::Rails.env}" unless cfg.storage_path
21
+ Neo4j::Config.setup.merge!(cfg.to_hash)
19
22
  end
20
23
 
21
24
  # Instantitate any registered observers after Rails initialization and
@@ -151,17 +151,20 @@ module Neo4j
151
151
  def create()
152
152
  begin
153
153
  # prevent calling create twice
154
- @start_node.rm_outgoing_rel(type, self)
155
- @end_node.rm_incoming_rel(type, self)
154
+ @start_node.add_unpersisted_outgoing_rel(type, self)
155
+ @end_node.add_unpersisted_incoming_rel(type, self)
156
156
 
157
- _persist_start_node
158
- _persist_end_node
157
+ return unless _persist_start_node && _persist_end_node
159
158
 
160
159
  @_java_rel = Neo4j::Relationship.new(type, start_node, end_node)
161
160
  Neo4j::IdentityMap.add(@_java_rel, self)
162
161
  init_on_create
163
162
  clear_changes
164
- end unless @end_node.nil?
163
+
164
+ @start_node.rm_unpersisted_outgoing_rel(type, self)
165
+ @end_node.rm_unpersisted_incoming_rel(type, self)
166
+
167
+ end unless @end_node.nil? or @start_node.nil?
165
168
  true
166
169
  end
167
170
 
@@ -170,18 +173,14 @@ module Neo4j
170
173
  end
171
174
 
172
175
  def _persist_start_node
173
- unless @start_node.persisted? || @start_node.save
174
- # not sure if this can happen - probably a bug
175
- raise "Can't save start_node #{@start_node} id #{@start_node.id}"
176
- end
176
+ (!@start_node.persisted? || @start_node.relationships_changed?) ? @start_node.save : true
177
177
  end
178
178
 
179
179
  def _persist_end_node
180
- unless @end_node.persisted? || @end_node.save
181
- raise "Can't save end_node #{@end_node} id #{@end_node.id}"
182
- end
180
+ (!@end_node.persisted? || @end_node.relationships_changed?) ? @end_node.save : true
183
181
  end
184
182
 
183
+
185
184
  def init_on_create(*)
186
185
  #self["_classname"] = self.class.to_s
187
186
  write_default_attributes
@@ -21,7 +21,8 @@ module Neo4j
21
21
 
22
22
  # Initialize a Node with a set of properties (or empty if nothing is passed)
23
23
  def initialize(*args)
24
- @type = args[0]
24
+ @properties_before_type_cast=java.util.HashMap.new
25
+ @type = args[0].to_s
25
26
  self.start_node = args[1]
26
27
  self.end_node = args[2]
27
28
  attributes = args[3]
@@ -39,11 +40,15 @@ module Neo4j
39
40
 
40
41
 
41
42
  alias_method :get_other_node, :other_node # so it looks like the java version
42
-
43
+
44
+ def rel_type
45
+ persisted? ? _java_entity.rel_type : @type
46
+ end
47
+
43
48
  def to_s
44
49
  "id: #{self.object_id} start_node: #{start_node.id} end_node: #{end_node.id} type:#{@type}"
45
50
  end
46
-
51
+
47
52
  def id
48
53
  _java_rel.nil? || neo_id.nil? ? nil : neo_id.to_s
49
54
  end
@@ -111,7 +116,7 @@ module Neo4j
111
116
  def _all
112
117
  _indexer.find(:_classname => self)
113
118
  end
114
-
119
+
115
120
  def load(*ids) # TODO Copied from finders.rb
116
121
  result = ids.map { |id| entity_load(id) }
117
122
  if ids.length == 1
@@ -9,7 +9,6 @@ module Neo4j
9
9
  #
10
10
  class NodesDSL
11
11
  include Enumerable
12
- include Neo4j::Paginate
13
12
 
14
13
  def initialize(storage, dir)
15
14
  @storage = storage
@@ -51,7 +50,14 @@ module Neo4j
51
50
 
52
51
  # Adds a new node to the relationship
53
52
  def <<(other)
54
- @storage.create_relationship_to(other, @dir)
53
+ if other.is_a?(String)
54
+ # this is typically called in an assignment operator, person.friends = ['42', '32']
55
+ node = Neo4j::Node.load(other)
56
+ @storage.create_relationship_to(node, @dir) unless all.include?(node)
57
+ else
58
+ # allow multiple relationships to the same node
59
+ @storage.create_relationship_to(other, @dir)
60
+ end
55
61
  self
56
62
  end
57
63
 
@@ -166,8 +172,8 @@ module Neo4j
166
172
 
167
173
  alias :length :size
168
174
 
169
- def each(&block)
170
- @storage.each_node(@dir, &block)
175
+ def each
176
+ @storage.each_node(@dir) {|n| yield n} # Why passing the &block through doesn't work on JRuby 1.9?
171
177
  end
172
178
 
173
179
  def delete(*nodes)
@@ -185,7 +191,11 @@ module Neo4j
185
191
  def to_s
186
192
  "Node dir: #{@dir}, #{@storage}"
187
193
  end
188
-
194
+
195
+ def rel_changed?
196
+ @storage.persisted?
197
+ end
198
+
189
199
  protected
190
200
 
191
201
 
@@ -30,6 +30,9 @@ module Neo4j
30
30
  @_relationships[decl_rels.rel_type.to_sym] ||= Storage.new(self, decl_rels.rel_type, decl_rels)
31
31
  end
32
32
 
33
+ def _storage_for(rel_type) #:nodoc:
34
+ @_relationships[rel_type.to_sym]
35
+ end
33
36
 
34
37
  # If the node is persisted and it does not have any unsaved relationship it returns a Neo4j::NodeTraverser.
35
38
  # Otherwise it will return a NodesDSL which behaves like the Neo4j::NodeTraverser except that it does not
@@ -66,8 +69,12 @@ module Neo4j
66
69
  # node.rels(:foo) #=> [node2] - incoming and outgoing
67
70
  #
68
71
  def rels(*rel_types)
69
- storage = _create_or_get_storage(rel_types.first)
70
- RelsDSL.new(storage)
72
+ if rel_types.empty?
73
+ AllRelsDsl.new(@_relationships, _java_node)
74
+ else
75
+ storage = _create_or_get_storage(rel_types.first)
76
+ RelsDSL.new(storage)
77
+ end
71
78
  end
72
79
 
73
80
  def add_outgoing_rel(rel_type, rel) #:nodoc:
@@ -86,6 +93,39 @@ module Neo4j
86
93
  _create_or_get_storage(rel_type).rm_outgoing_rel(rel)
87
94
  end
88
95
 
96
+ def rm_outgoing_rel(rel_type, rel) #:nodoc:
97
+ _create_or_get_storage(rel_type).rm_outgoing_rel(rel)
98
+ end
99
+
100
+ def add_unpersisted_incoming_rel(rel_type, rel) #:nodoc
101
+ if (storage = _create_or_get_storage(rel_type))
102
+ # move the relationship since we are now about to store the relationship
103
+ storage.add_unpersisted_incoming_rel(rel)
104
+ storage.rm_incoming_rel(rel)
105
+ end
106
+ end
107
+
108
+ def add_unpersisted_outgoing_rel(rel_type, rel) #:nodoc
109
+ if (storage = _create_or_get_storage(rel_type))
110
+ # move the relationship since we are now about to store the relationship
111
+ storage.add_unpersisted_outgoing_rel(rel)
112
+ storage.rm_outgoing_rel(rel)
113
+ end
114
+ end
115
+
116
+ def rm_unpersisted_outgoing_rel(rel_type, rel) #:nodoc
117
+ if (storage = _storage_for(rel_type))
118
+ storage.rm_unpersisted_outgoing_rel(rel)
119
+ end
120
+ end
121
+
122
+ def rm_unpersisted_incoming_rel(rel_type, rel) #:nodoc
123
+ if (storage = _storage_for(rel_type))
124
+ storage.rm_unpersisted_incoming_rel(rel)
125
+ end
126
+ end
127
+
128
+
89
129
  end
90
130
  end
91
131
  end