rod-rest 0.0.1.1 → 0.5.0

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.
@@ -6,7 +6,7 @@ module Rod
6
6
  include Enumerable
7
7
  attr_reader :size
8
8
 
9
- # Initializes a CollectionPorxy.
9
+ # Initializes a CollectionProxy.
10
10
  # * +:proxy+ - the object this collection belongs to
11
11
  # * +:association_name+ - the name of proxie's plural association this collection is returned for
12
12
  # * +:size+ - the size of the collection
@@ -16,6 +16,18 @@ module Rod
16
16
  @association_name = association_name
17
17
  @size = size
18
18
  @client = client
19
+ @cache = []
20
+ end
21
+
22
+ # Detailed description of the object, i.e.
23
+ # Rod::Rest::CollectionProxy<Car#drivers[5]>
24
+ def inspect
25
+ "#{self.class.name}<#{@proxy.type}\##{@association_name}[#{@size}]>"
26
+ end
27
+
28
+ # Short description of the collection, i.e. [5-elements].
29
+ def to_s
30
+ "[#{@size}-elements]"
19
31
  end
20
32
 
21
33
  # Returns true if the collection is empty (i.e. its size == 0).
@@ -26,22 +38,32 @@ module Rod
26
38
  # Returns the index-th element of the collection.
27
39
  def [](index)
28
40
  begin
29
- @client.fetch_related_object(@proxy,@association_name,index)
41
+ if Range === index
42
+ @cache[index] = @client.fetch_related_objects(@proxy,@association_name,index)
43
+ else
44
+ return @cache[index] unless @cache[index].nil?
45
+ @cache[index] = @client.fetch_related_object(@proxy,@association_name,index)
46
+ end
30
47
  rescue MissingResource
31
48
  nil
32
49
  end
33
50
  end
34
51
 
52
+ # Returns the first element of the collection.
53
+ def first
54
+ self.size > 0 ? self[0] : nil
55
+ end
56
+
35
57
  # Returns the last element of the collection.
36
58
  def last
37
- size > 0 ? self[size - 1] : nil
59
+ self.size > 0 ? self[size - 1] : nil
38
60
  end
39
61
 
40
62
  # Iterates over the elements of the collection.
41
63
  def each
42
64
  if block_given?
43
- @size.times do |index|
44
- yield self[index]
65
+ self[0..@size-1].each do |object|
66
+ yield object
45
67
  end
46
68
  else
47
69
  enum_for(:each)
@@ -1,5 +1,5 @@
1
1
  module Rod
2
2
  module Rest
3
- VERSION = "0.0.1.1"
3
+ VERSION = "0.5.0"
4
4
  end
5
5
  end
@@ -4,5 +4,6 @@ module Rod
4
4
  class APIError < RuntimeError; end
5
5
  class InvalidData < RuntimeError; end
6
6
  class UnknownResource < RuntimeError; end
7
+ class CacheMissed < RuntimeError; end
7
8
  end
8
9
  end
@@ -22,6 +22,16 @@ module Rod
22
22
  @resources
23
23
  end
24
24
 
25
+ # Returns the description of the metadata and the class name.
26
+ def inspect
27
+ "#{self.class}<#{@description.inspect}>"
28
+ end
29
+
30
+ # Returns the description of the metadata.
31
+ def to_s
32
+ @description.to_s
33
+ end
34
+
25
35
  private
26
36
  def create_resource_descriptions(hash_description)
27
37
  hash_description.map do |name,description|
@@ -15,6 +15,18 @@ module Rod
15
15
  def indexed?
16
16
  !! @index
17
17
  end
18
+
19
+ # Detailed description of the porperty, i.e.
20
+ # Rod::Rest::PropertyMetadata<name>
21
+ def inspect
22
+ indexed = indexed? ? ",indexed" : ""
23
+ "#{self.class}<#{@name}#{indexed}>"
24
+ end
25
+
26
+ # Short description of the poperty.
27
+ def to_s
28
+ "'#{@name}' property"
29
+ end
18
30
  end
19
31
  end
20
32
  end
@@ -38,8 +38,13 @@ module Rod
38
38
  private
39
39
  def build_class(metadata)
40
40
  Class.new do
41
+ class_variable_set("@@metadata",metadata)
42
+
41
43
  attr_reader :type,:rod_id
42
44
 
45
+ # Initialize the object with its +rod_it+, +type+ and the +client+
46
+ # used to fetch the referenced objects. Use +collection_proxy_factory+
47
+ # to create the referenced objects.
43
48
  def initialize(rod_id,type,client,collection_proxy_factory)
44
49
  @rod_id = rod_id
45
50
  @type = type
@@ -47,6 +52,22 @@ module Rod
47
52
  @collection_proxy_factory = collection_proxy_factory
48
53
  end
49
54
 
55
+ # Detailed description of the object, e.g.
56
+ # Proxy[Car]<rod_id:1,brand:Mercedes><owner:Person:1><drivers[3]>
57
+ def inspect
58
+ result = "Proxy[#{@type}]"
59
+ result << "<#{inspect_fields}>"
60
+ result << "<#{inspect_singular_associations}>"
61
+ result << "<#{inspect_plural_associations}>"
62
+ result
63
+ end
64
+
65
+ # Only reports the type of the proxy and its rod_id, e.g.
66
+ # Car[1]
67
+ def to_s
68
+ "#{@type}[#{@rod_id}]"
69
+ end
70
+
50
71
  metadata.fields.each do |field|
51
72
  attr_reader field.symbolic_name
52
73
  end
@@ -65,10 +86,34 @@ module Rod
65
86
  metadata.plural_associations.each do |association|
66
87
  class_eval <<-END
67
88
  def #{association.name}
68
- @collection_proxy_factory.new(self,"#{association.name}",@_#{association.name}_count,@client)
89
+ if defined?(@#{association.name})
90
+ return @#{association.name}
91
+ end
92
+ @#{association.name} = @collection_proxy_factory.new(self,"#{association.name}",@_#{association.name}_count,@client)
69
93
  end
70
94
  END
71
95
  end
96
+
97
+ private
98
+ def inspect_fields
99
+ @@metadata.fields.map do |field|
100
+ "#{field.name}:#{self.send(field.symbolic_name)}"
101
+ end.join(",")
102
+ end
103
+
104
+ def inspect_singular_associations
105
+ @@metadata.singular_associations.map do |association|
106
+ description = instance_variable_get("@_#{association.name}_description")
107
+ "#{association.name}:#{description[:type]}:#{description[:rod_id]}"
108
+ end.join(",")
109
+ end
110
+
111
+ def inspect_plural_associations
112
+ @@metadata.plural_associations.map do |association|
113
+ count = instance_variable_get("@_#{association.name}_count")
114
+ "#{association.name}[#{count}]"
115
+ end.join(",")
116
+ end
72
117
  end
73
118
  end
74
119
 
@@ -0,0 +1,65 @@
1
+ require 'rod/rest/exception'
2
+
3
+ module Rod
4
+ module Rest
5
+ # Cache used to store proxy objects.
6
+ class ProxyCache
7
+ # Initializes empty cache.
8
+ def initialize()
9
+ @cache_implementation = {}
10
+ end
11
+
12
+ # Returns true if the described object is in the cache.
13
+ def has_key?(description)
14
+ check_description(description)
15
+ @cache_implementation.has_key?(description_signature(description))
16
+ end
17
+
18
+ # Returns the object stored in the cache. Raises CacheMissed exception if
19
+ # the result is nil.
20
+ def [](description)
21
+ check_description(description)
22
+ begin
23
+ @cache_implementation.fetch(description_signature(description))
24
+ rescue KeyError
25
+ raise CacheMissed.new(missing_entry_message(description))
26
+ end
27
+ end
28
+
29
+ # Store the +object+ in the cache.
30
+ def store(object)
31
+ check_object(object)
32
+ @cache_implementation[description_signature(rod_id: object.rod_id,type: object.type)] = object
33
+ end
34
+
35
+ private
36
+ def description_signature(description)
37
+ "#{description[:rod_id]},#{description[:type]}"
38
+ end
39
+
40
+ def check_object(object)
41
+ if !object.respond_to?(:rod_id) || !object.respond_to?(:type)
42
+ raise InvalidData.new(invalid_object_message(object))
43
+ end
44
+ end
45
+
46
+ def check_description(description)
47
+ if !description.respond_to?(:has_key?) || !description.has_key?(:rod_id) || !description.has_key?(:type)
48
+ raise InvalidData.new(invalid_description_message(description))
49
+ end
50
+ end
51
+
52
+ def missing_entry_message(description)
53
+ "No entry for object rod_id:#{description[:rod_id]} type:#{description[:type]}"
54
+ end
55
+
56
+ def invalid_object_message(object)
57
+ "The object cannot be stored in the cache: #{object}."
58
+ end
59
+
60
+ def invalid_description_message(description)
61
+ "The description of the object is invalid: #{description}."
62
+ end
63
+ end
64
+ end
65
+ end
@@ -7,8 +7,10 @@ module Rod
7
7
  # +client+.
8
8
  # Options:
9
9
  # * proxy_class - the class used to create the resource proxy factories.
10
+ # * cache - used to cache created proxy instances
10
11
  def initialize(metadata,client,options={})
11
12
  proxy_class = options[:proxy_class] || Proxy
13
+ @cache = options[:cache]
12
14
  @proxies = {}
13
15
  metadata.each do |resource_metadata|
14
16
  @proxies[resource_metadata.name] = proxy_class.new(resource_metadata,client)
@@ -17,6 +19,7 @@ module Rod
17
19
 
18
20
  # Build new object-proxy from the hash-like +object_description+.
19
21
  def build(object_description)
22
+ return from_cache(object_description) if in_cache?(object_description)
20
23
  check_type(object_description[:type])
21
24
  @proxies[object_description[:type]].new(object_description)
22
25
  end
@@ -25,6 +28,15 @@ module Rod
25
28
  def check_type(type)
26
29
  raise UnknownResource.new(type) unless @proxies.has_key?(type)
27
30
  end
31
+
32
+ def from_cache(description)
33
+ @cache[description]
34
+ end
35
+
36
+ def in_cache?(description)
37
+ return false if @cache.nil?
38
+ @cache.has_key?(description)
39
+ end
28
40
  end
29
41
  end
30
42
  end
@@ -8,6 +8,7 @@ module Rod
8
8
  # * +property_factory+ - factory used to create descriptions of the
9
9
  # properties
10
10
  def initialize(name,description,options={})
11
+ @description = description
11
12
  @name = name.to_s
12
13
  @property_factory = options[:property_factory] || PropertyMetadata
13
14
  @fields = create_properties(description[:fields])
@@ -17,6 +18,16 @@ module Rod
17
18
  @indexed_properties = @properties.select{|p| p.indexed? }
18
19
  end
19
20
 
21
+ # Description of the metadata.
22
+ def inspect
23
+ @description.inspect
24
+ end
25
+
26
+ # Description of the metadata.
27
+ def to_s
28
+ @description.to_s
29
+ end
30
+
20
31
  private
21
32
  def create_properties(description)
22
33
  if description
@@ -82,8 +82,9 @@ module Rod
82
82
  sleep 0.5
83
83
  end
84
84
 
85
- after(:all) do
86
- #Thread.join
85
+ example "Client#inspect reports host and port" do
86
+ client.inspect.should match(/4567/)
87
+ client.inspect.should match(/localhost/)
87
88
  end
88
89
 
89
90
  example "API serves the metadata" do
@@ -99,38 +100,44 @@ module Rod
99
100
  end
100
101
  end
101
102
 
102
- example "Schumaher might be retrieved by id" do
103
- schumaher_in_rod = Person.find_by_name(SCHUMAHER_NAME)
104
- schumaher_via_api = client.find_person(schumaher_in_rod.rod_id)
105
- verify_person_equality(schumaher_via_api,schumaher_in_rod)
106
- end
103
+ describe "with cars and drivers loaded from Rod" do
104
+ let(:schumaher_in_rod) { Person.find_by_name(SCHUMAHER_NAME) }
105
+ let(:kubica_in_rod) { Person.find_by_name(KUBICA_NAME) }
106
+ let(:mercedes_in_rod) { Car.find_by_brand(MERCEDES_300_NAME) }
107
107
 
108
- example "Kubica might be retrieved by name" do
109
- kubica_in_rod = Person.find_by_name(KUBICA_NAME)
110
- kubica_via_api = client.find_people_by_name(KUBICA_NAME).first
111
- verify_person_equality(kubica_via_api,kubica_in_rod)
112
- end
108
+ example "Schumaher might be retrieved by id" do
109
+ schumaher_via_api = client.find_person(schumaher_in_rod.rod_id)
110
+ verify_person_equality(schumaher_via_api,schumaher_in_rod)
111
+ end
113
112
 
114
- example "Mercedes might be retrieved by id" do
115
- mercedes_in_rod = Car.find_by_brand(MERCEDES_300_NAME)
116
- mercedes_via_api = client.find_car(mercedes_in_rod.rod_id)
117
- mercedes_via_api.brand.should == mercedes_in_rod.brand
118
- end
113
+ example "Kubica might be retrieved by name" do
114
+ kubica_via_api = client.find_people_by_name(KUBICA_NAME).first
115
+ verify_person_equality(kubica_via_api,kubica_in_rod)
116
+ end
119
117
 
120
- example "Mercedes owner is retrieved properly" do
121
- mercedes_in_rod = Car.find_by_brand(MERCEDES_300_NAME)
122
- schumaher_in_rod = Person.find_by_name(SCHUMAHER_NAME)
123
- mercedes_via_api = client.find_car(mercedes_in_rod.rod_id)
124
- verify_person_equality(mercedes_via_api.owner,schumaher_in_rod)
125
- end
118
+ example "Mercedes might be retrieved by id" do
119
+ mercedes_via_api = client.find_car(mercedes_in_rod.rod_id)
120
+ mercedes_via_api.brand.should == mercedes_in_rod.brand
121
+ end
122
+
123
+ example "Mercedes owner is retrieved properly" do
124
+ mercedes_via_api = client.find_car(mercedes_in_rod.rod_id)
125
+ verify_person_equality(mercedes_via_api.owner,schumaher_in_rod)
126
+ end
126
127
 
127
- example "Mercedes drivers are retrieved properly" do
128
- mercedes_in_rod = Car.find_by_brand(MERCEDES_300_NAME)
129
- schumaher_in_rod = Person.find_by_name(SCHUMAHER_NAME)
130
- kubica_in_rod = Person.find_by_name(KUBICA_NAME)
131
- mercedes_via_api = client.find_car(mercedes_in_rod.rod_id)
132
- verify_person_equality(mercedes_via_api.drivers[0],schumaher_in_rod)
133
- verify_person_equality(mercedes_via_api.drivers[1],kubica_in_rod)
128
+ example "Mercedes drivers are retrieved properly" do
129
+ mercedes_via_api = client.find_car(mercedes_in_rod.rod_id)
130
+ verify_person_equality(mercedes_via_api.drivers[0],schumaher_in_rod)
131
+ verify_person_equality(mercedes_via_api.drivers[1],kubica_in_rod)
132
+ end
133
+
134
+ example "Mercedes drivers might be iterated over" do
135
+ mercedes_via_api = client.find_car(mercedes_in_rod.rod_id)
136
+ expected_drivers = [schumaher_in_rod,kubica_in_rod]
137
+ mercedes_via_api.drivers.zip(expected_drivers).each do |driver,expected_driver|
138
+ verify_person_equality(driver,expected_driver)
139
+ end
140
+ end
134
141
  end
135
142
  end
136
143
  end
@@ -82,35 +82,88 @@ module Rod
82
82
  end
83
83
  end
84
84
 
85
- describe "GET /cars/1" do
85
+ describe "with three cars" do
86
86
  let(:mercedes_300_id) { 1 }
87
- let(:audi_a4_id) { 2 }
88
87
  let(:mercedes_300) { Object.new }
89
- let(:mercedes_300_response) { Object.new.to_s }
88
+ let(:audi_a4_id) { 2 }
89
+ let(:audi_a4) { Object.new }
90
+ let(:fiat_panda_id) { 3 }
91
+ let(:fiat_panda) { Object.new }
92
+ let(:invalid_id) { 4 }
90
93
 
91
94
  before do
92
95
  stub(resource).find_by_rod_id(mercedes_300_id) { mercedes_300 }
93
- stub(resource).find_by_rod_id(audi_a4_id) { nil }
94
- stub(serializer).serialize(mercedes_300) { mercedes_300_response }
96
+ stub(resource).find_by_rod_id(audi_a4_id) { audi_a4 }
97
+ stub(resource).find_by_rod_id(fiat_panda_id) { fiat_panda }
98
+ stub(resource).find_by_rod_id(invalid_id) { nil }
95
99
  end
96
100
 
97
- it "returns serialized car" do
98
- get "/#{resource_name}/#{mercedes_300_id}"
99
- last_response.status.should == 200
100
- last_response.body.should == mercedes_300_response
101
+ describe "GET /cars/1" do
102
+ let(:mercedes_300_response) { Object.new.to_s }
103
+
104
+ before do
105
+ stub(serializer).serialize(mercedes_300) { mercedes_300_response }
106
+ end
107
+
108
+ it "returns serialized car" do
109
+ get "/#{resource_name}/#{mercedes_300_id}"
110
+ last_response.status.should == 200
111
+ last_response.body.should == mercedes_300_response
112
+ end
113
+
114
+ it "returns 404 for non-existing car" do
115
+ get "/#{resource_name}/#{invalid_id}"
116
+ last_response.status.should == 404
117
+ end
101
118
  end
102
119
 
103
- it "returns 404 for non-existing car" do
104
- get "/#{resource_name}/#{audi_a4_id}"
105
- last_response.status.should == 404
120
+ describe "GET /cars/1..3" do
121
+ let(:collection_response) { Object.new.to_s }
122
+
123
+ before do
124
+ stub(serializer).serialize([mercedes_300,audi_a4,fiat_panda]) { collection_response }
125
+ end
126
+
127
+ it "returns collection of cars" do
128
+ get "/#{resource_name}/#{mercedes_300_id}..#{fiat_panda_id}"
129
+ last_response.status.should == 200
130
+ last_response.body.should == collection_response
131
+ end
132
+
133
+ it "only returns non-nil elements" do
134
+ get "/#{resource_name}/#{mercedes_300_id}..#{invalid_id}"
135
+ last_response.status.should == 200
136
+ last_response.body.should == collection_response
137
+ end
138
+ end
139
+
140
+ describe "GET /cars/1,3" do
141
+ let(:collection_response) { Object.new.to_s }
142
+
143
+ before do
144
+ stub(serializer).serialize([mercedes_300,fiat_panda]) { collection_response }
145
+ end
146
+
147
+ it "returns collection of cars" do
148
+ get "/#{resource_name}/#{mercedes_300_id},#{fiat_panda_id}"
149
+ last_response.status.should == 200
150
+ last_response.body.should == collection_response
151
+ end
152
+
153
+ it "only returns non-nil elements" do
154
+ get "/#{resource_name}/#{mercedes_300_id},#{fiat_panda_id},#{invalid_id}"
155
+ last_response.status.should == 200
156
+ last_response.body.should == collection_response
157
+ end
106
158
  end
107
159
  end
108
160
 
161
+
109
162
  describe "GET /cars/1/drivers" do
110
163
  let(:relation_name) { "drivers" }
111
164
  let(:mercedes_300_id) { 1 }
112
165
  let(:mercedes_300) { stub!.drivers_count { drivers_count }.subject }
113
- let(:audi_a4_id) { 2 }
166
+ let(:invalid_id) { 2 }
114
167
  let(:drivers_count) { 4 }
115
168
  let(:drivers_response){ Object.new.to_s }
116
169
  let(:plural_associations) { [ property1 ] }
@@ -118,7 +171,7 @@ module Rod
118
171
 
119
172
  before do
120
173
  stub(resource).find_by_rod_id(mercedes_300_id) { mercedes_300 }
121
- stub(resource).find_by_rod_id(audi_a4_id) { nil }
174
+ stub(resource).find_by_rod_id(invalid_id) { nil }
122
175
  stub(serializer).serialize({count: drivers_count}) { drivers_response }
123
176
  end
124
177
 
@@ -129,7 +182,7 @@ module Rod
129
182
  end
130
183
 
131
184
  it "returns 404 for non-existing car" do
132
- get "/#{resource_name}/#{audi_a4_id}/#{relation_name}"
185
+ get "/#{resource_name}/#{invalid_id}/#{relation_name}"
133
186
  last_response.status.should == 404
134
187
  end
135
188
 
@@ -139,43 +192,47 @@ module Rod
139
192
  end
140
193
  end
141
194
 
142
- describe "GET /cars/1/drivers/0" do
195
+ describe "with three drivers" do
143
196
  let(:relation_name) { "drivers" }
144
197
  let(:plural_associations) { [ drivers_property ] }
145
198
  let(:drivers_property) { stub!.name { relation_name }.subject }
146
199
 
147
200
  let(:mercedes_300_id) { 1 }
148
- let(:audi_a4_id) { 2 }
149
- let(:driver_index) { 0 }
150
- let(:invalid_driver_index){ 10 }
201
+ let(:invalid_id) { 2 }
202
+ let(:schumaher_index) { 0 }
203
+ let(:kubica_index) { 1 }
204
+ let(:alonzo_index) { 2 }
205
+ let(:invalid_driver_index){ 3 }
206
+ let(:mercedes_300) { stub!.drivers { drivers }.subject }
207
+ let(:drivers) { [schumaher,kubica,alonzo] }
208
+ let(:schumaher) { Object.new }
209
+ let(:kubica) { Object.new }
210
+ let(:alonzo) { Object.new }
151
211
 
152
212
  before do
153
213
  stub(resource).find_by_rod_id(mercedes_300_id) { mercedes_300 }
154
- stub(resource).find_by_rod_id(audi_a4_id) { nil }
214
+ stub(resource).find_by_rod_id(invalid_id) { nil }
155
215
  end
156
216
 
157
- it "returns 404 for non-existing car" do
158
- get "/#{resource_name}/#{audi_a4_id}/#{relation_name}/#{driver_index}"
159
- last_response.status.should == 404
160
- end
161
-
162
- it "returns 404 for non-existing relation" do
163
- get "/#{resource_name}/#{mercedes_300_id}/non_existing/#{driver_index}"
164
- last_response.status.should == 404
165
- end
166
-
167
- describe "with one driver" do
168
- let(:mercedes_300) { stub!.drivers { drivers }.subject }
169
- let(:drivers) { [schumaher] }
170
- let(:schumaher) { Object.new }
217
+ describe "GET /cars/1/drivers/0" do
171
218
  let(:schumaher_response) { Object.new.to_s }
172
219
 
173
220
  before do
174
221
  stub(serializer).serialize(schumaher) { schumaher_response }
175
222
  end
176
223
 
224
+ it "returns 404 for non-existing car" do
225
+ get "/#{resource_name}/#{invalid_id}/#{relation_name}/#{schumaher_index}"
226
+ last_response.status.should == 404
227
+ end
228
+
229
+ it "returns 404 for non-existing relation" do
230
+ get "/#{resource_name}/#{mercedes_300_id}/non_existing/#{schumaher_index}"
231
+ last_response.status.should == 404
232
+ end
233
+
177
234
  it "returns the serialized driver" do
178
- get "/#{resource_name}/#{mercedes_300_id}/#{relation_name}/#{driver_index}"
235
+ get "/#{resource_name}/#{mercedes_300_id}/#{relation_name}/#{schumaher_index}"
179
236
  last_response.status.should == 200
180
237
  last_response.body.should == schumaher_response
181
238
  end
@@ -185,6 +242,46 @@ module Rod
185
242
  last_response.status.should == 404
186
243
  end
187
244
  end
245
+
246
+ describe "GET /cars/1/drivers/0..2" do
247
+ let(:collection_response) { Object.new.to_s }
248
+
249
+ before do
250
+ stub(serializer).serialize(drivers) { collection_response }
251
+ end
252
+
253
+ it "returns the serialized drivers" do
254
+ get "/#{resource_name}/#{mercedes_300_id}/#{relation_name}/#{schumaher_index}..#{alonzo_index}"
255
+ last_response.status.should == 200
256
+ last_response.body.should == collection_response
257
+ end
258
+
259
+ it "only returns non-nil elements" do
260
+ get "/#{resource_name}/#{mercedes_300_id}/#{relation_name}/#{schumaher_index}..#{invalid_driver_index}"
261
+ last_response.status.should == 200
262
+ last_response.body.should == collection_response
263
+ end
264
+ end
265
+
266
+ describe "GET /cars/1/drivers/0,2" do
267
+ let(:collection_response) { Object.new.to_s }
268
+
269
+ before do
270
+ stub(serializer).serialize([schumaher,alonzo]) { collection_response }
271
+ end
272
+
273
+ it "returns the serialized drivers" do
274
+ get "/#{resource_name}/#{mercedes_300_id}/#{relation_name}/#{schumaher_index},#{alonzo_index}"
275
+ last_response.status.should == 200
276
+ last_response.body.should == collection_response
277
+ end
278
+
279
+ it "only returns non-nil elements" do
280
+ get "/#{resource_name}/#{mercedes_300_id}/#{relation_name}/#{schumaher_index},#{alonzo_index},#{invalid_driver_index}"
281
+ last_response.status.should == 200
282
+ last_response.body.should == collection_response
283
+ end
284
+ end
188
285
  end
189
286
  end
190
287