neo4j 1.0.0.beta.21-java
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +141 -0
- data/CONTRIBUTORS +17 -0
- data/Gemfile +16 -0
- data/README.rdoc +135 -0
- data/lib/generators/neo4j.rb +65 -0
- data/lib/generators/neo4j/model/model_generator.rb +39 -0
- data/lib/generators/neo4j/model/templates/model.erb +7 -0
- data/lib/neo4j.rb +77 -0
- data/lib/neo4j/config.rb +153 -0
- data/lib/neo4j/database.rb +56 -0
- data/lib/neo4j/equal.rb +21 -0
- data/lib/neo4j/event_handler.rb +116 -0
- data/lib/neo4j/index/class_methods.rb +62 -0
- data/lib/neo4j/index/index.rb +33 -0
- data/lib/neo4j/index/indexer.rb +312 -0
- data/lib/neo4j/index/indexer_registry.rb +68 -0
- data/lib/neo4j/index/lucene_query.rb +191 -0
- data/lib/neo4j/jars/geronimo-jta_1.1_spec-1.1.1.jar +0 -0
- data/lib/neo4j/jars/lucene-core-3.0.2.jar +0 -0
- data/lib/neo4j/jars/neo4j-index-1.2-1.2.M03.jar +0 -0
- data/lib/neo4j/jars/neo4j-kernel-1.2-1.2.M03.jar +0 -0
- data/lib/neo4j/jars/neo4j-lucene-index-0.2-1.2.M03.jar +0 -0
- data/lib/neo4j/load.rb +21 -0
- data/lib/neo4j/mapping/class_methods/init_node.rb +50 -0
- data/lib/neo4j/mapping/class_methods/init_rel.rb +35 -0
- data/lib/neo4j/mapping/class_methods/list.rb +13 -0
- data/lib/neo4j/mapping/class_methods/property.rb +82 -0
- data/lib/neo4j/mapping/class_methods/relationship.rb +91 -0
- data/lib/neo4j/mapping/class_methods/rule.rb +295 -0
- data/lib/neo4j/mapping/decl_relationship_dsl.rb +214 -0
- data/lib/neo4j/mapping/has_list.rb +134 -0
- data/lib/neo4j/mapping/has_n.rb +83 -0
- data/lib/neo4j/mapping/node_mixin.rb +112 -0
- data/lib/neo4j/mapping/relationship_mixin.rb +120 -0
- data/lib/neo4j/model.rb +4 -0
- data/lib/neo4j/neo4j.rb +95 -0
- data/lib/neo4j/node.rb +131 -0
- data/lib/neo4j/node_mixin.rb +4 -0
- data/lib/neo4j/node_relationship.rb +149 -0
- data/lib/neo4j/node_traverser.rb +157 -0
- data/lib/neo4j/property.rb +111 -0
- data/lib/neo4j/rails/attributes.rb +155 -0
- data/lib/neo4j/rails/callbacks.rb +34 -0
- data/lib/neo4j/rails/finders.rb +134 -0
- data/lib/neo4j/rails/lucene_connection_closer.rb +19 -0
- data/lib/neo4j/rails/mapping/property.rb +60 -0
- data/lib/neo4j/rails/model.rb +105 -0
- data/lib/neo4j/rails/persistence.rb +260 -0
- data/lib/neo4j/rails/railtie.rb +21 -0
- data/lib/neo4j/rails/relationships/mapper.rb +96 -0
- data/lib/neo4j/rails/relationships/relationship.rb +30 -0
- data/lib/neo4j/rails/relationships/relationships.rb +60 -0
- data/lib/neo4j/rails/serialization.rb +25 -0
- data/lib/neo4j/rails/timestamps.rb +65 -0
- data/lib/neo4j/rails/transaction.rb +67 -0
- data/lib/neo4j/rails/tx_methods.rb +15 -0
- data/lib/neo4j/rails/validations.rb +38 -0
- data/lib/neo4j/rails/validations/non_nil.rb +11 -0
- data/lib/neo4j/rails/validations/uniqueness.rb +37 -0
- data/lib/neo4j/relationship.rb +169 -0
- data/lib/neo4j/relationship_mixin.rb +4 -0
- data/lib/neo4j/relationship_traverser.rb +92 -0
- data/lib/neo4j/to_java.rb +31 -0
- data/lib/neo4j/transaction.rb +68 -0
- data/lib/neo4j/type_converters.rb +117 -0
- data/lib/neo4j/version.rb +3 -0
- data/lib/orm_adapter/adapters/neo4j.rb +55 -0
- data/lib/tmp/neo4j/active_tx_log +1 -0
- data/lib/tmp/neo4j/index/lucene-store.db +0 -0
- data/lib/tmp/neo4j/index/lucene.log.active +0 -0
- data/lib/tmp/neo4j/lucene-fulltext/lucene-store.db +0 -0
- data/lib/tmp/neo4j/lucene-fulltext/lucene.log.active +0 -0
- data/lib/tmp/neo4j/lucene/lucene-store.db +0 -0
- data/lib/tmp/neo4j/lucene/lucene.log.active +0 -0
- data/lib/tmp/neo4j/messages.log +85 -0
- data/lib/tmp/neo4j/neostore +0 -0
- data/lib/tmp/neo4j/neostore.id +0 -0
- data/lib/tmp/neo4j/neostore.nodestore.db +0 -0
- data/lib/tmp/neo4j/neostore.nodestore.db.id +0 -0
- data/lib/tmp/neo4j/neostore.propertystore.db +0 -0
- data/lib/tmp/neo4j/neostore.propertystore.db.arrays +0 -0
- data/lib/tmp/neo4j/neostore.propertystore.db.arrays.id +0 -0
- data/lib/tmp/neo4j/neostore.propertystore.db.id +0 -0
- data/lib/tmp/neo4j/neostore.propertystore.db.index +0 -0
- data/lib/tmp/neo4j/neostore.propertystore.db.index.id +0 -0
- data/lib/tmp/neo4j/neostore.propertystore.db.index.keys +0 -0
- data/lib/tmp/neo4j/neostore.propertystore.db.index.keys.id +0 -0
- data/lib/tmp/neo4j/neostore.propertystore.db.strings +0 -0
- data/lib/tmp/neo4j/neostore.propertystore.db.strings.id +0 -0
- data/lib/tmp/neo4j/neostore.relationshipstore.db +0 -0
- data/lib/tmp/neo4j/neostore.relationshipstore.db.id +0 -0
- data/lib/tmp/neo4j/neostore.relationshiptypestore.db +0 -0
- data/lib/tmp/neo4j/neostore.relationshiptypestore.db.id +0 -0
- data/lib/tmp/neo4j/neostore.relationshiptypestore.db.names +0 -0
- data/lib/tmp/neo4j/neostore.relationshiptypestore.db.names.id +0 -0
- data/lib/tmp/neo4j/nioneo_logical.log.active +0 -0
- data/lib/tmp/neo4j/tm_tx_log.1 +0 -0
- data/neo4j.gemspec +31 -0
- metadata +216 -0
@@ -0,0 +1,134 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Rails
|
3
|
+
module Finders
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
rule :_all
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
# overwrite the index method to add find_by_xxx class methods
|
12
|
+
def index(*args)
|
13
|
+
field = args.first
|
14
|
+
module_eval <<-RUBY, __FILE__, __LINE__
|
15
|
+
def self.all_by_#{field}(value)
|
16
|
+
find_with_indexer("#{field}: \\"\#{value}\\"")
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.find_by_#{field}(value)
|
20
|
+
all_by_#{field}(value).first
|
21
|
+
end
|
22
|
+
RUBY
|
23
|
+
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
# load an id or array of ids from the database
|
28
|
+
def load(*ids)
|
29
|
+
result = ids.map { |id| Neo4j::Node.load(id) }
|
30
|
+
if ids.length == 1
|
31
|
+
result.first
|
32
|
+
else
|
33
|
+
result
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Behave like the ActiveRecord query interface
|
38
|
+
|
39
|
+
# Handle Model.find(params[:id])
|
40
|
+
|
41
|
+
# Model.find
|
42
|
+
# Model.find(:first)
|
43
|
+
|
44
|
+
# Model.find("1")
|
45
|
+
# Model.find(1)
|
46
|
+
|
47
|
+
# Model.find("name: test")
|
48
|
+
# Model.find(:name => "test")
|
49
|
+
|
50
|
+
# Model.find(:first, "name: test")
|
51
|
+
# Model.find(:first, { :name => "test" })
|
52
|
+
|
53
|
+
# Model.find(:first, :conditions => "name: test")
|
54
|
+
# Model.find(:first, :conditions => { :name => "test" })
|
55
|
+
|
56
|
+
# Model.find(:all, "name: test")
|
57
|
+
# Model.find(:all, { :name => "test" })
|
58
|
+
|
59
|
+
# Model.find(:all, :conditions => "name: test")
|
60
|
+
# Model.find(:all, :conditions => { :name => "test" })
|
61
|
+
def find(*args)
|
62
|
+
case args.first
|
63
|
+
when :all, :first
|
64
|
+
kind = args.shift
|
65
|
+
send(kind, *args)
|
66
|
+
else
|
67
|
+
find_with_ids(*args) or first(*args)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def all(*args)
|
72
|
+
args = normalize_args(*args)
|
73
|
+
if args.empty?
|
74
|
+
# use the _all rule to recover all the stored instances of this node
|
75
|
+
_all
|
76
|
+
else
|
77
|
+
# handle the special case of a search by id
|
78
|
+
if args.first.is_a?(Hash) && args.first[:id]
|
79
|
+
[find_with_ids(args.first[:id])].flatten
|
80
|
+
else
|
81
|
+
find_with_indexer(*args)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def first(*args)
|
87
|
+
all(*args).first
|
88
|
+
end
|
89
|
+
|
90
|
+
def last(*args)
|
91
|
+
a = all(*args)
|
92
|
+
a.empty? ? nil : a[a.size - 1]
|
93
|
+
end
|
94
|
+
|
95
|
+
def count
|
96
|
+
all.size
|
97
|
+
end
|
98
|
+
|
99
|
+
protected
|
100
|
+
def find_with_ids(*args)
|
101
|
+
if ((args.first.is_a?(String) || args.first.is_a?(Integer)) && args.first.to_i > 0)
|
102
|
+
load(*args.map { |p| p.to_i })
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def find_with_indexer(*args)
|
107
|
+
hits = _indexer.find(*args)
|
108
|
+
# We need to save this so that the Rack Neo4j::Rails:LuceneConnection::Closer can close it
|
109
|
+
Thread.current[:neo4j_lucene_connection] ||= []
|
110
|
+
Thread.current[:neo4j_lucene_connection] << hits
|
111
|
+
hits
|
112
|
+
end
|
113
|
+
|
114
|
+
def normalize_args(*args)
|
115
|
+
options = args.extract_options!
|
116
|
+
|
117
|
+
if options.present?
|
118
|
+
|
119
|
+
# TODO: Handle order
|
120
|
+
options.delete(:order)
|
121
|
+
|
122
|
+
if options[:conditions]
|
123
|
+
args << options[:conditions] if options[:conditions].present?
|
124
|
+
else
|
125
|
+
args << options if options.present?
|
126
|
+
end
|
127
|
+
end
|
128
|
+
args
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Rails
|
3
|
+
class LuceneConnectionCloser
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
@app.call(env)
|
10
|
+
ensure
|
11
|
+
Thread.current[:neo4j_lucene_connection].each {|hits| hits.close} if Thread.current[:neo4j_lucene_connection]
|
12
|
+
Thread.current[:neo4j_lucene_connection] = nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
Thread.current
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Rails
|
3
|
+
module Mapping
|
4
|
+
module Property
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# Handles options for the property
|
9
|
+
#
|
10
|
+
# Set the property type :type => Time
|
11
|
+
# Set a default :default => "default"
|
12
|
+
# Property must be there :null => false
|
13
|
+
# Property has a length limit :limit => 128
|
14
|
+
def property(*args)
|
15
|
+
options = args.extract_options!
|
16
|
+
args.each do |property_sym|
|
17
|
+
property_setup(property_sym, options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
def property_setup(property, options)
|
23
|
+
_decl_props[property] = options
|
24
|
+
handle_property_options_for(property, options)
|
25
|
+
define_property_methods_for(property, options)
|
26
|
+
_decl_props[property][:defined] = true
|
27
|
+
end
|
28
|
+
|
29
|
+
def handle_property_options_for(property, options)
|
30
|
+
attribute_defaults[property.to_s] = options[:default] if options.has_key?(:default)
|
31
|
+
|
32
|
+
if options.has_key?(:null) && options[:null] === false
|
33
|
+
validates(property, :non_nil => true, :on => :create)
|
34
|
+
validates(property, :non_nil => true, :on => :update)
|
35
|
+
end
|
36
|
+
validates(property, :length => { :maximum => options[:limit] }) if options[:limit]
|
37
|
+
end
|
38
|
+
|
39
|
+
def define_property_methods_for(property, options)
|
40
|
+
unless method_defined?(property)
|
41
|
+
class_eval <<-RUBY, __FILE__, __LINE__
|
42
|
+
def #{property}
|
43
|
+
send(:[], "#{property}")
|
44
|
+
end
|
45
|
+
RUBY
|
46
|
+
end
|
47
|
+
|
48
|
+
unless method_defined?("#{property}=".to_sym)
|
49
|
+
class_eval <<-RUBY, __FILE__, __LINE__
|
50
|
+
def #{property}=(value)
|
51
|
+
send(:[]=, "#{property}", value)
|
52
|
+
end
|
53
|
+
RUBY
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Rails
|
3
|
+
class Model
|
4
|
+
include Neo4j::NodeMixin
|
5
|
+
|
6
|
+
# Initialize a Node with a set of properties (or empty if nothing is passed)
|
7
|
+
def initialize(attributes = {})
|
8
|
+
reset_attributes
|
9
|
+
clear_relationships
|
10
|
+
self.attributes = attributes
|
11
|
+
end
|
12
|
+
|
13
|
+
def id
|
14
|
+
neo_id.nil? ? nil : neo_id.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_param
|
18
|
+
persisted? ? neo_id.to_s : nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns an Enumerable of all (primary) key attributes
|
22
|
+
# or nil if model.persisted? is false
|
23
|
+
def to_key
|
24
|
+
persisted? ? [id] : nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def reject_if?(proc_or_symbol, attr)
|
28
|
+
return false if proc_or_symbol.nil?
|
29
|
+
if proc_or_symbol.is_a?(Symbol)
|
30
|
+
meth = method(proc_or_symbol)
|
31
|
+
meth.arity == 0 ? meth.call : meth.call(attr)
|
32
|
+
else
|
33
|
+
proc_or_symbol.call(attr)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_model
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def ==(other)
|
42
|
+
new? ? self.__id__ == other.__id__ : @_java_node == (other)
|
43
|
+
end
|
44
|
+
|
45
|
+
# --------------------------------------
|
46
|
+
# Public Class Methods
|
47
|
+
# --------------------------------------
|
48
|
+
class << self
|
49
|
+
# NodeMixin overwrites the #new class method but it saves it as orig_new
|
50
|
+
# Here, we just get it back to normal
|
51
|
+
alias :new :orig_new
|
52
|
+
|
53
|
+
def transaction(&block)
|
54
|
+
Neo4j::Rails::Transaction.run do |tx|
|
55
|
+
block.call(tx)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def accepts_nested_attributes_for(*attr_names)
|
60
|
+
options = attr_names.pop if attr_names[-1].is_a?(Hash)
|
61
|
+
|
62
|
+
attr_names.each do |association_name|
|
63
|
+
# Do some validation that we have defined the relationships we want to nest
|
64
|
+
rel = self._decl_rels[association_name.to_sym]
|
65
|
+
raise "No relationship declared with has_n or has_one with type #{association_name}" unless rel
|
66
|
+
raise "Can't use accepts_nested_attributes_for(#{association_name}) since it has not defined which class it has a relationship to, use has_n(#{association_name}).to(MyOtherClass)" unless rel.target_class
|
67
|
+
|
68
|
+
if rel.has_one?
|
69
|
+
send(:define_method, "#{association_name}_attributes=") do |attributes|
|
70
|
+
update_nested_attributes(association_name.to_sym, attributes, options)
|
71
|
+
end
|
72
|
+
else
|
73
|
+
send(:define_method, "#{association_name}_attributes=") do |attributes|
|
74
|
+
if attributes.is_a?(Array)
|
75
|
+
attributes.each do |attr|
|
76
|
+
update_nested_attributes(association_name.to_sym, attr, options)
|
77
|
+
end
|
78
|
+
else
|
79
|
+
attributes.each_value do |attr|
|
80
|
+
update_nested_attributes(association_name.to_sym, attr, options)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
Model.class_eval do
|
92
|
+
extend ActiveModel::Translation
|
93
|
+
|
94
|
+
include Persistence # handles how to save, create and update the model
|
95
|
+
include Attributes # handles how to save and retrieve attributes
|
96
|
+
include Mapping::Property # allows some additional options on the #property class method
|
97
|
+
include Serialization # enable to_xml and to_json
|
98
|
+
include Timestamps # handle created_at, updated_at timestamp properties
|
99
|
+
include Validations # enable validations
|
100
|
+
include Callbacks # enable callbacks
|
101
|
+
include Finders # ActiveRecord style find
|
102
|
+
include Relationships # for none persisted relationships
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,260 @@
|
|
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
|
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
|
+
def save(*)
|
15
|
+
create_or_update
|
16
|
+
end
|
17
|
+
|
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.
|
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?()
|
56
|
+
@_deleted
|
57
|
+
end
|
58
|
+
|
59
|
+
# Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved.
|
60
|
+
# If saving fails because the resource is invalid then false will be returned.
|
61
|
+
def update_attributes(attributes)
|
62
|
+
self.attributes = attributes
|
63
|
+
save
|
64
|
+
end
|
65
|
+
|
66
|
+
# Same as #update_attributes, but raises an exception if saving fails.
|
67
|
+
def update_attributes!(attributes)
|
68
|
+
self.attributes = attributes
|
69
|
+
save!
|
70
|
+
end
|
71
|
+
|
72
|
+
# Reload the object from the DB.
|
73
|
+
def reload(options = nil)
|
74
|
+
clear_changes
|
75
|
+
clear_relationships
|
76
|
+
reset_attributes
|
77
|
+
unless reload_from_database
|
78
|
+
set_deleted_properties
|
79
|
+
freeze
|
80
|
+
end
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns if the record is persisted, i.e. it’s not a new record and it was not destroyed
|
85
|
+
def persisted?
|
86
|
+
!new_record? && !destroyed?
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns true if the record hasn't been saved to Neo4j yet.
|
90
|
+
def new_record?
|
91
|
+
_java_node.nil?
|
92
|
+
end
|
93
|
+
|
94
|
+
alias :new? :new_record?
|
95
|
+
|
96
|
+
# Freeze the properties hash.
|
97
|
+
def freeze
|
98
|
+
@properties.freeze; self
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns +true+ if the properties hash has been frozen.
|
102
|
+
def frozen?
|
103
|
+
reload
|
104
|
+
@properties.frozen?
|
105
|
+
end
|
106
|
+
|
107
|
+
module ClassMethods
|
108
|
+
# Initialize a model and set a bunch of attributes at the same time. Returns
|
109
|
+
# the object whether saved successfully or not.
|
110
|
+
def create(*args)
|
111
|
+
new(*args).tap { |o| o.save }
|
112
|
+
end
|
113
|
+
|
114
|
+
# Same as #create, but raises an error if there is a problem during save.
|
115
|
+
# Returns the object whether saved successfully or not.
|
116
|
+
def create!(*args)
|
117
|
+
new(*args).tap { |o| o.save! }
|
118
|
+
end
|
119
|
+
|
120
|
+
# Destroy each node in turn. Runs the destroy callbacks for each node.
|
121
|
+
def destroy_all
|
122
|
+
all.each do |n|
|
123
|
+
n.destroy
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
protected
|
129
|
+
def create_or_update
|
130
|
+
result = persisted? ? update : create
|
131
|
+
unless result != false
|
132
|
+
Neo4j::Rails::Transaction.fail if Neo4j::Rails::Transaction.running?
|
133
|
+
false
|
134
|
+
else
|
135
|
+
true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def update
|
140
|
+
write_changed_attributes
|
141
|
+
clear_changes
|
142
|
+
clear_relationships
|
143
|
+
true
|
144
|
+
end
|
145
|
+
|
146
|
+
def create
|
147
|
+
node = Neo4j::Node.new
|
148
|
+
#unless _java_node.save_nested(node)
|
149
|
+
# Neo4j::Rails::Transaction.fail
|
150
|
+
# false
|
151
|
+
#else
|
152
|
+
init_on_load(node)
|
153
|
+
init_on_create
|
154
|
+
clear_changes
|
155
|
+
clear_relationships
|
156
|
+
true
|
157
|
+
end
|
158
|
+
|
159
|
+
def init_on_create(*)
|
160
|
+
self["_classname"] = self.class.to_s
|
161
|
+
write_default_attributes
|
162
|
+
write_changed_attributes
|
163
|
+
write_changed_relationships
|
164
|
+
end
|
165
|
+
|
166
|
+
def reset_attributes
|
167
|
+
@properties = {}
|
168
|
+
end
|
169
|
+
|
170
|
+
def reload_from_database
|
171
|
+
if reloaded = self.class.load(id)
|
172
|
+
send(:attributes=, reloaded.attributes, false)
|
173
|
+
end
|
174
|
+
reloaded
|
175
|
+
end
|
176
|
+
|
177
|
+
def set_deleted_properties
|
178
|
+
@_deleted = true
|
179
|
+
@_persisted = false
|
180
|
+
@_java_node = nil
|
181
|
+
end
|
182
|
+
|
183
|
+
# Ensure any defaults are stored in the DB
|
184
|
+
def write_default_attributes
|
185
|
+
attribute_defaults.each do |attribute, value|
|
186
|
+
write_attribute(attribute, Neo4j::TypeConverters.convert(value, attribute, self.class)) unless changed_attributes.has_key?(attribute) || _java_node.has_property?(attribute)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Write attributes to the Neo4j DB only if they're altered
|
191
|
+
def write_changed_attributes
|
192
|
+
@properties.each do |attribute, value|
|
193
|
+
write_attribute(attribute, value) if changed_attributes.has_key?(attribute)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def _add_relationship(rel_type, attr)
|
198
|
+
clazz = self.class._decl_rels[rel_type.to_sym].target_class
|
199
|
+
node = clazz.new(attr)
|
200
|
+
if respond_to?("#{rel_type}=")
|
201
|
+
send("#{rel_type}=", node)
|
202
|
+
elsif respond_to?("#{rel_type}")
|
203
|
+
has_n = send("#{rel_type}")
|
204
|
+
has_n << node
|
205
|
+
else
|
206
|
+
raise "oops #{rel_type}"
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def _find_node(rel_type, id)
|
211
|
+
if respond_to?("#{rel_type}=")
|
212
|
+
send("#{rel_type}")
|
213
|
+
elsif respond_to?("#{rel_type}")
|
214
|
+
has_n = send("#{rel_type}")
|
215
|
+
has_n.find { |n| n.id == id }
|
216
|
+
else
|
217
|
+
raise "oops #{rel_type}"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def update_nested_attributes(rel_type, attr, options)
|
222
|
+
allow_destroy, reject_if = [options[:allow_destroy], options[:reject_if]] if options
|
223
|
+
|
224
|
+
if new?
|
225
|
+
# We are updating a node that was created with the 'new' method.
|
226
|
+
# The relationship will only be kept in the Value object.
|
227
|
+
_add_relationship(rel_type, attr) unless reject_if?(reject_if, attr) || (allow_destroy && attr[:_destroy] && attr[:_destroy] != '0')
|
228
|
+
else
|
229
|
+
# We have a node that was created with the #create method - has real Neo4j relationships
|
230
|
+
# does it exist ?
|
231
|
+
found = _find_node(rel_type, attr[:id])
|
232
|
+
|
233
|
+
# Check if we want to destroy not found nodes (e.g. {..., :_destroy => '1' } ?
|
234
|
+
destroy = attr[:_destroy] && attr[:_destroy] != '0'
|
235
|
+
|
236
|
+
if found
|
237
|
+
if destroy
|
238
|
+
found.destroy if allow_destroy
|
239
|
+
else
|
240
|
+
found.update_attributes(attr) # it already exist, so update that one
|
241
|
+
end
|
242
|
+
elsif !destroy && !reject_if?(reject_if, attr)
|
243
|
+
_add_relationship(rel_type, attr)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
public
|
249
|
+
class RecordInvalidError < RuntimeError
|
250
|
+
attr_reader :record
|
251
|
+
|
252
|
+
def initialize(record)
|
253
|
+
@record = record
|
254
|
+
super(@record.errors.full_messages.join(", "))
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|