active_node 2.0.0 → 2.0.1
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.
- checksums.yaml +4 -4
- data/lib/active_node.rb +1 -0
- data/lib/active_node/associations/association.rb +50 -14
- data/lib/active_node/associations/builder/collection_association.rb +8 -0
- data/lib/active_node/associations/builder/singular_association.rb +8 -0
- data/lib/active_node/associations/collection_association.rb +0 -14
- data/lib/active_node/associations/singular_association.rb +13 -9
- data/lib/active_node/persistence.rb +51 -1
- data/lib/active_node/relationship.rb +38 -0
- data/lib/active_node/version.rb +1 -1
- data/spec/functional/associations_spec.rb +35 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 05485943a60e029748218ef9bb075ac6a601e863
|
4
|
+
data.tar.gz: 025ea77b6cfd4e68a839f0712d48fdb5c8de0a02
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0f21defc7f1b9477c886879b2fbca7aaf3e2a79ec7f39b2304084faf6cd84efc9dc3e231cf2f15fce724c23be89bff6b733ccedbcce0fbd6975aea90217aeeb
|
7
|
+
data.tar.gz: 8ab33c466677513cc63a9be1009a7c10b06cd82796dc6d6a2a499cdc3bfdee1612b7e8ad5db0b0675dcc0baaf01d97170b7d92e77bac3ba5137c1f33fb54a0fd
|
data/lib/active_node.rb
CHANGED
@@ -40,32 +40,68 @@ module ActiveNode
|
|
40
40
|
reflection.klass
|
41
41
|
end
|
42
42
|
|
43
|
-
def load_target
|
44
|
-
owner.send(reflection.direction, reflection.type, reflection.klass)
|
45
|
-
end
|
46
43
|
|
47
|
-
|
48
|
-
|
49
|
-
@target ||= load_target
|
44
|
+
def rel(*associations)
|
45
|
+
owner.relationships(reflection, *associations)
|
50
46
|
end
|
51
47
|
|
52
48
|
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
|
53
49
|
def writer(records)
|
54
50
|
@dirty = true
|
51
|
+
@rel_target = nil
|
55
52
|
@target = records
|
56
53
|
end
|
54
|
+
alias :super_writer :writer
|
55
|
+
|
56
|
+
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
57
|
+
def ids_writer(ids)
|
58
|
+
@rel_target = nil
|
59
|
+
super_writer klass.find(ids.reject(&:blank?).map!(&:to_i))
|
60
|
+
end
|
61
|
+
|
62
|
+
def reader(*args)
|
63
|
+
@target ||= rels_reader(*args).map &:other
|
64
|
+
end
|
65
|
+
|
66
|
+
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
67
|
+
def ids_reader
|
68
|
+
reader
|
69
|
+
@target.map(&:id)
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def rels_reader(*args)
|
74
|
+
@rel_target ||= rel(*args)
|
75
|
+
end
|
76
|
+
|
77
|
+
def rels_writer(rels)
|
78
|
+
@target = nil
|
79
|
+
@dirty = true
|
80
|
+
@rel_target = rels
|
81
|
+
end
|
57
82
|
|
58
|
-
def save
|
59
|
-
return unless @dirty
|
83
|
+
def save(fresh=false)
|
84
|
+
#return unless @dirty
|
60
85
|
#delete all relations missing in new target
|
61
|
-
|
62
|
-
|
63
|
-
|
86
|
+
original_rels = fresh ? [] : rel
|
87
|
+
original_rels.each do |r|
|
88
|
+
unless ids_reader.include? r.other.id
|
89
|
+
Neo.db.delete_relationship(r.id)
|
90
|
+
original_rels.delete(r)
|
91
|
+
end
|
64
92
|
end
|
65
|
-
|
66
|
-
original_target_ids = original_target.map(&:neo_id).map(&:to_i)
|
93
|
+
|
67
94
|
#add relations missing in old target
|
68
|
-
|
95
|
+
#if no rel_target proceed as before + set rel_target from db
|
96
|
+
#if rel_target exists update persisted records and insert new records
|
97
|
+
if @rel_target
|
98
|
+
@rel_target.each { |r| r.save(self) }
|
99
|
+
else
|
100
|
+
@target.map do |n|
|
101
|
+
original_rels.detect { |r| r.other.id == n.id }.tap { |o_r| o_r.try :other=, n } ||
|
102
|
+
ActiveNode::Relationship.create!(n, self)
|
103
|
+
end
|
104
|
+
end
|
69
105
|
end
|
70
106
|
end
|
71
107
|
end
|
@@ -9,6 +9,10 @@ module ActiveNode::Associations::Builder
|
|
9
9
|
def #{name.to_s.singularize}_ids
|
10
10
|
association(:#{name}).ids_reader
|
11
11
|
end
|
12
|
+
|
13
|
+
def #{name.to_s.singularize}_rels
|
14
|
+
association(:#{name}).rels_reader
|
15
|
+
end
|
12
16
|
CODE
|
13
17
|
end
|
14
18
|
|
@@ -19,6 +23,10 @@ module ActiveNode::Associations::Builder
|
|
19
23
|
def #{name.to_s.singularize}_ids=(ids)
|
20
24
|
association(:#{name}).ids_writer(ids)
|
21
25
|
end
|
26
|
+
|
27
|
+
def #{name.to_s.singularize}_rels=(ids)
|
28
|
+
association(:#{name}).rels_writer(ids)
|
29
|
+
end
|
22
30
|
CODE
|
23
31
|
end
|
24
32
|
end
|
@@ -23,6 +23,10 @@ module ActiveNode::Associations::Builder
|
|
23
23
|
def #{name}_id
|
24
24
|
association(:#{name}).id_reader
|
25
25
|
end
|
26
|
+
|
27
|
+
def #{name}_rel
|
28
|
+
association(:#{name}).rel_reader
|
29
|
+
end
|
26
30
|
CODE
|
27
31
|
end
|
28
32
|
|
@@ -33,6 +37,10 @@ module ActiveNode::Associations::Builder
|
|
33
37
|
def #{name}_id=(value)
|
34
38
|
association(:#{name}).id_writer(value)
|
35
39
|
end
|
40
|
+
|
41
|
+
def #{name}_rel=(value)
|
42
|
+
association(:#{name}).rel_writer(value)
|
43
|
+
end
|
36
44
|
CODE
|
37
45
|
end
|
38
46
|
end
|
@@ -26,20 +26,6 @@ module ActiveNode
|
|
26
26
|
# +load_target+ and the +loaded+ flag are your friends.
|
27
27
|
class CollectionAssociation < Association #:nodoc:
|
28
28
|
|
29
|
-
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
30
|
-
def ids_reader
|
31
|
-
reader.map(&:id)
|
32
|
-
end
|
33
|
-
|
34
|
-
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
35
|
-
def ids_writer(ids)
|
36
|
-
writer klass.find(ids.reject(&:blank?).map!(&:to_i))
|
37
|
-
end
|
38
|
-
|
39
|
-
def target_each
|
40
|
-
target.each {|n| yield n}
|
41
|
-
end
|
42
|
-
|
43
29
|
def reset
|
44
30
|
super
|
45
31
|
@target = owner.new_record? ? [] : nil
|
@@ -1,24 +1,28 @@
|
|
1
1
|
module ActiveNode
|
2
2
|
module Associations
|
3
3
|
class SingularAssociation < Association #:nodoc:
|
4
|
-
def
|
4
|
+
def reader
|
5
5
|
super.try :first
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
9
|
-
|
8
|
+
def id_reader
|
9
|
+
ids_reader.try :first
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
[
|
12
|
+
def id_writer(id)
|
13
|
+
ids_writer([id].compact)
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
16
|
+
def rel_reader
|
17
|
+
rels_reader.try :first
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
21
|
-
|
20
|
+
def rel_writer(rel)
|
21
|
+
rels_writer([rel].compact)
|
22
|
+
end
|
23
|
+
|
24
|
+
def writer(record)
|
25
|
+
super([record].compact)
|
22
26
|
end
|
23
27
|
end
|
24
28
|
end
|
@@ -37,6 +37,10 @@ module ActiveNode
|
|
37
37
|
node.is_a?(Array) ? new_instances(node, klass) : new_instance(node, klass)
|
38
38
|
end
|
39
39
|
|
40
|
+
def wrap_rel rel, node, klass
|
41
|
+
ActiveNode::Relationship.new wrap(node, klass), rel['data'].merge(id: get_id(rel).to_i)
|
42
|
+
end
|
43
|
+
|
40
44
|
def active_node_class(class_name, default_klass=nil)
|
41
45
|
klass = Module.const_get(class_name) rescue nil
|
42
46
|
klass && klass < ActiveNode::Base && klass || default_klass
|
@@ -117,7 +121,51 @@ module ActiveNode
|
|
117
121
|
related(:outgoing, type, klass)
|
118
122
|
end
|
119
123
|
|
124
|
+
def relationships(reflection, *associations)
|
125
|
+
id ?
|
126
|
+
Neo.db.execute_query(
|
127
|
+
"start n=node({id}) match #{match reflection}#{optional_match reflection, associations} return #{list_with_rel reflection.name, *associations} order by #{created_at_list reflection.name, *associations}",
|
128
|
+
{id: id})['data'].map { |rel_node| self.class.wrap_rel rel_node[0], rel_node[1], reflection.klass } :
|
129
|
+
[]
|
130
|
+
end
|
131
|
+
|
120
132
|
private
|
133
|
+
def parse_result klass, result, associations
|
134
|
+
node_map = {}
|
135
|
+
result.each do |record|
|
136
|
+
(node_map[extract_id(record[1])] ||= wrap_rel(record[0], record[1], klass))
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def extract_id(id)
|
141
|
+
get_id(id).to_i
|
142
|
+
end
|
143
|
+
|
144
|
+
def match(reflection, start_var='n')
|
145
|
+
"(#{start_var})#{'<' if reflection.direction == :incoming}-[#{reflection.name}_rel:#{reflection.type}]-#{'>' if reflection.direction == :outgoing}(#{reflection.name}#{label reflection.klass})"
|
146
|
+
end
|
147
|
+
|
148
|
+
def optional_match reflection, associations
|
149
|
+
return if associations.empty?
|
150
|
+
" optional match " + comma_sep_list(associations.map { |association| match(reflection.klass.reflect_on_association(association), association) })
|
151
|
+
end
|
152
|
+
|
153
|
+
def label klass
|
154
|
+
":#{klass.label}" if klass
|
155
|
+
end
|
156
|
+
|
157
|
+
def list_with_rel *names
|
158
|
+
comma_sep_list names.map { |name| ["#{name}_rel", name] }.flatten
|
159
|
+
end
|
160
|
+
|
161
|
+
def comma_sep_list *items
|
162
|
+
items.join(', ')
|
163
|
+
end
|
164
|
+
|
165
|
+
def created_at_list *names
|
166
|
+
comma_sep_list names.map { |name| "#{name}.created_at" }
|
167
|
+
end
|
168
|
+
|
121
169
|
def split_hash hash, method, split_by
|
122
170
|
hash.try(method) { |k, _| send split_by, k }
|
123
171
|
end
|
@@ -144,8 +192,9 @@ module ActiveNode
|
|
144
192
|
end
|
145
193
|
|
146
194
|
def create_or_update
|
195
|
+
fresh = new_record?
|
147
196
|
write; true
|
148
|
-
association_cache.values.each
|
197
|
+
association_cache.values.each { |assoc| assoc.save(fresh) }
|
149
198
|
end
|
150
199
|
|
151
200
|
def write
|
@@ -171,5 +220,6 @@ module ActiveNode
|
|
171
220
|
def all_attributes
|
172
221
|
attributes.merge(@hash)
|
173
222
|
end
|
223
|
+
|
174
224
|
end
|
175
225
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module ActiveNode
|
2
|
+
class Relationship
|
3
|
+
include Neography::Rest::Helpers
|
4
|
+
|
5
|
+
attr_accessor :other
|
6
|
+
attr_reader :id
|
7
|
+
|
8
|
+
def initialize other, props={}
|
9
|
+
self.other = other
|
10
|
+
@hash = props.with_indifferent_access
|
11
|
+
@id = @hash.delete(:id).try(&:to_i)
|
12
|
+
end
|
13
|
+
|
14
|
+
delegate :[], to: :@hash
|
15
|
+
delegate :[]=, to: :@hash
|
16
|
+
|
17
|
+
def new_record?
|
18
|
+
!id
|
19
|
+
end
|
20
|
+
|
21
|
+
def save(association)
|
22
|
+
if new_record?
|
23
|
+
from=association.owner
|
24
|
+
to=other
|
25
|
+
if association.reflection.direction == :incoming
|
26
|
+
from, to = to, from
|
27
|
+
end
|
28
|
+
@id = get_id(Neo.db.create_relationship(association.reflection.type, from.id, to.id, @hash)).to_i
|
29
|
+
else
|
30
|
+
Neo.db.reset_relationship_properties(id, @hash)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.create!(n, association)
|
35
|
+
new(n).save(association)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/active_node/version.rb
CHANGED
@@ -143,5 +143,40 @@ describe ActiveNode::Associations do
|
|
143
143
|
father = Person.create!(children: [Person.create!])
|
144
144
|
father.children.first.father.should == father
|
145
145
|
end
|
146
|
+
|
147
|
+
it 'returns relationships to related nodes' do
|
148
|
+
child1 = Person.create!
|
149
|
+
child2 = Person.create!
|
150
|
+
person = Person.create! child_ids: [child1.id, child2.id]
|
151
|
+
person.save
|
152
|
+
person = Person.find(person.id)
|
153
|
+
person.child_rels.map(&:other).should == person.children
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'relationship should include property' do
|
157
|
+
client = Client.create! name: 'Heinrich'
|
158
|
+
client.address_rel=ActiveNode::Relationship.new(Address.create!, address_type: 'home')
|
159
|
+
client.save
|
160
|
+
client = Client.find(client.id)
|
161
|
+
client.address_rel[:address_type].should == 'home'
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'should save updated property on relationship' do
|
165
|
+
client = Client.create! name: 'Heinrich'
|
166
|
+
client.address_rel=ActiveNode::Relationship.new(Address.create!, address_type: 'home')
|
167
|
+
client.save
|
168
|
+
client = Client.find(client.id)
|
169
|
+
client.address_rel[:address_type]='office'
|
170
|
+
client.save
|
171
|
+
client=Client.find(client.id)
|
172
|
+
ar=client.address_rel
|
173
|
+
ar[:address_type].should == 'office'
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'should retrieve multiple relationships at once' do
|
177
|
+
address = Address.create!
|
178
|
+
person = Person.create! children: [Person.create!(address: address)]
|
179
|
+
Person.find(person.id).children(:address).first.address.should == address
|
180
|
+
end
|
146
181
|
end
|
147
182
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_node
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Heinrich Klobuczek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-01-
|
11
|
+
date: 2014-01-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: active_attr
|
@@ -155,6 +155,7 @@ files:
|
|
155
155
|
- lib/active_node/neo.rb
|
156
156
|
- lib/active_node/persistence.rb
|
157
157
|
- lib/active_node/reflection.rb
|
158
|
+
- lib/active_node/relationship.rb
|
158
159
|
- lib/active_node/validations.rb
|
159
160
|
- lib/active_node/version.rb
|
160
161
|
- spec/functional/associations_spec.rb
|