active_node 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2d31741bd5a8017c8fbc4a49a3494d5d4622e6b0
4
- data.tar.gz: 4fe415aecee9215b53ab04a5e5f031c94fcca1b8
3
+ metadata.gz: 05485943a60e029748218ef9bb075ac6a601e863
4
+ data.tar.gz: 025ea77b6cfd4e68a839f0712d48fdb5c8de0a02
5
5
  SHA512:
6
- metadata.gz: 740435b0a5eceb85a52c8c98a0a2068b26c1e17cfb5dde3341bb6075c9f6717478a811e564c48cf1ae2460f7131bea2ef34c8bf2713b3e5a9eb04fbe28255304
7
- data.tar.gz: ec10984057ef8c34ccfd19e4a1b83359db40b121e02e5d5b400618c78ff565c6f198b67ce8329c1f9f9a2f673dc6969bbedd82f373f3a69cf7a452cf73c44f1d
6
+ metadata.gz: a0f21defc7f1b9477c886879b2fbca7aaf3e2a79ec7f39b2304084faf6cd84efc9dc3e231cf2f15fce724c23be89bff6b733ccedbcce0fbd6975aea90217aeeb
7
+ data.tar.gz: 8ab33c466677513cc63a9be1009a7c10b06cd82796dc6d6a2a499cdc3bfdee1612b7e8ad5db0b0675dcc0baaf01d97170b7d92e77bac3ba5137c1f33fb54a0fd
@@ -10,6 +10,7 @@ module ActiveNode
10
10
  autoload :Reflection
11
11
  autoload :VERSION
12
12
  autoload :Neo
13
+ autoload :Relationship
13
14
 
14
15
  eager_autoload do
15
16
  autoload :ActiveNodeError, 'active_node/errors'
@@ -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
- # Implements the reader method, e.g. foo.items for Foo.has_many :items
48
- def reader(force_reload = false)
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
- node = Neography::Node.load(owner.id)
62
- node.rels(reflection.type).send(reflection.direction).each do |rel|
63
- rel.del unless ids_reader.include? rel.other_node(node).neo_id.to_i
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
- original_target = node.send(reflection.direction, reflection.type)
66
- original_target_ids = original_target.map(&:neo_id).map(&:to_i)
93
+
67
94
  #add relations missing in old target
68
- target_each { |n| original_target << n.id unless original_target_ids.include? n.id }
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 load_target
4
+ def reader
5
5
  super.try :first
6
6
  end
7
7
 
8
- def target_each
9
- yield target if target.present?
8
+ def id_reader
9
+ ids_reader.try :first
10
10
  end
11
11
 
12
- def ids_reader
13
- [id_reader].compact
12
+ def id_writer(id)
13
+ ids_writer([id].compact)
14
14
  end
15
15
 
16
- def id_reader
17
- target.try :id
16
+ def rel_reader
17
+ rels_reader.try :first
18
18
  end
19
19
 
20
- def id_writer(id)
21
- writer(id.blank? ? nil : klass.find(id.to_i))
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 &:save
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
@@ -1,3 +1,3 @@
1
1
  module ActiveNode
2
- VERSION = "2.0.0"
2
+ VERSION = "2.0.1"
3
3
  end
@@ -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.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-24 00:00:00.000000000 Z
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