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 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