arrest 0.0.16 → 0.0.17

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.
@@ -8,7 +8,11 @@ require "arrest/attributes/has_attributes"
8
8
  require "arrest/attributes/attribute"
9
9
  require "arrest/attributes/nested_attribute"
10
10
  require "arrest/attributes/polymorphic_attribute"
11
+ require "arrest/attributes/belongs_to_attribute"
12
+ require "arrest/attributes/has_many_attribute"
11
13
  require "arrest/attributes/converter"
14
+ require "arrest/class_utils.rb"
15
+ require "arrest/string_utils.rb"
12
16
  require "arrest/handler"
13
17
  require "arrest/source"
14
18
  require "arrest/helper/filter"
@@ -50,23 +50,31 @@ module Arrest
50
50
 
51
51
  def has_many(*args)
52
52
  method_name, options = args
53
- singular = (StringUtils.singular(method_name.to_s) + '_ids').to_sym
53
+ ids_field_name = (StringUtils.singular(method_name.to_s) + '_ids').to_sym
54
54
  method_name = method_name.to_sym
55
-
56
- clazz_name = method_name.to_s
55
+ clazz_name = StringUtils.singular(method_name.to_s)
56
+ foreign_key = clazz_name + "_id"
57
57
  if options
58
- clazz = options[:class_name]
59
- if clazz
60
- clazz_name = clazz.to_s
61
- end
58
+ clazz_name = options[:class_name].to_s unless options[:class_name] == nil
59
+ foreign_key = "#{StringUtils.underscore(clazz_name)}_id"
60
+ foreign_key = options[:foreign_key].to_s unless options[:foreign_key] == nil
62
61
  end
63
- attribute singular, Array
62
+
63
+ url_part = method_name.to_s
64
+
65
+ hm_attr = HasManyAttribute.new(ids_field_name,
66
+ method_name,
67
+ clazz_name,
68
+ url_part,
69
+ foreign_key)
70
+ add_attribute(hm_attr)
71
+
64
72
  send :define_method, method_name do
65
73
  if @has_many_collections == nil
66
74
  @has_many_collections = {}
67
75
  end
68
76
  if @has_many_collections[method_name] == nil
69
- @has_many_collections[method_name] = HasManyCollection.new(self, (StringUtils.classify (StringUtils.singular clazz_name)))
77
+ @has_many_collections[method_name] = HasManyCollection.new(self, hm_attr)
70
78
  end
71
79
 
72
80
  @has_many_collections[method_name]
@@ -144,7 +152,7 @@ module Arrest
144
152
  true
145
153
  end
146
154
  #
147
- # convenicence method printing curl command
155
+ # convenience method printing curl command
148
156
  def curl
149
157
  hs = ""
150
158
  Arrest::Source.header_decorator.headers.each_pair do |k,v|
@@ -17,28 +17,31 @@ module Arrest
17
17
  end
18
18
  end
19
19
 
20
- def create_and_add_attribute(field_name, polymorphic, read_only)
20
+ def create_and_add_attribute(field_name, polymorphic, read_only, foreign_key, class_name)
21
21
  if polymorphic
22
22
  add_attribute(PolymorphicAttribute.new(field_name.to_sym, read_only))
23
23
  else
24
- add_attribute(Attribute.new(field_name.to_sym, read_only, String))
24
+ add_attribute(BelongsToAttribute.new(field_name.to_sym, read_only, String, foreign_key, class_name))
25
25
  end
26
26
  end
27
27
 
28
28
  def belongs_to(*args)
29
29
  arg = args[0]
30
30
  name = arg.to_s.downcase
31
- class_name = StringUtils.classify name
31
+ class_name = StringUtils.classify(name)
32
+ foreign_key = "#{StringUtils.underscore(ClassUtils.simple_name(self))}_id"
32
33
  params = args[1] unless args.length < 2
34
+
33
35
  if params
34
36
  read_only = params[:read_only] == true
35
37
  polymorphic = params[:polymorphic] unless params[:polymorphic] == nil
36
38
  class_name = params[:class_name].to_s unless params[:class_name] == nil
39
+ foreign_key = params[:foreign_key].to_s unless params[:foreign_key] == nil
37
40
  end
38
41
 
39
42
  field_name = create_field_name(name, params, polymorphic)
40
43
 
41
- create_and_add_attribute(field_name, polymorphic, read_only)
44
+ create_and_add_attribute(field_name, polymorphic, read_only, foreign_key, class_name)
42
45
 
43
46
  send :define_method, name do
44
47
  val = self.send(field_name)
@@ -0,0 +1,13 @@
1
+ module Arrest
2
+ class BelongsToAttribute < Attribute
3
+ attr_accessor :foreign_key
4
+ def initialize(name, read_only, field_class, foreign_key, target_class_name)
5
+ super(name, read_only, field_class)
6
+ @foreign_key = foreign_key
7
+ @target_class_name = target_class_name
8
+ end
9
+ def target_class
10
+ Arrest::Source.mod.const_get(@target_class_name)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module Arrest
2
+ class HasManyAttribute < Attribute
3
+ attr_reader :method_name, :clazz_name, :url_part, :foreign_key
4
+ def initialize(ids_field_name,
5
+ method_name,
6
+ clazz_name,
7
+ url_part,
8
+ foreign_key)
9
+ super(ids_field_name, false, Array)
10
+ @method_name = method_name.to_sym
11
+ @clazz_name = clazz_name.to_sym
12
+ @url_part = url_part.to_sym
13
+ @foreign_key = foreign_key.to_sym
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ module Arrest
2
+ class ClassUtils
3
+ class << self
4
+ # Returns the simple class name without any preceding modules or namespaces
5
+ # (removes everything up to the last '::' inclusively from class.name)
6
+ def simple_name(clazz)
7
+ clazz.name.gsub(/.*::/,"")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,8 +1,9 @@
1
1
  module Arrest
2
2
  class HasManyCollection #< BasicObject
3
- def initialize parent, clazz_name
3
+ def initialize parent, has_many_attribute
4
4
  @parent = parent
5
- @clazz_name = clazz_name
5
+ @clazz_name = (StringUtils.classify(has_many_attribute.clazz_name.to_s))
6
+ @url_part = has_many_attribute.url_part
6
7
  @children = nil
7
8
  @foreign_key_name = (StringUtils.underscore(@parent.class.name).gsub(/^.*\//, '') + '_id').to_sym
8
9
  define_filters
@@ -23,10 +24,9 @@ module Arrest
23
24
 
24
25
  private
25
26
 
26
-
27
27
  def children
28
28
  if @children == nil
29
- url = @parent.resource_location + '/' + resolved_class.resource_name.to_s
29
+ url = @parent.resource_location + '/' + @url_part.to_s
30
30
  @children = resolved_class.by_url(url)
31
31
  end
32
32
  @children
@@ -1,4 +1,7 @@
1
1
  module Arrest
2
+
3
+ Edge = Struct.new(:foreign_key, :name, :id, :tail)
4
+
2
5
  class MemSource
3
6
 
4
7
  attr_accessor :data
@@ -8,7 +11,9 @@ module Arrest
8
11
  # each having a unique id
9
12
 
10
13
  @@collections = {} # maps urls to collections of ids of objects
11
-
14
+
15
+ # For every has_many relation
16
+ @@edge_matrix = {} # matrix of edges based on node ids for has_many and belongs_to relations
12
17
 
13
18
  @@data = {}
14
19
 
@@ -24,12 +29,25 @@ module Arrest
24
29
  @@data
25
30
  end
26
31
 
32
+ def edge_matrix
33
+ @@edge_matrix
34
+ end
35
+
36
+ def edge_count
37
+ @@edge_matrix.values.inject(0){|sum, edges| sum + edges.length }
38
+ end
39
+
40
+ def node_count
41
+ @@edge_matrix.length
42
+ end
43
+
27
44
  def initialize
28
45
  @@all_objects = {} # holds all objects of all types,
29
46
 
30
47
  @@collections = {} # maps urls to collections of ids of objects
31
48
  @@random = Random.new(42)
32
49
 
50
+ @@edge_matrix = {}
33
51
  end
34
52
 
35
53
 
@@ -61,17 +79,38 @@ module Arrest
61
79
  end
62
80
  end
63
81
 
64
- def get_many sub, filters = {}
82
+ def parse_for_has_many_relations(resource_path)
83
+ matcher = /^.+\/([^\/]+)\/([^\/]+)$/.match(resource_path)
84
+ return [] unless matcher
85
+ object_id = matcher[1]
86
+ relation = matcher[2]
87
+
88
+ if (object_id && relation && @@edge_matrix[object_id])
89
+ result = []
90
+ @@edge_matrix[object_id].each do |edge|
91
+ if (edge.name.to_s == relation)
92
+ result << edge.id
93
+ end
94
+ end
95
+ return result
96
+ end
97
+ []
98
+ end
99
+
100
+ def get_many(sub, filters = {})
65
101
  Arrest::debug sub + (hash_to_query filters)
66
102
  # filters are ignored by mem impl so far
103
+
104
+ id_list = parse_for_has_many_relations(sub)
105
+ if id_list.empty?
106
+ id_list = @@collections[sub] || []
107
+ end
67
108
 
68
- id_list = @@collections[sub] || []
69
109
  objects = id_list.map do |id|
70
110
  @@all_objects[id]
71
111
  end
72
112
 
73
113
  wrap collection_json(objects), id_list.length
74
-
75
114
  end
76
115
 
77
116
  def get_one sub, filters = {}
@@ -95,6 +134,7 @@ module Arrest
95
134
  v.reject!{ |id| id == base_id }
96
135
  end
97
136
  @@all_objects[base_id].delete
137
+ remove_edges(@@edge_matrix, base_id)
98
138
  end
99
139
  end
100
140
 
@@ -118,26 +158,104 @@ module Arrest
118
158
  end
119
159
 
120
160
 
121
- def delete rest_resource
161
+ def delete(rest_resource)
122
162
  raise "To change an object it must have an id" unless rest_resource.respond_to?(:id) && rest_resource.id != nil
123
163
  @@all_objects.delete(rest_resource.id)
124
164
  @@collections.each_pair do |k,v|
125
165
  v.reject!{ |id| id == rest_resource.id }
126
166
  end
167
+ remove_edges(@@edge_matrix, rest_resource.id)
127
168
  rest_resource
128
169
  end
129
170
 
130
- def put rest_resource
171
+ def remove_outgoing_edges(edge_matrix, id)
172
+ if (edge_matrix[id])
173
+ out_edges = edge_matrix[id].find_all{|edge| edge.tail}
174
+ in_edges_to_delete = out_edges.map do |out_edge|
175
+ foreign_edges = edge_matrix[out_edge.id] # the edge set of the foreign node that this node points to
176
+ has_many_back_edges = foreign_edges.find_all do |for_edge|
177
+ for_edge.id == id && for_edge.foreign_key == out_edge.foreign_key
178
+ end
179
+ [has_many_back_edges.first, out_edge.id] # first element may be nil
180
+ end
181
+
182
+ in_edges_to_delete.each do |tupel|
183
+ if tupel[0]
184
+ edge_matrix[tupel[1]].delete_if{|e| e.id == tupel[0].id && e.foreign_key == tupel[0].foreign_key}
185
+ end
186
+ end
187
+ edge_matrix[id] = Set.new()
188
+ end
189
+ end
190
+
191
+ def remove_edges(edge_matrix, node_id)
192
+ if (edge_matrix[node_id])
193
+ edge_matrix[node_id].each do |edge|
194
+ to_nodes = edge_matrix[edge.id]
195
+ to_nodes.delete_if{|e| e.id == node_id}
196
+ end
197
+ edge_matrix.delete(node_id)
198
+ end
199
+ end
200
+
201
+ def identify_and_store_edges(edge_matrix, rest_resource)
202
+ from_id = rest_resource.id
203
+
204
+ rest_resource.class.all_fields.each do |attr|
205
+ if attr.is_a?(Arrest::HasManyAttribute)
206
+ to_ids = rest_resource.send(attr.name) # -> foo_ids
207
+ url_part = attr.url_part
208
+ foreign_key = attr.foreign_key
209
+ edge_matrix[from_id] ||= Set.new()
210
+ if to_ids
211
+ to_ids.each do |to_id|
212
+ edge_matrix[from_id].add(Edge.new(foreign_key, url_part, to_id, true))
213
+ edge_matrix[to_id] ||= Set.new()
214
+ edge_matrix[to_id].add(Edge.new(foreign_key, url_part, from_id, false))
215
+ end
216
+ end
217
+ elsif attr.is_a?(Arrest::BelongsToAttribute)
218
+ to_id = rest_resource.send(attr.name)
219
+ if to_id
220
+ foreign_key = attr.foreign_key
221
+ has_many_clazz = attr.target_class()
222
+ puts "#{foreign_key}"
223
+ hm_candidates = has_many_clazz.all_fields.find_all do |field|
224
+ puts "->#{field.foreign_key.to_s}" if field.is_a?(Arrest::HasManyAttribute)
225
+ field.is_a?(Arrest::HasManyAttribute) && field.foreign_key.to_s == foreign_key
226
+ end
227
+ return if hm_candidates.empty?
228
+ has_many_node = hm_candidates.first
229
+ url_part = has_many_node.url_part
230
+
231
+ edge_matrix[from_id] ||= Set.new()
232
+ edge_matrix[from_id].add(Edge.new(foreign_key, url_part, to_id, true))
233
+ edge_matrix[to_id] ||= Set.new()
234
+ edge_matrix[to_id].add(Edge.new(foreign_key, url_part, from_id, false))
235
+ end
236
+ end
237
+ end
238
+ end
239
+
240
+
241
+ def put(rest_resource)
131
242
  raise "To change an object it must have an id" unless rest_resource.respond_to?(:id) && rest_resource.id != nil
132
243
  old = @@all_objects[rest_resource.id]
133
-
244
+
245
+ remove_outgoing_edges(@@edge_matrix, old.id)
246
+
134
247
  rest_resource.class.all_fields.each do |f|
135
248
  old.send("#{f.name}=", rest_resource.send(f.name))
136
249
  end
250
+
251
+ identify_and_store_edges(@@edge_matrix, rest_resource)
252
+
137
253
  true
138
254
  end
139
255
 
140
- def post rest_resource
256
+
257
+ def post(rest_resource)
258
+
141
259
  Arrest::debug "post -> #{rest_resource.class.name} #{rest_resource.to_hash} #{rest_resource.class.all_fields.map(&:name)}"
142
260
  raise "new object must have setter for id" unless rest_resource.respond_to?(:id=)
143
261
  raise "new object must not have id" if rest_resource.respond_to?(:id) && rest_resource.id != nil
@@ -152,10 +270,13 @@ module Arrest
152
270
  @@collections[rest_resource.resource_path] = []
153
271
  end
154
272
  @@collections[rest_resource.resource_path] << rest_resource.id
273
+
274
+ identify_and_store_edges(@@edge_matrix, rest_resource)
275
+
155
276
  true
156
277
  end
157
278
 
158
- def cheat_collection url, ids
279
+ def cheat_collection(url, ids)
159
280
  @@collections[url] = ids
160
281
  end
161
282
 
@@ -9,7 +9,7 @@ module Arrest
9
9
 
10
10
  def by_url url
11
11
  begin
12
- body = body_root(source().get_many url)
12
+ body = body_root(source().get_many(url))
13
13
  rescue Arrest::Errors::DocumentNotFoundError
14
14
  Arrest::logger.info "DocumentNotFoundError for #{url} gracefully returning []"
15
15
  return []
@@ -1,64 +1,66 @@
1
- class StringUtils
2
- class << self
3
-
4
- PLURALS = [['(quiz)$', '\1zes'],['(ox)$', '\1en'],['([m|l])ouse$', '\1ice'],['(matr|vert|ind)ix|ex$', '\1ices'],
5
- ['(x|ch|ss|sh)$', '\1es'],['([^aeiouy]|qu)ies$', '\1y'],['([^aeiouy]|q)y$$', '\1ies'],['(hive)$', '\1s'],
6
- ['(?:[^f]fe|([lr])f)$', '\1\2ves'],['(sis)$', 'ses'],['([ti])um$', '\1a'],['(buffal|tomat)o$', '\1oes'],['(bu)s$', '\1es'],
7
- ['(alias|status)$', '\1es'],['(octop|vir)us$', '\1i'],['(ax|test)is$', '\1es'],['s$', 's'],['$', 's']]
8
- SINGULARS =[['(quiz)zes$', '\1'],['(matr)ices$', '\1ix'],['(vert|ind)ices$', '\1ex'],['^(ox)en$', '\1'],['(alias|status)es$', '\1'],
9
- ['(octop|vir)i$', '\1us'],['(cris|ax|test)es$', '\1is'],['(shoe)s$', '\1'],['[o]es$', '\1'],['[bus]es$', '\1'],['([m|l])ice$', '\1ouse'],
10
- ['(x|ch|ss|sh)es$', '\1'],['(m)ovies$', '\1ovie'],['[s]eries$', '\1eries'],['([^aeiouy]|qu)ies$', '\1y'],['[lr]ves$', '\1f'],
11
- ['(tive)s$', '\1'],['(hive)s$', '\1'],['([^f])ves$', '\1fe'],['(^analy)ses$', '\1sis'],
12
- ['([a]naly|[b]a|[d]iagno|[p]arenthe|[p]rogno|[s]ynop|[t]he)ses$', '\1\2sis'],['([ti])a$', '\1um'],['(news)$', '\1ews'], ['(.*)s$', '\1'], ['^(.*)$', '\1']]
13
-
14
- def singular(str)
15
- SINGULARS.each { |match_exp, replacement_exp| return str.gsub(Regexp.compile(match_exp), replacement_exp) unless str.match(Regexp.compile(match_exp)).nil?}
16
- end
17
-
18
- def plural(str)
19
- PLURALS.each { |match_exp, replacement_exp| return str.gsub(Regexp.compile(match_exp), replacement_exp) unless str.match(Regexp.compile(match_exp)).nil? }
20
- end
21
-
22
- def plural?
23
- PLURALS.each {|match_exp, replacement_exp| return true if str.match(Regexp.compile(match_exp))}
24
- false
25
- end
26
-
27
- def blank? str
28
- str == nil || str == ""
29
- end
30
-
31
- def is_upper? str
32
- str == str.upcase
33
- end
34
-
35
- def underscore str
36
- word = str.to_s.dup
37
- word.gsub!(/::/, '/')
38
- word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
39
- word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
40
- word.tr!("-", "_")
41
- word.downcase!
42
- word
43
- end
44
-
45
- def classify(str, upper_first = true)
46
- result = ""
47
- upperNext = false
48
- #(singular str) .each_char do |c|
49
- (str) .each_char do |c|
50
- if c == "_"
51
- upperNext = true
52
- else
53
- if upperNext || (result == "" && upper_first)
54
- result << c.upcase
1
+ module Arrest
2
+ class StringUtils
3
+ class << self
4
+
5
+ PLURALS = [['(quiz)$', '\1zes'],['(ox)$', '\1en'],['([m|l])ouse$', '\1ice'],['(matr|vert|ind)ix|ex$', '\1ices'],
6
+ ['(x|ch|ss|sh)$', '\1es'],['([^aeiouy]|qu)ies$', '\1y'],['([^aeiouy]|q)y$$', '\1ies'],['(hive)$', '\1s'],
7
+ ['(?:[^f]fe|([lr])f)$', '\1\2ves'],['(sis)$', 'ses'],['([ti])um$', '\1a'],['(buffal|tomat)o$', '\1oes'],['(bu)s$', '\1es'],
8
+ ['(alias|status)$', '\1es'],['(octop|vir)us$', '\1i'],['(ax|test)is$', '\1es'],['s$', 's'],['$', 's']]
9
+ SINGULARS =[['(quiz)zes$', '\1'],['(matr)ices$', '\1ix'],['(vert|ind)ices$', '\1ex'],['^(ox)en$', '\1'],['(alias|status)es$', '\1'],
10
+ ['(octop|vir)i$', '\1us'],['(cris|ax|test)es$', '\1is'],['(shoe)s$', '\1'],['[o]es$', '\1'],['[bus]es$', '\1'],['([m|l])ice$', '\1ouse'],
11
+ ['(x|ch|ss|sh)es$', '\1'],['(m)ovies$', '\1ovie'],['[s]eries$', '\1eries'],['([^aeiouy]|qu)ies$', '\1y'],['[lr]ves$', '\1f'],
12
+ ['(tive)s$', '\1'],['(hive)s$', '\1'],['([^f])ves$', '\1fe'],['(^analy)ses$', '\1sis'],
13
+ ['([a]naly|[b]a|[d]iagno|[p]arenthe|[p]rogno|[s]ynop|[t]he)ses$', '\1\2sis'],['([ti])a$', '\1um'],['(news)$', '\1ews'], ['(.*)s$', '\1'], ['^(.*)$', '\1']]
14
+
15
+ def singular(str)
16
+ SINGULARS.each { |match_exp, replacement_exp| return str.gsub(Regexp.compile(match_exp), replacement_exp) unless str.match(Regexp.compile(match_exp)).nil?}
17
+ end
18
+
19
+ def plural(str)
20
+ PLURALS.each { |match_exp, replacement_exp| return str.gsub(Regexp.compile(match_exp), replacement_exp) unless str.match(Regexp.compile(match_exp)).nil? }
21
+ end
22
+
23
+ def plural?
24
+ PLURALS.each {|match_exp, replacement_exp| return true if str.match(Regexp.compile(match_exp))}
25
+ false
26
+ end
27
+
28
+ def blank? str
29
+ str == nil || str == ""
30
+ end
31
+
32
+ def is_upper? str
33
+ str == str.upcase
34
+ end
35
+
36
+ def underscore str
37
+ word = str.to_s.dup
38
+ word.gsub!(/::/, '/')
39
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
40
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
41
+ word.tr!("-", "_")
42
+ word.downcase!
43
+ word
44
+ end
45
+
46
+ def classify(str, upper_first = true)
47
+ result = ""
48
+ upperNext = false
49
+ #(singular str) .each_char do |c|
50
+ (str) .each_char do |c|
51
+ if c == "_"
52
+ upperNext = true
55
53
  else
56
- result << c
54
+ if upperNext || (result == "" && upper_first)
55
+ result << c.upcase
56
+ else
57
+ result << c
58
+ end
59
+ upperNext = false
57
60
  end
58
- upperNext = false
59
61
  end
62
+ result
60
63
  end
61
- result
62
64
  end
63
65
  end
64
- end
66
+ end
@@ -1,3 +1,3 @@
1
1
  module Arrest
2
- VERSION = "0.0.16"
2
+ VERSION = "0.0.17"
3
3
  end
@@ -120,3 +120,13 @@ end
120
120
 
121
121
  class DeleteMeAll < Arrest::RootResource
122
122
  end
123
+
124
+ class Foo < Arrest::RootResource
125
+ has_many :bars#, :class_name => :Bar, :foreign_key => defaults to bar_id
126
+ has_many :other_bars, :class_name => :Bar, :foreign_key => :common_key
127
+ end
128
+ class Bar < Arrest::RootResource
129
+ has_many :foos
130
+ belongs_to :foo# foreign key defaults to class name, {:foreign_key => bar_id}
131
+ belongs_to :other_foo, {:class_name => Foo, :foreign_key => :common_key}#, :foreign_key => :other_foo_key
132
+ end
@@ -451,5 +451,103 @@ class FirstTest < Test::Unit::TestCase
451
451
  all = DeleteMeAll.all
452
452
  assert_equal [], all
453
453
  end
454
+
455
+ def test_update_belongs_to
456
+ f1 = Foo.new()
457
+ f1.save
458
+ assert_equal 0, Arrest::Source.source.edge_count
459
+ b1 = Bar.new({:foo_id => f1.id})
460
+ b1.save
461
+ assert_equal 2, Arrest::Source.source.edge_count
462
+ assert_equal 2, Arrest::Source.source.node_count
463
+
464
+ f2 = Foo.new()
465
+ f2.save
466
+ assert_equal 2, Arrest::Source.source.edge_count
467
+ b1.foo_id = f2.id
468
+ b1.save
469
+ #Arrest::Source.source.edge_matrix.each_pair{|k,v| y k; y v}
470
+ assert_equal 2, Arrest::Source.source.edge_count
471
+ assert_equal 3, Arrest::Source.source.node_count
472
+ end
473
+
474
+ def test_has_many_matrix_in_mem_source
475
+ f1 = Foo.new()
476
+ f1.save
477
+ f2 = Foo.new()
478
+ f2.save
479
+ f3 = Foo.new()
480
+ f3.save
481
+
482
+ b1 = Bar.new({:foo_ids => [f1.id, f2.id], :foo_id => f3.id})
483
+ b1.save
484
+
485
+ assert_equal 2, b1.foos.length
486
+
487
+ b2 = Bar.new({:foo_ids => [f2.id, f3.id], :foo_id =>f1.id})
488
+ b2.save
489
+
490
+ f1.delete
491
+
492
+ b1_rel = Bar.find(b1.id)
493
+ assert_equal 1, b1_rel.foos.length
494
+ assert_equal f2.id, b1_rel.foos.first.id
495
+
496
+
497
+ f2.bar_ids=[b1.id]
498
+ f2.other_bar_ids=[b2.id]
499
+ f2.save
500
+ f2_rel = Foo.find(f2.id)
501
+ assert_equal 1, f2_rel.bars.length
502
+ assert_equal 1, f2_rel.other_bars.length
503
+
504
+ b2.delete
505
+
506
+ f2_rel = Foo.find(f2.id)
507
+ assert_equal 1, f2_rel.bars.length
508
+ assert_equal 0, f2_rel.other_bars.length
509
+ assert_equal b1.id, f2_rel.bars.first.id
510
+
511
+ end
512
+
513
+ def test_has_many_with_belongs_to
514
+ f1 = Foo.new()
515
+ f1.save
516
+ f2 = Foo.new()
517
+ f2.save
518
+ f3 = Foo.new()
519
+ f3.save
520
+
521
+ b1 = Bar.new({:other_foo_id => f1.id, :foo_id => f3.id})
522
+ b1.save
523
+ b2 = Bar.new({:other_foo_id => f2.id, :foo_id => f1.id})
524
+ b2.save
525
+
526
+ f1_rel = Foo.find(f1.id)
527
+ f2_rel = Foo.find(f2.id)
528
+ f3_rel = Foo.find(f3.id)
529
+
530
+ assert_equal 1, f1_rel.bars.length
531
+ assert_equal b1.id, f1_rel.other_bars.first.id
532
+ assert_equal b1.id, f3_rel.bars.first.id
533
+
534
+ assert_equal b2.id, f1_rel.bars.first.id
535
+ assert_equal b2.id, f2_rel.other_bars.first.id
536
+
537
+ #test update
538
+ b1.foo_id = f2.id
539
+ b1.save
540
+
541
+
542
+
543
+ f3_rel = Foo.find(f3.id)
544
+ assert f3_rel.bars.empty?
545
+ f2_rel = Foo.find(f2.id)
546
+ assert_equal b1.id, f2_rel.bars.first.id
547
+
548
+ b1.delete
549
+ f1_rel = Foo.find(f1.id)
550
+ assert f1_rel.other_bars.empty?
551
+ end
454
552
  end
455
553
 
metadata CHANGED
@@ -1,133 +1,176 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: arrest
3
- version: !ruby/object:Gem::Version
4
- version: 0.0.16
3
+ version: !ruby/object:Gem::Version
4
+ hash: 61
5
5
  prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 17
10
+ version: 0.0.17
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - Axel Tetzlaff
9
14
  autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
- date: 2012-01-18 00:00:00.000000000Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
17
+
18
+ date: 2012-01-24 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
15
21
  name: json
16
- requirement: &18586060 !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
17
24
  none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
22
32
  type: :runtime
23
- prerelease: false
24
- version_requirements: *18586060
25
- - !ruby/object:Gem::Dependency
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
26
35
  name: faraday
27
- requirement: &18585500 !ruby/object:Gem::Requirement
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
28
38
  none: false
29
- requirements:
30
- - - =
31
- - !ruby/object:Gem::Version
39
+ requirements:
40
+ - - "="
41
+ - !ruby/object:Gem::Version
42
+ hash: 9
43
+ segments:
44
+ - 0
45
+ - 7
46
+ - 5
32
47
  version: 0.7.5
33
48
  type: :runtime
34
- prerelease: false
35
- version_requirements: *18585500
36
- - !ruby/object:Gem::Dependency
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
37
51
  name: activemodel
38
- requirement: &18585000 !ruby/object:Gem::Requirement
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
39
54
  none: false
40
- requirements:
55
+ requirements:
41
56
  - - ~>
42
- - !ruby/object:Gem::Version
43
- version: '3'
57
+ - !ruby/object:Gem::Version
58
+ hash: 5
59
+ segments:
60
+ - 3
61
+ version: "3"
44
62
  type: :runtime
45
- prerelease: false
46
- version_requirements: *18585000
47
- - !ruby/object:Gem::Dependency
63
+ version_requirements: *id003
64
+ - !ruby/object:Gem::Dependency
48
65
  name: bundler
49
- requirement: &18584540 !ruby/object:Gem::Requirement
66
+ prerelease: false
67
+ requirement: &id004 !ruby/object:Gem::Requirement
50
68
  none: false
51
- requirements:
52
- - - ! '>='
53
- - !ruby/object:Gem::Version
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 23
73
+ segments:
74
+ - 1
75
+ - 0
76
+ - 0
54
77
  version: 1.0.0
55
78
  type: :development
56
- prerelease: false
57
- version_requirements: *18584540
58
- - !ruby/object:Gem::Dependency
79
+ version_requirements: *id004
80
+ - !ruby/object:Gem::Dependency
59
81
  name: rake
60
- requirement: &18584160 !ruby/object:Gem::Requirement
82
+ prerelease: false
83
+ requirement: &id005 !ruby/object:Gem::Requirement
61
84
  none: false
62
- requirements:
63
- - - ! '>='
64
- - !ruby/object:Gem::Version
65
- version: '0'
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ hash: 3
89
+ segments:
90
+ - 0
91
+ version: "0"
66
92
  type: :development
67
- prerelease: false
68
- version_requirements: *18584160
69
- - !ruby/object:Gem::Dependency
93
+ version_requirements: *id005
94
+ - !ruby/object:Gem::Dependency
70
95
  name: rdoc
71
- requirement: &18583700 !ruby/object:Gem::Requirement
96
+ prerelease: false
97
+ requirement: &id006 !ruby/object:Gem::Requirement
72
98
  none: false
73
- requirements:
74
- - - ! '>='
75
- - !ruby/object:Gem::Version
76
- version: '0'
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ hash: 3
103
+ segments:
104
+ - 0
105
+ version: "0"
77
106
  type: :development
78
- prerelease: false
79
- version_requirements: *18583700
80
- - !ruby/object:Gem::Dependency
107
+ version_requirements: *id006
108
+ - !ruby/object:Gem::Dependency
81
109
  name: rspec
82
- requirement: &18583040 !ruby/object:Gem::Requirement
110
+ prerelease: false
111
+ requirement: &id007 !ruby/object:Gem::Requirement
83
112
  none: false
84
- requirements:
113
+ requirements:
85
114
  - - ~>
86
- - !ruby/object:Gem::Version
87
- version: '2'
115
+ - !ruby/object:Gem::Version
116
+ hash: 7
117
+ segments:
118
+ - 2
119
+ version: "2"
88
120
  type: :development
89
- prerelease: false
90
- version_requirements: *18583040
91
- - !ruby/object:Gem::Dependency
121
+ version_requirements: *id007
122
+ - !ruby/object:Gem::Dependency
92
123
  name: rr
93
- requirement: &18582580 !ruby/object:Gem::Requirement
124
+ prerelease: false
125
+ requirement: &id008 !ruby/object:Gem::Requirement
94
126
  none: false
95
- requirements:
96
- - - ! '>='
97
- - !ruby/object:Gem::Version
98
- version: '0'
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ hash: 3
131
+ segments:
132
+ - 0
133
+ version: "0"
99
134
  type: :development
100
- prerelease: false
101
- version_requirements: *18582580
102
- - !ruby/object:Gem::Dependency
135
+ version_requirements: *id008
136
+ - !ruby/object:Gem::Dependency
103
137
  name: simplecov
104
- requirement: &18581960 !ruby/object:Gem::Requirement
138
+ prerelease: false
139
+ requirement: &id009 !ruby/object:Gem::Requirement
105
140
  none: false
106
- requirements:
107
- - - ! '>='
108
- - !ruby/object:Gem::Version
109
- version: '0'
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ hash: 3
145
+ segments:
146
+ - 0
147
+ version: "0"
110
148
  type: :development
111
- prerelease: false
112
- version_requirements: *18581960
113
- - !ruby/object:Gem::Dependency
149
+ version_requirements: *id009
150
+ - !ruby/object:Gem::Dependency
114
151
  name: rack
115
- requirement: &18581440 !ruby/object:Gem::Requirement
152
+ prerelease: false
153
+ requirement: &id010 !ruby/object:Gem::Requirement
116
154
  none: false
117
- requirements:
118
- - - ! '>='
119
- - !ruby/object:Gem::Version
120
- version: '0'
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ hash: 3
159
+ segments:
160
+ - 0
161
+ version: "0"
121
162
  type: :development
122
- prerelease: false
123
- version_requirements: *18581440
163
+ version_requirements: *id010
124
164
  description: Consume a rest API in a AR like fashion
125
- email:
165
+ email:
126
166
  - axel.tetzlaff@fortytools.com
127
167
  executables: []
168
+
128
169
  extensions: []
170
+
129
171
  extra_rdoc_files: []
130
- files:
172
+
173
+ files:
131
174
  - .gitignore
132
175
  - .rspec
133
176
  - Gemfile
@@ -138,10 +181,13 @@ files:
138
181
  - lib/arrest/abstract_resource.rb
139
182
  - lib/arrest/attributes/attribute.rb
140
183
  - lib/arrest/attributes/belongs_to.rb
184
+ - lib/arrest/attributes/belongs_to_attribute.rb
141
185
  - lib/arrest/attributes/converter.rb
142
186
  - lib/arrest/attributes/has_attributes.rb
187
+ - lib/arrest/attributes/has_many_attribute.rb
143
188
  - lib/arrest/attributes/nested_attribute.rb
144
189
  - lib/arrest/attributes/polymorphic_attribute.rb
190
+ - lib/arrest/class_utils.rb
145
191
  - lib/arrest/exceptions.rb
146
192
  - lib/arrest/handler.rb
147
193
  - lib/arrest/helper/child_collection.rb
@@ -164,28 +210,45 @@ files:
164
210
  - test/nested_resource.rb
165
211
  - test/unit.rb
166
212
  - test/validations.rb
167
- homepage: ''
213
+ homepage: ""
168
214
  licenses: []
215
+
169
216
  post_install_message:
170
217
  rdoc_options: []
171
- require_paths:
218
+
219
+ require_paths:
172
220
  - lib
173
- required_ruby_version: !ruby/object:Gem::Requirement
221
+ required_ruby_version: !ruby/object:Gem::Requirement
174
222
  none: false
175
- requirements:
176
- - - ! '>='
177
- - !ruby/object:Gem::Version
178
- version: '0'
179
- required_rubygems_version: !ruby/object:Gem::Requirement
223
+ requirements:
224
+ - - ">="
225
+ - !ruby/object:Gem::Version
226
+ hash: 3
227
+ segments:
228
+ - 0
229
+ version: "0"
230
+ required_rubygems_version: !ruby/object:Gem::Requirement
180
231
  none: false
181
- requirements:
182
- - - ! '>='
183
- - !ruby/object:Gem::Version
184
- version: '0'
232
+ requirements:
233
+ - - ">="
234
+ - !ruby/object:Gem::Version
235
+ hash: 3
236
+ segments:
237
+ - 0
238
+ version: "0"
185
239
  requirements: []
240
+
186
241
  rubyforge_project: arrest
187
242
  rubygems_version: 1.8.10
188
243
  signing_key:
189
244
  specification_version: 3
190
245
  summary: Another ruby rest client
191
- test_files: []
246
+ test_files:
247
+ - spec/arrest_spec.rb
248
+ - spec/spec_helper.rb
249
+ - spec/support/models/user.rb
250
+ - test/has_attributed.rb
251
+ - test/models.rb
252
+ - test/nested_resource.rb
253
+ - test/unit.rb
254
+ - test/validations.rb