rod-rest 0.0.1.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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