neo4j 1.0.0.beta.21-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.
- 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
|
+
|