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