neo4j 1.0.0.beta.11 → 1.0.0.beta.12
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/lib/neo4j.rb +2 -0
- data/lib/neo4j/mapping/class_methods/relationship.rb +17 -19
- data/lib/neo4j/mapping/decl_relationship_dsl.rb +36 -0
- data/lib/neo4j/mapping/has_n.rb +13 -50
- data/lib/neo4j/mapping/node_mixin.rb +5 -1
- data/lib/neo4j/model.rb +4 -0
- data/lib/neo4j/node_traverser.rb +2 -5
- data/lib/neo4j/rails/model.rb +272 -287
- data/lib/neo4j/rails/tx_methods.rb +12 -8
- data/lib/neo4j/rails/value.rb +69 -16
- data/lib/neo4j/relationship_traverser.rb +1 -4
- data/lib/neo4j/version.rb +1 -1
- metadata +4 -3
data/lib/neo4j.rb
CHANGED
@@ -2,6 +2,7 @@ module Neo4j::Mapping
|
|
2
2
|
module ClassMethods
|
3
3
|
|
4
4
|
module Relationship
|
5
|
+
include Neo4j::ToJava
|
5
6
|
|
6
7
|
# Specifies a relationship between two node classes.
|
7
8
|
# Generates assignment and accessor methods for the given relationship.
|
@@ -24,9 +25,9 @@ module Neo4j::Mapping
|
|
24
25
|
def has_n(rel_type, params = {})
|
25
26
|
clazz = self
|
26
27
|
module_eval(%Q{
|
27
|
-
def #{rel_type}
|
28
|
+
def #{rel_type}
|
28
29
|
dsl = #{clazz}._decl_rels[:'#{rel_type.to_s}']
|
29
|
-
Neo4j::Mapping::HasN.new(self, dsl
|
30
|
+
Neo4j::Mapping::HasN.new(self, dsl)
|
30
31
|
end}, __FILE__, __LINE__)
|
31
32
|
|
32
33
|
module_eval(%Q{
|
@@ -61,28 +62,25 @@ module Neo4j::Mapping
|
|
61
62
|
#
|
62
63
|
def has_one(rel_type, params = {})
|
63
64
|
clazz = self
|
64
|
-
|
65
|
-
|
66
65
|
module_eval(%Q{def #{rel_type}=(value)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end}, __FILE__, __LINE__)
|
66
|
+
dsl = #{clazz}._decl_rels[:'#{rel_type.to_s}']
|
67
|
+
rel = dsl.single_relationship(self)
|
68
|
+
rel.del unless rel.nil?
|
69
|
+
dsl.create_relationship_to(self, value)
|
70
|
+
end}, __FILE__, __LINE__)
|
73
71
|
|
74
72
|
module_eval(%Q{def #{rel_type}
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end}, __FILE__, __LINE__)
|
73
|
+
dsl = #{clazz}._decl_rels[:'#{rel_type.to_s}']
|
74
|
+
dsl.single_relationship(self)
|
75
|
+
end}, __FILE__, __LINE__)
|
79
76
|
|
77
|
+
# TODO
|
80
78
|
module_eval(%Q{
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
79
|
+
def #{rel_type}_rel
|
80
|
+
dsl = #{clazz}._decl_rels[:'#{rel_type.to_s}']
|
81
|
+
r = Neo4j::Mapping::HasN.new(self, dsl).rels
|
82
|
+
[*r][0]
|
83
|
+
end}, __FILE__, __LINE__)
|
86
84
|
|
87
85
|
_decl_rels[rel_type.to_sym] = Neo4j::Mapping::DeclRelationshipDsl.new(rel_type, true, params)
|
88
86
|
end
|
@@ -31,6 +31,7 @@ module Neo4j::Mapping
|
|
31
31
|
# <b>File#folder</b> :: for accessing nodes from relationship 'files' from the outgoing Folder node
|
32
32
|
#
|
33
33
|
class DeclRelationshipDsl
|
34
|
+
include Neo4j::ToJava
|
34
35
|
|
35
36
|
attr_reader :to_type, :to_class, :cascade_delete_prop_name, :counter, :rel_id, :direction
|
36
37
|
CASCADE_DELETE_PROP_NAMES = {:outgoing => :_cascade_delete_outgoing, :incoming => :_cascade_delete_incoming}
|
@@ -75,6 +76,41 @@ module Neo4j::Mapping
|
|
75
76
|
@to_class.nil? ? @to_type.to_s : "#{@to_class.to_s}##{@to_type.to_s}"
|
76
77
|
end
|
77
78
|
|
79
|
+
def each_node(node, direction, &block)
|
80
|
+
type = type_to_java(namespace_type)
|
81
|
+
dir = dir_to_java(direction)
|
82
|
+
node._java_node.getRelationships(type, dir).each do |rel|
|
83
|
+
other = rel.getOtherNode(node).wrapper
|
84
|
+
block.call other
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def single_relationship(node)
|
89
|
+
type = type_to_java(namespace_type)
|
90
|
+
dir = dir_to_java(@direction)
|
91
|
+
rel = node._java_node.getSingleRelationship(type, dir)
|
92
|
+
rel.nil? ? nil : rel.end_node.wrapper
|
93
|
+
end
|
94
|
+
|
95
|
+
def create_relationship_to(node, other)
|
96
|
+
# If the are creating an incoming relationship we need to swap incoming and outgoing nodes
|
97
|
+
if @direction == :outgoing
|
98
|
+
from, to = node, other
|
99
|
+
else
|
100
|
+
from, to = other, node
|
101
|
+
end
|
102
|
+
|
103
|
+
java_type = type_to_java(namespace_type)
|
104
|
+
rel = from._java_node.create_relationship_to(to._java_node, java_type)
|
105
|
+
rel[:_classname] = relationship_class.to_s if relationship_class
|
106
|
+
|
107
|
+
# TODO - not implemented yet
|
108
|
+
# the from.neo_id is only used for cascade_delete_incoming since that node will be deleted when all the list items has been deleted.
|
109
|
+
# if cascade_delete_outgoing all nodes will be deleted when the root node is deleted
|
110
|
+
# if cascade_delete_incoming then the root node will be deleted when all root nodes' outgoing nodes are deleted
|
111
|
+
#rel[@dsl.cascade_delete_prop_name] = node.neo_id if @dsl.cascade_delete?
|
112
|
+
rel.wrapper
|
113
|
+
end
|
78
114
|
|
79
115
|
# Specifies an outgoing relationship.
|
80
116
|
# The name of the outgoing class will be used as a prefix for the relationship used.
|
data/lib/neo4j/mapping/has_n.rb
CHANGED
@@ -11,7 +11,7 @@ module Neo4j
|
|
11
11
|
def initialize(node, dsl) # :nodoc:
|
12
12
|
@node = node
|
13
13
|
@direction = dsl.direction
|
14
|
-
# returns the other DSL if it exists otherwise use this DSL for
|
14
|
+
# returns the other DSL if it exists otherwise use this DSL for specifying incoming relationships
|
15
15
|
if @direction == :outgoing
|
16
16
|
@dsl = dsl
|
17
17
|
else
|
@@ -20,8 +20,10 @@ module Neo4j
|
|
20
20
|
@dsl = clazz._decl_rels[dsl.to_type]
|
21
21
|
raise "Unspecified outgoing relationship '#{dsl.to_type}' for incoming relationship '#{dsl.rel_id}' on class #{clazz}" if @dsl.nil?
|
22
22
|
end
|
23
|
+
end
|
23
24
|
|
24
|
-
|
25
|
+
def to_s
|
26
|
+
"HasN [#@direction, #{@node.neo_id} #{@dsl.namespace_type}]"
|
25
27
|
end
|
26
28
|
|
27
29
|
def size
|
@@ -41,6 +43,12 @@ module Neo4j
|
|
41
43
|
super
|
42
44
|
end
|
43
45
|
|
46
|
+
# Required by the Enumerable mixin.
|
47
|
+
def each(&block)
|
48
|
+
@dsl.each_node(@node, @direction, &block)
|
49
|
+
end
|
50
|
+
|
51
|
+
|
44
52
|
# Returns the relationships instead of the nodes.
|
45
53
|
#
|
46
54
|
# ==== Example
|
@@ -51,39 +59,16 @@ module Neo4j
|
|
51
59
|
Neo4j::RelationshipTraverser.new(@node._java_node, [@dsl.namespace_type], @direction)
|
52
60
|
end
|
53
61
|
|
54
|
-
# Sets the depth of the traversal.
|
55
|
-
# Default is 1 if not specified.
|
56
|
-
#
|
57
|
-
# ==== Example
|
58
|
-
# morpheus.friends.depth(:all).each { ... }
|
59
|
-
# morpheus.friends.depth(3).each { ... }
|
60
|
-
#
|
61
|
-
# ==== Arguments
|
62
|
-
# d:: the depth or :all if traversing to the end of the network (symbol or fixnum)
|
63
|
-
# ==== Return
|
64
|
-
# self
|
65
|
-
#
|
66
|
-
def depth(d)
|
67
|
-
@traverser.depth(d)
|
68
|
-
self
|
69
|
-
end
|
70
|
-
|
71
|
-
# Required by the Enumerable mixin.
|
72
|
-
def each(&block)
|
73
|
-
@traverser.each(&block)
|
74
|
-
end
|
75
|
-
|
76
|
-
|
77
62
|
# Returns true if there are no node in this type of relationship
|
78
63
|
def empty?
|
79
|
-
|
64
|
+
first != nil
|
80
65
|
end
|
81
66
|
|
82
67
|
|
83
68
|
# Creates a relationship instance between this and the other node.
|
84
69
|
# Returns the relationship object
|
85
70
|
def new(other)
|
86
|
-
|
71
|
+
@dsl.create_relationship_to(@node, other)
|
87
72
|
end
|
88
73
|
|
89
74
|
|
@@ -101,31 +86,9 @@ module Neo4j
|
|
101
86
|
# self
|
102
87
|
#
|
103
88
|
def <<(other)
|
104
|
-
|
89
|
+
@dsl.create_relationship_to(@node, other)
|
105
90
|
self
|
106
91
|
end
|
107
|
-
|
108
|
-
|
109
|
-
def create_rel(node, other) # :nodoc:
|
110
|
-
# If the are creating an incoming relationship we need to swap incoming and outgoing nodes
|
111
|
-
if @direction == :outgoing
|
112
|
-
from, to = node, other
|
113
|
-
else
|
114
|
-
from, to = other, node
|
115
|
-
end
|
116
|
-
|
117
|
-
java_type = type_to_java(@dsl.namespace_type)
|
118
|
-
rel = from._java_node.create_relationship_to(to._java_node, java_type)
|
119
|
-
rel[:_classname] = @dsl.relationship_class.to_s if @dsl.relationship_class
|
120
|
-
|
121
|
-
# TODO - not implemented yet
|
122
|
-
# the from.neo_id is only used for cascade_delete_incoming since that node will be deleted when all the list items has been deleted.
|
123
|
-
# if cascade_delete_outgoing all nodes will be deleted when the root node is deleted
|
124
|
-
# if cascade_delete_incoming then the root node will be deleted when all root nodes' outgoing nodes are deleted
|
125
|
-
#rel[@dsl.cascade_delete_prop_name] = node.neo_id if @dsl.cascade_delete?
|
126
|
-
rel.wrapper
|
127
|
-
end
|
128
|
-
|
129
92
|
end
|
130
93
|
|
131
94
|
end
|
@@ -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
|
9
|
+
:equal?, :eql?, :==, :exist?, :getRelationships, :getSingleRelationship
|
10
10
|
|
11
11
|
|
12
12
|
# --------------------------------------------------------------------------
|
@@ -40,6 +40,10 @@ module Neo4j::Mapping
|
|
40
40
|
end
|
41
41
|
|
42
42
|
|
43
|
+
def wrapper
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
43
47
|
def self.included(c) # :nodoc:
|
44
48
|
c.instance_eval do
|
45
49
|
class << self
|
data/lib/neo4j/model.rb
ADDED
data/lib/neo4j/node_traverser.rb
CHANGED
@@ -132,10 +132,7 @@ module Neo4j
|
|
132
132
|
end
|
133
133
|
|
134
134
|
def each
|
135
|
-
|
136
|
-
while (iter.hasNext) do
|
137
|
-
yield iter.next.wrapper
|
138
|
-
end
|
135
|
+
iterator.each {|i| yield i.wrapper}
|
139
136
|
end
|
140
137
|
|
141
138
|
def iterator
|
@@ -147,7 +144,7 @@ module Neo4j
|
|
147
144
|
end
|
148
145
|
end
|
149
146
|
@td = @td.prune(org.neo4j.kernel.Traversal.pruneAfterDepth( @depth ) ) unless @depth == :all
|
150
|
-
@td.traverse(@from).nodes
|
147
|
+
@td.traverse(@from).nodes
|
151
148
|
end
|
152
149
|
end
|
153
150
|
|
data/lib/neo4j/rails/model.rb
CHANGED
@@ -1,358 +1,343 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
module Neo4j
|
2
|
+
module Rails
|
3
|
+
class Model
|
4
|
+
include Neo4j::NodeMixin
|
5
|
+
include ActiveModel::Validations
|
6
|
+
include ActiveModel::Dirty
|
7
|
+
include ActiveModel::MassAssignmentSecurity
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
extend ActiveModel::Naming
|
10
|
+
extend ActiveModel::Callbacks
|
11
|
+
extend Neo4j::Validations::ClassMethods
|
12
|
+
extend TxMethods
|
13
|
+
# extend ClassMethods::Relationship
|
11
14
|
|
12
|
-
|
15
|
+
define_model_callbacks :create, :save, :update, :destroy
|
13
16
|
|
14
17
|
|
15
|
-
|
18
|
+
UniquenessValidator = Neo4j::Validations::UniquenessValidator
|
16
19
|
|
17
|
-
|
18
|
-
|
20
|
+
class RecordInvalidError < RuntimeError
|
21
|
+
attr_reader :record
|
19
22
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
def initialize(record)
|
24
|
+
@record = record
|
25
|
+
super(@record.errors.full_messages.join(", "))
|
26
|
+
end
|
27
|
+
end
|
25
28
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
+
# --------------------------------------
|
30
|
+
# Initialize
|
31
|
+
# --------------------------------------
|
29
32
|
|
30
|
-
|
31
|
-
|
33
|
+
def initialize(*)
|
34
|
+
end
|
32
35
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
def init_on_create(*args) # :nodoc:
|
37
|
+
super()
|
38
|
+
self.attributes=args[0] if args[0].respond_to?(:each_pair)
|
39
|
+
@_created_record = true
|
40
|
+
end
|
38
41
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
+
# --------------------------------------
|
43
|
+
# Identity
|
44
|
+
# --------------------------------------
|
42
45
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
+
def id
|
47
|
+
neo_id.nil? ? nil : neo_id.to_s
|
48
|
+
end
|
46
49
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
+
def to_param
|
51
|
+
persisted? ? neo_id.to_s : nil
|
52
|
+
end
|
50
53
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
54
|
+
# Returns an Enumerable of all (primary) key attributes
|
55
|
+
# or nil if model.persisted? is false
|
56
|
+
def to_key
|
57
|
+
persisted? ? [:id] : nil
|
58
|
+
end
|
56
59
|
|
57
60
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
# redefine this methods so that ActiveModel::Dirty will work
|
68
|
-
def []=(key, new_value)
|
69
|
-
key = key.to_s
|
70
|
-
unless key[0] == ?_
|
71
|
-
old_value = self.send(:[], key)
|
72
|
-
attribute_will_change!(key) unless old_value == new_value
|
73
|
-
end
|
74
|
-
Neo4j::Rails::Transaction.running? ? super : Neo4j::Rails::Transaction.run { super }
|
75
|
-
end
|
61
|
+
# enables ActiveModel::Dirty and Validation
|
62
|
+
def method_missing(method_id, *args, &block)
|
63
|
+
if !self.class.attribute_methods_generated?
|
64
|
+
self.class.define_attribute_methods(self.class._decl_props.keys)
|
65
|
+
# try again
|
66
|
+
send(method_id, *args, &block)
|
67
|
+
end
|
68
|
+
end
|
76
69
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
70
|
+
# redefine this methods so that ActiveModel::Dirty will work
|
71
|
+
def []=(key, new_value)
|
72
|
+
key = key.to_s
|
73
|
+
unless key[0] == ?_
|
74
|
+
old_value = self.send(:[], key)
|
75
|
+
attribute_will_change!(key) unless old_value == new_value
|
76
|
+
end
|
77
|
+
Neo4j::Rails::Transaction.running? ? super : Neo4j::Rails::Transaction.run { super }
|
78
|
+
end
|
85
79
|
|
80
|
+
def attribute_will_change!(attr)
|
81
|
+
begin
|
82
|
+
value = __send__(:[], attr)
|
83
|
+
value = value.duplicable? ? value.clone : value
|
84
|
+
rescue TypeError, NoMethodError
|
85
|
+
end
|
86
|
+
changed_attributes[attr] = value
|
87
|
+
end
|
86
88
|
|
87
|
-
def read_attribute_for_validation(key)
|
88
|
-
respond_to?(key) ? send(key) : self[key]
|
89
|
-
end
|
90
89
|
|
91
|
-
|
92
|
-
|
93
|
-
if respond_to?("#{k}=")
|
94
|
-
send("#{k}=", v)
|
95
|
-
else
|
96
|
-
self[k] = v
|
90
|
+
def read_attribute_for_validation(key)
|
91
|
+
respond_to?(key) ? send(key) : self[key]
|
97
92
|
end
|
98
|
-
end
|
99
|
-
end
|
100
93
|
|
94
|
+
def attributes=(values)
|
95
|
+
sanitize_for_mass_assignment(values).each do |k, v|
|
96
|
+
if respond_to?("#{k}=")
|
97
|
+
send("#{k}=", v)
|
98
|
+
else
|
99
|
+
self[k] = v
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
101
103
|
|
102
|
-
# Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved.
|
103
|
-
# If saving fails because the resource is invalid then false will be returned.
|
104
|
-
def update_attributes(attributes)
|
105
|
-
self.attributes=attributes
|
106
|
-
save
|
107
|
-
end
|
108
104
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
105
|
+
# Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved.
|
106
|
+
# If saving fails because the resource is invalid then false will be returned.
|
107
|
+
def update_attributes(attributes)
|
108
|
+
self.attributes=attributes
|
109
|
+
save
|
110
|
+
end
|
113
111
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
# We are updating a node that was created with the 'new' method.
|
119
|
-
# The relationship will only be kept in the Value object.
|
120
|
-
outgoing(rel_type)<<clazz.new(attr) unless reject_if?(reject_if,attr)
|
121
|
-
else
|
122
|
-
# We have a node that was created with the #create method - has real Neo4j relationships
|
123
|
-
# does it exist ?
|
124
|
-
found = if has_one
|
125
|
-
# id == nil that means we have a has_one relationship
|
126
|
-
outgoing(rel_type).first
|
127
|
-
else
|
128
|
-
# do we have an ID ?
|
129
|
-
id = attr[:id]
|
130
|
-
# this is a has_n relationship, find which one we want to update
|
131
|
-
id && outgoing(rel_type).find { |n| n.id == id }
|
132
|
-
end
|
112
|
+
def update_attributes!(attributes)
|
113
|
+
self.attributes=attributes
|
114
|
+
save!
|
115
|
+
end
|
133
116
|
|
134
|
-
|
135
|
-
|
117
|
+
def update_nested_attributes(rel_type, clazz, has_one, attr, options)
|
118
|
+
allow_destroy, reject_if = [options[:allow_destroy], options[:reject_if]] if options
|
136
119
|
|
137
|
-
|
138
|
-
|
139
|
-
|
120
|
+
if new?
|
121
|
+
# We are updating a node that was created with the 'new' method.
|
122
|
+
# The relationship will only be kept in the Value object.
|
123
|
+
outgoing(rel_type)<<clazz.new(attr) unless reject_if?(reject_if, attr)
|
140
124
|
else
|
141
|
-
|
125
|
+
# We have a node that was created with the #create method - has real Neo4j relationships
|
126
|
+
# does it exist ?
|
127
|
+
found = if has_one
|
128
|
+
# id == nil that means we have a has_one relationship
|
129
|
+
outgoing(rel_type).first
|
130
|
+
else
|
131
|
+
# do we have an ID ?
|
132
|
+
id = attr[:id]
|
133
|
+
# this is a has_n relationship, find which one we want to update
|
134
|
+
id && outgoing(rel_type).find { |n| n.id == id }
|
135
|
+
end
|
136
|
+
|
137
|
+
# Check if we want to destroy not found nodes (e.g. {..., :_destroy => '1' } ?
|
138
|
+
destroy = attr[:_destroy] && attr[:_destroy] != '0'
|
139
|
+
|
140
|
+
puts "FOUND #{found}"
|
141
|
+
if found
|
142
|
+
if destroy
|
143
|
+
found.destroy if allow_destroy
|
144
|
+
else
|
145
|
+
found.update_attributes_in_tx(attr) # it already exist, so update that one
|
146
|
+
end
|
147
|
+
elsif !destroy && !reject_if?(reject_if, attr)
|
148
|
+
new_node = clazz.new(attr)
|
149
|
+
saved = new_node.save
|
150
|
+
outgoing(rel_type) << new_node if saved
|
151
|
+
end
|
142
152
|
end
|
143
|
-
elsif !destroy && !reject_if?(reject_if,attr)
|
144
|
-
new_node = clazz.new(attr)
|
145
|
-
saved = new_node.save
|
146
|
-
outgoing(rel_type) << new_node if saved
|
147
153
|
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
def reject_if?(proc_or_symbol, attr)
|
152
|
-
return false if proc_or_symbol.nil?
|
153
|
-
if proc_or_symbol.is_a?(Symbol)
|
154
|
-
meth = method(proc_or_symbol)
|
155
|
-
meth.arity == 0 ? meth.call : meth.call(attr)
|
156
|
-
else
|
157
|
-
proc_or_symbol.call(attr)
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
def delete
|
162
|
-
super
|
163
|
-
@_deleted = true
|
164
|
-
@_persisted = false
|
165
|
-
end
|
166
154
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
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::Value)
|
178
|
-
false
|
179
|
-
end
|
180
|
-
valid
|
181
|
-
end
|
155
|
+
def reject_if?(proc_or_symbol, attr)
|
156
|
+
return false if proc_or_symbol.nil?
|
157
|
+
if proc_or_symbol.is_a?(Symbol)
|
158
|
+
meth = method(proc_or_symbol)
|
159
|
+
meth.arity == 0 ? meth.call : meth.call(attr)
|
160
|
+
else
|
161
|
+
proc_or_symbol.call(attr)
|
162
|
+
end
|
163
|
+
end
|
182
164
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
init_on_load(node)
|
189
|
-
init_on_create
|
190
|
-
end
|
165
|
+
def delete
|
166
|
+
super
|
167
|
+
@_deleted = true
|
168
|
+
@_persisted = false
|
169
|
+
end
|
191
170
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
171
|
+
def save
|
172
|
+
valid = valid?
|
173
|
+
if valid
|
174
|
+
# if we are trying to save a value then we should create a real node
|
175
|
+
valid = _run_save_callbacks { create_or_update_node }
|
176
|
+
@_created_record = false
|
177
|
+
true
|
178
|
+
else
|
179
|
+
# if not valid we should rollback the transaction so that the changes does not take place.
|
180
|
+
# no point failing the transaction if we have created a model with 'new'
|
181
|
+
Neo4j::Rails::Transaction.fail if Neo4j::Rails::Transaction.running? && !_java_node.kind_of?(Neo4j::Rails::Value)
|
182
|
+
false
|
183
|
+
end
|
184
|
+
valid
|
185
|
+
end
|
199
186
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
187
|
+
def create_or_update_node
|
188
|
+
valid = true
|
189
|
+
if _java_node.kind_of?(Neo4j::Rails::Value)
|
190
|
+
node = Neo4j::Node.new(props)
|
191
|
+
valid = _java_node.save_nested(node)
|
192
|
+
init_on_load(node)
|
193
|
+
init_on_create
|
194
|
+
end
|
204
195
|
|
205
|
-
|
206
|
-
|
207
|
-
|
196
|
+
if new_record?
|
197
|
+
_run_create_callbacks { clear_changes }
|
198
|
+
else
|
199
|
+
_run_update_callbacks { clear_changes }
|
200
|
+
end
|
201
|
+
valid
|
202
|
+
end
|
208
203
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
204
|
+
def clear_changes
|
205
|
+
@previously_changed = changes
|
206
|
+
@changed_attributes.clear
|
207
|
+
end
|
213
208
|
|
214
|
-
|
215
|
-
|
216
|
-
|
209
|
+
def save!
|
210
|
+
raise RecordInvalidError.new(self) unless save
|
211
|
+
end
|
217
212
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
end
|
213
|
+
# Returns if the record is persisted, i.e. it’s not a new record and it was not destroyed
|
214
|
+
def persisted?
|
215
|
+
!new_record? && !destroyed?
|
216
|
+
end
|
223
217
|
|
224
|
-
|
225
|
-
|
226
|
-
|
218
|
+
def to_model
|
219
|
+
self
|
220
|
+
end
|
227
221
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
222
|
+
# Returns true if this object hasn’t been saved yet — that is, a record for the object doesn’t exist yet; otherwise, returns false.
|
223
|
+
def new_record?()
|
224
|
+
# it is new if the model has been created with either the new or create method
|
225
|
+
new? || @_created_record == true
|
226
|
+
end
|
232
227
|
|
233
|
-
|
234
|
-
|
235
|
-
|
228
|
+
def new?
|
229
|
+
_java_node.kind_of?(Neo4j::Rails::Value)
|
230
|
+
end
|
236
231
|
|
237
|
-
|
238
|
-
|
239
|
-
|
232
|
+
def del
|
233
|
+
@_deleted = true
|
234
|
+
super
|
235
|
+
end
|
240
236
|
|
241
|
-
|
237
|
+
def destroy
|
238
|
+
_run_update_callbacks { del }
|
239
|
+
end
|
242
240
|
|
243
|
-
|
244
|
-
|
245
|
-
|
241
|
+
def destroyed?()
|
242
|
+
@_deleted
|
243
|
+
end
|
246
244
|
|
247
|
-
|
248
|
-
extend Neo4j::TxMethods
|
245
|
+
tx_methods :destroy, :create_or_update_node, :update_attributes, :update_attributes!
|
249
246
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
wrapped = self.orig_new
|
254
|
-
wrapped.init_on_load(value)
|
255
|
-
wrapped.attributes=args[0] if args[0].respond_to?(:each_pair)
|
247
|
+
# --------------------------------------
|
248
|
+
# Class Methods
|
249
|
+
# --------------------------------------
|
256
250
|
|
257
|
-
|
251
|
+
class << self
|
252
|
+
extend TxMethods
|
258
253
|
|
259
|
-
|
260
|
-
|
254
|
+
# returns a value object instead of creating a new node
|
255
|
+
def new(*args)
|
256
|
+
wrapped = self.orig_new
|
257
|
+
value = Neo4j::Rails::Value.new(wrapped)
|
258
|
+
wrapped.init_on_load(value)
|
259
|
+
wrapped.attributes=args[0] if args[0].respond_to?(:each_pair)
|
260
|
+
wrapped
|
261
261
|
end
|
262
262
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
end if dsl.direction == :incoming
|
263
|
+
# Handle Model.find(params[:id])
|
264
|
+
def find(*args)
|
265
|
+
if args.length == 1 && String === args[0] && args[0].to_i != 0
|
266
|
+
load(*args)
|
267
|
+
else
|
268
|
+
hits = super
|
269
|
+
# We need to save this so that the Rack Neo4j::Rails:LuceneConnection::Closer can close it
|
270
|
+
Thread.current[:neo4j_lucene_connection] ||= []
|
271
|
+
Thread.current[:neo4j_lucene_connection] << hits
|
272
|
+
hits
|
273
|
+
end
|
275
274
|
end
|
276
|
-
end
|
277
|
-
|
278
|
-
wrapped
|
279
|
-
end
|
280
275
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
Thread.current[:neo4j_lucene_connection] << hits
|
290
|
-
hits
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
|
-
def load(*ids)
|
295
|
-
result = ids.map { |id| Neo4j::Node.load(id) }
|
296
|
-
if ids.length == 1
|
297
|
-
result.first
|
298
|
-
else
|
299
|
-
result
|
300
|
-
end
|
301
|
-
end
|
276
|
+
def load(*ids)
|
277
|
+
result = ids.map { |id| Neo4j::Node.load(id) }
|
278
|
+
if ids.length == 1
|
279
|
+
result.first
|
280
|
+
else
|
281
|
+
result
|
282
|
+
end
|
283
|
+
end
|
302
284
|
|
303
285
|
|
304
|
-
|
286
|
+
alias_method :_orig_create, :create
|
305
287
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
288
|
+
def create(*)
|
289
|
+
model = super
|
290
|
+
model.save
|
291
|
+
model
|
292
|
+
end
|
311
293
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
294
|
+
def create!(*args)
|
295
|
+
model = _orig_create(*args)
|
296
|
+
model.save!
|
297
|
+
model
|
298
|
+
end
|
317
299
|
|
318
|
-
|
300
|
+
tx_methods :create, :create!
|
319
301
|
|
320
302
|
|
321
|
-
|
322
|
-
|
323
|
-
|
303
|
+
def transaction(&block)
|
304
|
+
Neo4j::Rails::Transaction.run &block
|
305
|
+
end
|
324
306
|
|
325
|
-
|
326
|
-
|
307
|
+
def accepts_nested_attributes_for(*attr_names)
|
308
|
+
options = attr_names.pop if attr_names[-1].is_a?(Hash)
|
327
309
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
310
|
+
attr_names.each do |association_name|
|
311
|
+
rel = self._decl_rels[association_name.to_sym]
|
312
|
+
raise "No relationship declared with has_n or has_one with type #{association_name}" unless rel
|
313
|
+
to_class = rel.to_class
|
314
|
+
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 to_class
|
315
|
+
type = rel.namespace_type
|
316
|
+
has_one = rel.has_one?
|
335
317
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
318
|
+
send(:define_method, "#{association_name}_attributes=") do |attributes|
|
319
|
+
if has_one
|
320
|
+
update_nested_attributes(type, to_class, true, attributes, options)
|
321
|
+
else
|
322
|
+
if attributes.is_a?(Array)
|
323
|
+
attributes.each do |attr|
|
324
|
+
update_nested_attributes(type, to_class, false, attr, options)
|
325
|
+
end
|
326
|
+
else
|
327
|
+
attributes.each_value do |attr|
|
328
|
+
update_nested_attributes(type, to_class, false, attr, options)
|
329
|
+
end
|
330
|
+
end
|
347
331
|
end
|
348
332
|
end
|
333
|
+
tx_methods("#{association_name}_attributes=")
|
349
334
|
end
|
350
335
|
end
|
351
|
-
|
336
|
+
|
352
337
|
end
|
338
|
+
|
353
339
|
end
|
354
340
|
|
355
341
|
end
|
356
|
-
|
357
342
|
end
|
358
343
|
|
@@ -1,11 +1,15 @@
|
|
1
|
-
module Neo4j
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
module Neo4j
|
2
|
+
module Rails
|
3
|
+
module TxMethods
|
4
|
+
def tx_methods(*methods)
|
5
|
+
methods.each do |method|
|
6
|
+
tx_method = "#{method}_in_tx"
|
7
|
+
send(:alias_method, tx_method, method)
|
8
|
+
send(:define_method, method) do |*args|
|
9
|
+
Neo4j::Rails::Transaction.running? ? send(tx_method, *args) : Neo4j::Rails::Transaction.run { send(tx_method, *args) }
|
10
|
+
end
|
11
|
+
end
|
8
12
|
end
|
9
13
|
end
|
10
14
|
end
|
11
|
-
end
|
15
|
+
end
|
data/lib/neo4j/rails/value.rb
CHANGED
@@ -1,16 +1,13 @@
|
|
1
|
-
module Neo4j
|
1
|
+
module Neo4j::Rails
|
2
2
|
|
3
3
|
class Value
|
4
4
|
include Neo4j::Property
|
5
5
|
include org.neo4j.graphdb.Node
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
|
7
|
+
def initialize(wrapper)
|
8
|
+
@wrapper = wrapper
|
9
9
|
@props = {}
|
10
|
-
|
11
|
-
args[0].each_pair { |k, v| set_property(k.to_s, v) }
|
12
|
-
end
|
13
|
-
@rels = {} # a hash of all relationship with key type
|
10
|
+
@outgoing_rels = {} # a hash of all relationship with key type
|
14
11
|
end
|
15
12
|
|
16
13
|
# override Neo4j::Property#props
|
@@ -22,6 +19,23 @@ module Neo4j
|
|
22
19
|
nil
|
23
20
|
end
|
24
21
|
|
22
|
+
|
23
|
+
def create_relationship_to(other_java_node, java_type)
|
24
|
+
outgoing(java_type.name).new(other_java_node)
|
25
|
+
end
|
26
|
+
|
27
|
+
def getSingleRelationship(type, dir)
|
28
|
+
# TODO incoming not implemented, needed ?
|
29
|
+
@outgoing_rels[type.name] && @outgoing_rels[type.name].rels.first
|
30
|
+
end
|
31
|
+
|
32
|
+
def getRelationships(*args)
|
33
|
+
type = args[0].name
|
34
|
+
outgoing = @outgoing_rels[type]
|
35
|
+
return [] unless outgoing
|
36
|
+
outgoing.rels
|
37
|
+
end
|
38
|
+
|
25
39
|
# Pretend this object is a Java Node
|
26
40
|
def has_property?(key)
|
27
41
|
!@props[key].nil?
|
@@ -39,16 +53,22 @@ module Neo4j
|
|
39
53
|
@props.delete(key)
|
40
54
|
end
|
41
55
|
|
56
|
+
def wrapper
|
57
|
+
@wrapper
|
58
|
+
end
|
59
|
+
|
42
60
|
def outgoing(type)
|
43
|
-
@
|
61
|
+
@outgoing_rels[type.to_s] ||= OutgoingRels.new(self)
|
44
62
|
end
|
45
63
|
|
46
64
|
def save_nested(root_node)
|
47
65
|
valid = true
|
48
|
-
@
|
66
|
+
@outgoing_rels.each_pair do |type, rel|
|
49
67
|
rel.each do |new_node|
|
50
|
-
|
51
|
-
|
68
|
+
wrapper = new_node.respond_to?(:wrapper) ? new_node.wrapper : new_node
|
69
|
+
if wrapper.save
|
70
|
+
puts "NEW RELATIONSHIP OF TYPE #{type} from #{root_node} to #{new_node}"
|
71
|
+
root_node.outgoing(type) << wrapper
|
52
72
|
else
|
53
73
|
valid = false
|
54
74
|
end
|
@@ -57,22 +77,55 @@ module Neo4j
|
|
57
77
|
valid
|
58
78
|
end
|
59
79
|
|
80
|
+
class Relationship
|
81
|
+
include org.neo4j.graphdb.Relationship
|
82
|
+
attr_reader :end_node, :start_node
|
83
|
+
|
84
|
+
def initialize(from, to)
|
85
|
+
@end_node = to
|
86
|
+
@start_node = from
|
87
|
+
end
|
88
|
+
|
89
|
+
def wrapper
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
def getOtherNode(other)
|
94
|
+
other == @end_node ? @start_node : @end_node
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
60
99
|
class OutgoingRels
|
61
100
|
include Enumerable
|
62
|
-
|
63
|
-
|
101
|
+
attr_reader :rels
|
102
|
+
|
103
|
+
def initialize(start_node)
|
104
|
+
@rels = []
|
105
|
+
@start_node = start_node
|
64
106
|
end
|
65
107
|
|
66
108
|
def <<(other)
|
67
|
-
|
109
|
+
new(other)
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
def new(other)
|
114
|
+
new_rel = Relationship.new(@start_node, other)
|
115
|
+
@rels << new_rel
|
116
|
+
new_rel
|
68
117
|
end
|
69
118
|
|
70
119
|
def each
|
71
|
-
@
|
120
|
+
@rels.each {|n| yield n.end_node}
|
121
|
+
end
|
122
|
+
|
123
|
+
def clear
|
124
|
+
@rels.clear
|
72
125
|
end
|
73
126
|
|
74
127
|
def empty?
|
75
|
-
@
|
128
|
+
@rels.empty?
|
76
129
|
end
|
77
130
|
|
78
131
|
def is_a?(type)
|
data/lib/neo4j/version.rb
CHANGED
metadata
CHANGED
@@ -7,8 +7,8 @@ version: !ruby/object:Gem::Version
|
|
7
7
|
- 0
|
8
8
|
- 0
|
9
9
|
- beta
|
10
|
-
-
|
11
|
-
version: 1.0.0.beta.
|
10
|
+
- 12
|
11
|
+
version: 1.0.0.beta.12
|
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-
|
19
|
+
date: 2010-10-12 00:00:00 +02:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -102,6 +102,7 @@ extra_rdoc_files:
|
|
102
102
|
files:
|
103
103
|
- lib/neo4j.rb
|
104
104
|
- lib/neo4j/event_handler.rb
|
105
|
+
- lib/neo4j/model.rb
|
105
106
|
- lib/neo4j/node_traverser.rb
|
106
107
|
- lib/neo4j/relationship_traverser.rb
|
107
108
|
- lib/neo4j/config.rb
|