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.
- data/.gitignore +1 -0
- data/Gemfile.lock +1 -1
- data/Readme.md +110 -0
- data/changelog.txt +11 -0
- data/lib/rod/rest.rb +1 -0
- data/lib/rod/rest/api.rb +148 -50
- data/lib/rod/rest/client.rb +95 -23
- data/lib/rod/rest/collection_proxy.rb +27 -5
- data/lib/rod/rest/constants.rb +1 -1
- data/lib/rod/rest/exception.rb +1 -0
- data/lib/rod/rest/metadata.rb +10 -0
- data/lib/rod/rest/property_metadata.rb +12 -0
- data/lib/rod/rest/proxy.rb +46 -1
- data/lib/rod/rest/proxy_cache.rb +65 -0
- data/lib/rod/rest/proxy_factory.rb +12 -0
- data/lib/rod/rest/resource_metadata.rb +11 -0
- data/test/int/end_to_end.rb +37 -30
- data/test/spec/api.rb +132 -35
- data/test/spec/client.rb +137 -38
- data/test/spec/collection_proxy.rb +52 -3
- data/test/spec/metadata.rb +24 -0
- data/test/spec/property_metadata.rb +31 -6
- data/test/spec/proxy.rb +50 -2
- data/test/spec/proxy_cache.rb +80 -0
- data/test/spec/proxy_factory.rb +16 -1
- data/test/spec/resource_metadata.rb +12 -0
- metadata +6 -2
@@ -6,7 +6,7 @@ module Rod
|
|
6
6
|
include Enumerable
|
7
7
|
attr_reader :size
|
8
8
|
|
9
|
-
# Initializes a
|
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
|
-
|
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
|
-
|
44
|
-
yield
|
65
|
+
self[0..@size-1].each do |object|
|
66
|
+
yield object
|
45
67
|
end
|
46
68
|
else
|
47
69
|
enum_for(:each)
|
data/lib/rod/rest/constants.rb
CHANGED
data/lib/rod/rest/exception.rb
CHANGED
data/lib/rod/rest/metadata.rb
CHANGED
@@ -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
|
data/lib/rod/rest/proxy.rb
CHANGED
@@ -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
|
-
|
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
|
data/test/int/end_to_end.rb
CHANGED
@@ -82,8 +82,9 @@ module Rod
|
|
82
82
|
sleep 0.5
|
83
83
|
end
|
84
84
|
|
85
|
-
|
86
|
-
|
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
|
-
|
103
|
-
schumaher_in_rod
|
104
|
-
|
105
|
-
|
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
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
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
|
data/test/spec/api.rb
CHANGED
@@ -82,35 +82,88 @@ module Rod
|
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
|
-
describe "
|
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(:
|
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) {
|
94
|
-
stub(
|
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
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
-
|
104
|
-
|
105
|
-
|
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(:
|
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(
|
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}/#{
|
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 "
|
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(:
|
149
|
-
let(:
|
150
|
-
let(:
|
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(
|
214
|
+
stub(resource).find_by_rod_id(invalid_id) { nil }
|
155
215
|
end
|
156
216
|
|
157
|
-
|
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}/#{
|
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
|
|