strelka 0.0.1pre4 → 0.0.1.pre129
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/History.rdoc +1 -1
- data/IDEAS.rdoc +62 -0
- data/Manifest.txt +38 -7
- data/README.rdoc +124 -5
- data/Rakefile +22 -6
- data/bin/leash +102 -157
- data/contrib/hoetemplate/.autotest.erb +23 -0
- data/contrib/hoetemplate/History.rdoc.erb +4 -0
- data/contrib/hoetemplate/Manifest.txt.erb +8 -0
- data/contrib/hoetemplate/README.rdoc.erb +17 -0
- data/contrib/hoetemplate/Rakefile.erb +24 -0
- data/contrib/hoetemplate/data/file_name/apps/file_name_app +36 -0
- data/contrib/hoetemplate/data/file_name/templates/layout.tmpl.erb +13 -0
- data/contrib/hoetemplate/data/file_name/templates/top.tmpl.erb +8 -0
- data/contrib/hoetemplate/lib/file_name.rb.erb +18 -0
- data/contrib/hoetemplate/spec/file_name_spec.rb.erb +21 -0
- data/data/strelka/apps/hello-world +30 -0
- data/lib/strelka/app/defaultrouter.rb +49 -30
- data/lib/strelka/app/errors.rb +121 -0
- data/lib/strelka/app/exclusiverouter.rb +40 -0
- data/lib/strelka/app/filters.rb +18 -7
- data/lib/strelka/app/negotiation.rb +122 -0
- data/lib/strelka/app/parameters.rb +171 -14
- data/lib/strelka/app/paramvalidator.rb +751 -0
- data/lib/strelka/app/plugins.rb +66 -46
- data/lib/strelka/app/restresources.rb +499 -0
- data/lib/strelka/app/router.rb +73 -0
- data/lib/strelka/app/routing.rb +140 -18
- data/lib/strelka/app/templating.rb +12 -3
- data/lib/strelka/app.rb +174 -24
- data/lib/strelka/constants.rb +0 -20
- data/lib/strelka/exceptions.rb +29 -0
- data/lib/strelka/httprequest/acceptparams.rb +377 -0
- data/lib/strelka/httprequest/negotiation.rb +257 -0
- data/lib/strelka/httprequest.rb +155 -7
- data/lib/strelka/httpresponse/negotiation.rb +579 -0
- data/lib/strelka/httpresponse.rb +140 -0
- data/lib/strelka/logging.rb +4 -1
- data/lib/strelka/mixins.rb +53 -0
- data/lib/strelka.rb +22 -1
- data/spec/data/error.tmpl +1 -0
- data/spec/lib/constants.rb +0 -1
- data/spec/lib/helpers.rb +21 -0
- data/spec/strelka/app/defaultrouter_spec.rb +41 -35
- data/spec/strelka/app/errors_spec.rb +212 -0
- data/spec/strelka/app/exclusiverouter_spec.rb +220 -0
- data/spec/strelka/app/filters_spec.rb +196 -0
- data/spec/strelka/app/negotiation_spec.rb +73 -0
- data/spec/strelka/app/parameters_spec.rb +149 -0
- data/spec/strelka/app/paramvalidator_spec.rb +1059 -0
- data/spec/strelka/app/plugins_spec.rb +26 -19
- data/spec/strelka/app/restresources_spec.rb +393 -0
- data/spec/strelka/app/router_spec.rb +63 -0
- data/spec/strelka/app/routing_spec.rb +183 -9
- data/spec/strelka/app/templating_spec.rb +1 -2
- data/spec/strelka/app_spec.rb +265 -32
- data/spec/strelka/exceptions_spec.rb +53 -0
- data/spec/strelka/httprequest/acceptparams_spec.rb +282 -0
- data/spec/strelka/httprequest/negotiation_spec.rb +246 -0
- data/spec/strelka/httprequest_spec.rb +204 -14
- data/spec/strelka/httpresponse/negotiation_spec.rb +464 -0
- data/spec/strelka/httpresponse_spec.rb +114 -0
- data/spec/strelka/mixins_spec.rb +99 -0
- data.tar.gz.sig +1 -0
- metadata +175 -79
- metadata.gz.sig +2 -0
- data/IDEAS.textile +0 -174
- data/data/strelka/apps/strelka-admin +0 -65
- data/data/strelka/apps/strelka-setup +0 -26
- data/data/strelka/bootstrap-config.rb +0 -34
- data/data/strelka/templates/admin/console.tmpl +0 -21
- data/data/strelka/templates/layout.tmpl +0 -30
- data/lib/strelka/process.rb +0 -19
@@ -7,6 +7,7 @@ BEGIN {
|
|
7
7
|
$LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
|
8
8
|
}
|
9
9
|
|
10
|
+
require 'uri'
|
10
11
|
require 'rspec'
|
11
12
|
require 'spec/lib/helpers'
|
12
13
|
require 'strelka/httprequest'
|
@@ -27,27 +28,216 @@ describe Strelka::HTTPRequest do
|
|
27
28
|
reset_logging()
|
28
29
|
end
|
29
30
|
|
31
|
+
context "instance" do
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
33
|
+
before( :each ) do
|
34
|
+
@req = @request_factory.get( '/directory/userinfo/ged' )
|
35
|
+
end
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
it "knows what the request's parsed URI is" do
|
38
|
+
@req.uri.should be_a( URI )
|
39
|
+
@req.uri.to_s.should == 'http://localhost:8080/directory/userinfo/ged'
|
40
|
+
end
|
41
|
+
|
42
|
+
it "knows what Mongrel2 route it followed" do
|
43
|
+
@req.pattern.should == '/directory'
|
44
|
+
end
|
45
|
+
|
46
|
+
it "knows what the URI of the route handling the request is" do
|
47
|
+
@req.base_uri.should be_a( URI )
|
48
|
+
@req.base_uri.to_s.should == 'http://localhost:8080/directory'
|
49
|
+
end
|
50
|
+
|
51
|
+
it "knows what the path of the request past its route is" do
|
52
|
+
@req.app_path.should == '/userinfo/ged'
|
53
|
+
end
|
54
|
+
|
55
|
+
it "knows what HTTP verb the request used" do
|
56
|
+
@req.verb.should == :GET
|
57
|
+
end
|
58
|
+
|
59
|
+
it "can get and set notes for communication between plugins" do
|
60
|
+
@req.notes.should be_a( Hash )
|
61
|
+
@req.notes[:routing].should be_a( Hash )
|
62
|
+
@req.notes[:routing][:route].should be_a( Hash )
|
63
|
+
end
|
40
64
|
|
41
|
-
it "knows what Mongrel2 route it followed" do
|
42
|
-
@req.pattern.should == '/directory'
|
43
65
|
end
|
44
66
|
|
45
|
-
|
46
|
-
|
67
|
+
|
68
|
+
context "instance with a query string" do
|
69
|
+
|
70
|
+
before( :each ) do
|
71
|
+
@req = @request_factory.get( '/directory/userinfo/ged?limit=10;offset=20' )
|
72
|
+
end
|
73
|
+
|
74
|
+
it "knows what the request's parsed URI is" do
|
75
|
+
@req.uri.should be_a( URI )
|
76
|
+
@req.uri.to_s.should == 'http://localhost:8080/directory/userinfo/ged?limit=10;offset=20'
|
77
|
+
end
|
78
|
+
|
79
|
+
it "knows what Mongrel2 route it followed" do
|
80
|
+
@req.pattern.should == '/directory'
|
81
|
+
end
|
82
|
+
|
83
|
+
it "knows what the URI of the route handling the request is" do
|
84
|
+
@req.base_uri.should be_a( URI )
|
85
|
+
@req.base_uri.to_s.should == 'http://localhost:8080/directory'
|
86
|
+
end
|
87
|
+
|
88
|
+
it "knows what the path of the request past its route is" do
|
89
|
+
@req.app_path.should == '/userinfo/ged'
|
90
|
+
@req.app_path.should == '/userinfo/ged' # make sure the slice is non-destructive
|
91
|
+
end
|
92
|
+
|
93
|
+
it "knows what HTTP verb the request used" do
|
94
|
+
@req.verb.should == :GET
|
95
|
+
end
|
96
|
+
|
47
97
|
end
|
48
98
|
|
49
|
-
|
50
|
-
|
99
|
+
|
100
|
+
describe "request-parameter parsing" do
|
101
|
+
|
102
|
+
context "a GET request" do
|
103
|
+
it "has an empty params Hash if the request doesn't have a query string " do
|
104
|
+
req = @request_factory.get( '/directory/path' )
|
105
|
+
req.params.should == {}
|
106
|
+
end
|
107
|
+
|
108
|
+
it "has a params Hash with the key/value pair in it if the query string has " +
|
109
|
+
"one key/value pair" do
|
110
|
+
req = @request_factory.get( '/directory/path?foo=bar' )
|
111
|
+
req.params.should == {'foo' => 'bar'}
|
112
|
+
end
|
113
|
+
|
114
|
+
it "has a params Hash with the key/value pairs in it if the query string has " +
|
115
|
+
"two pairs seperated with a an ampersand" do
|
116
|
+
req = @request_factory.get( '/directory/path?foo=bar&chunky=pork' )
|
117
|
+
req.params.should == {
|
118
|
+
'foo' => 'bar',
|
119
|
+
'chunky' => 'pork',
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
it "has a params Hash with the key/value pairs in it if the query string has " +
|
124
|
+
"two pairs with a semi-colon separator" do
|
125
|
+
req = @request_factory.get( '/directory/path?potato=gun;legume=bazooka' )
|
126
|
+
req.params.should == {
|
127
|
+
'potato' => 'gun',
|
128
|
+
'legume' => 'bazooka',
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
it "has a params Hash with an Array of values if the query string has two values " +
|
133
|
+
"for the same key" do
|
134
|
+
req = @request_factory.get( '/directory/path?foo=bar&foo=baz' )
|
135
|
+
req.params.should == {
|
136
|
+
'foo' => ['bar', 'baz'],
|
137
|
+
}
|
138
|
+
end
|
139
|
+
|
140
|
+
it "has a params Hash with one Array of values and a scalar value if the query " +
|
141
|
+
"string has three values and two keys" do
|
142
|
+
req = @request_factory.get( '/directory/path?foo=bar&foo=pork;mirror=sequel' )
|
143
|
+
req.params.should == {
|
144
|
+
'foo' => ['bar', 'pork'],
|
145
|
+
'mirror' => 'sequel',
|
146
|
+
}
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context "a POST request with a 'application/x-www-form-urlencoded' body" do
|
151
|
+
|
152
|
+
before( :each ) do
|
153
|
+
@req = @request_factory.post( '/directory/path', '',
|
154
|
+
'Content-type' => 'application/x-www-form-urlencoded' )
|
155
|
+
end
|
156
|
+
|
157
|
+
it "returns an empty Hash for an empty body" do
|
158
|
+
@req.body = ''
|
159
|
+
@req.params.should == {}
|
160
|
+
end
|
161
|
+
|
162
|
+
it "has a params Hash with the key/value pair in it if the form data has " +
|
163
|
+
"one key/value pair" do
|
164
|
+
@req.body = 'foo=bar'
|
165
|
+
@req.params.should == {'foo' => 'bar'}
|
166
|
+
end
|
167
|
+
|
168
|
+
it "has a params Hash with the key/value pairs in it if the form data has " +
|
169
|
+
"two pairs seperated with a an ampersand" do
|
170
|
+
@req.body = 'foo=bar&chunky=pork'
|
171
|
+
@req.params.should == {
|
172
|
+
'foo' => 'bar',
|
173
|
+
'chunky' => 'pork',
|
174
|
+
}
|
175
|
+
end
|
176
|
+
|
177
|
+
it "has a params Hash with the key/value pairs in it if the form data has " +
|
178
|
+
"two pairs with a semi-colon separator" do
|
179
|
+
@req.body = 'potato=gun;legume=bazooka'
|
180
|
+
@req.params.should == {
|
181
|
+
'potato' => 'gun',
|
182
|
+
'legume' => 'bazooka',
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
it "has a params Hash with an Array of values if the form data has two values " +
|
187
|
+
"for the same key" do
|
188
|
+
@req.body = 'foo=bar&foo=baz'
|
189
|
+
@req.params.should == { 'foo' => ['bar', 'baz'] }
|
190
|
+
end
|
191
|
+
|
192
|
+
it "has a params Hash with one Array of values and a scalar value if the form " +
|
193
|
+
"data has three values and two keys" do
|
194
|
+
@req.body = 'foo=bar&foo=pork;mirror=sequel'
|
195
|
+
@req.params.should == {
|
196
|
+
'foo' => ['bar', 'pork'],
|
197
|
+
'mirror' => 'sequel',
|
198
|
+
}
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context "a POST request with a 'multipart/form-data' body" do
|
203
|
+
|
204
|
+
before( :each ) do
|
205
|
+
@req = @request_factory.post( '/directory/path', '',
|
206
|
+
'Content-type' => 'multipart/form-data' )
|
207
|
+
end
|
208
|
+
|
209
|
+
it "returns nil for an empty body" do
|
210
|
+
pending "multipart/form-data support" do
|
211
|
+
@req.body = ''
|
212
|
+
@req.params.should be_nil()
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
context "a POST request with a 'application/json' body" do
|
220
|
+
before( :each ) do
|
221
|
+
@req = @request_factory.post( '/directory/path', '',
|
222
|
+
'Content-type' => 'application/json' )
|
223
|
+
end
|
224
|
+
|
225
|
+
it "returns nil for an empty body" do
|
226
|
+
@req.body = ''
|
227
|
+
@req.params.should be_nil()
|
228
|
+
end
|
229
|
+
|
230
|
+
it "has the JSON data as the params if it has a body with JSON object in it" do
|
231
|
+
data = {
|
232
|
+
'animal' => 'ducky',
|
233
|
+
'adjectives' => ['fluffy', 'puddle-ey'],
|
234
|
+
}
|
235
|
+
@req.body = Yajl.dump( data )
|
236
|
+
@req.params.should == data
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
|
51
241
|
end
|
52
242
|
|
53
243
|
end
|
@@ -0,0 +1,464 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
BEGIN {
|
5
|
+
require 'pathname'
|
6
|
+
basedir = Pathname.new( __FILE__ ).dirname.parent.parent.parent
|
7
|
+
|
8
|
+
libdir = basedir + "lib"
|
9
|
+
|
10
|
+
$LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
|
11
|
+
$LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
|
12
|
+
}
|
13
|
+
|
14
|
+
require 'rspec'
|
15
|
+
|
16
|
+
require 'spec/lib/helpers'
|
17
|
+
|
18
|
+
require 'strelka'
|
19
|
+
require 'strelka/httprequest/negotiation'
|
20
|
+
require 'strelka/httpresponse/negotiation'
|
21
|
+
|
22
|
+
|
23
|
+
#####################################################################
|
24
|
+
### C O N T E X T S
|
25
|
+
#####################################################################
|
26
|
+
|
27
|
+
describe Strelka::HTTPResponse::Negotiation do
|
28
|
+
|
29
|
+
before( :all ) do
|
30
|
+
setup_logging( :fatal )
|
31
|
+
@request_factory = Mongrel2::RequestFactory.new( route: '/service/user' )
|
32
|
+
end
|
33
|
+
|
34
|
+
after( :all ) do
|
35
|
+
reset_logging()
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
before( :each ) do
|
40
|
+
@app = Class.new( Strelka::App ) { plugins :negotiation }
|
41
|
+
@req = @request_factory.get( '/service/user/estark' )
|
42
|
+
@res = @req.response
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
describe "content-alternative callback methods" do
|
47
|
+
|
48
|
+
it "can provide blocks for bodies of several different mediatypes" do
|
49
|
+
@req.headers.accept = 'application/x-yaml, application/json; q=0.7, text/xml; q=0.2'
|
50
|
+
|
51
|
+
@res.for( 'application/json' ) { %{["a JSON dump"]} }
|
52
|
+
@res.for( 'application/x-yaml' ) { "---\na: YAML dump\n\n" }
|
53
|
+
|
54
|
+
@res.negotiated_body.should == "---\na: YAML dump\n\n"
|
55
|
+
@res.content_type.should == "application/x-yaml"
|
56
|
+
@res.header_data.should =~ /accept(?!-)/i
|
57
|
+
end
|
58
|
+
|
59
|
+
it "can provide a single block for bodies of several different mediatypes" do
|
60
|
+
@req.headers.accept = 'application/x-yaml; q=0.7, application/json; q=0.9'
|
61
|
+
|
62
|
+
@res.for( 'application/json', 'application/x-yaml' ) do
|
63
|
+
{ uuid: 'fc85e35b-c9c3-4675-a882-25bf98d11e1b', name: "Harlot's Garden" }
|
64
|
+
end
|
65
|
+
|
66
|
+
@res.negotiated_body.should == "{\"uuid\":\"fc85e35b-c9c3-4675-a882-25bf98d11e1b\"," +
|
67
|
+
"\"name\":\"Harlot's Garden\"}"
|
68
|
+
@res.content_type.should == "application/json"
|
69
|
+
@res.header_data.should =~ /accept(?!-)/i
|
70
|
+
end
|
71
|
+
|
72
|
+
it "can provide a block for bodies of several different symbolic mediatypes" do
|
73
|
+
@req.headers.accept = 'application/x-yaml; q=0.7, application/json; q=0.9'
|
74
|
+
|
75
|
+
@res.for( :json, :yaml ) do
|
76
|
+
{ uuid: 'fc85e35b-c9c3-4675-a882-25bf98d11e1b', name: "Harlot's Garden" }
|
77
|
+
end
|
78
|
+
|
79
|
+
@res.negotiated_body.should == "{\"uuid\":\"fc85e35b-c9c3-4675-a882-25bf98d11e1b\"," +
|
80
|
+
"\"name\":\"Harlot's Garden\"}"
|
81
|
+
@res.content_type.should == "application/json"
|
82
|
+
@res.header_data.should =~ /accept(?!-)/i
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
describe "automatic content transcoding" do
|
89
|
+
|
90
|
+
it "transcodes String entity bodies if the charset is not acceptable" do
|
91
|
+
@req.headers.accept_charset = 'koi8-r, koi8-u;q=0.9, utf-8;q=0.8'
|
92
|
+
|
93
|
+
@res.body = File.read( __FILE__, encoding: 'iso-8859-5' )
|
94
|
+
@res.content_type = 'text/plain'
|
95
|
+
|
96
|
+
@res.negotiated_body.encoding.should == Encoding::KOI8_R
|
97
|
+
@res.header_data.should =~ /accept-charset(?!-)/i
|
98
|
+
end
|
99
|
+
|
100
|
+
it "transcodes String entity bodies if the charset is not acceptable" do
|
101
|
+
@req.headers.accept_charset = 'utf-8'
|
102
|
+
|
103
|
+
@res.body = File.read( __FILE__, encoding: 'iso-8859-5' )
|
104
|
+
@res.content_type = 'application/json'
|
105
|
+
|
106
|
+
@res.negotiated_body.encoding.should == Encoding::UTF_8
|
107
|
+
@res.header_data.should =~ /accept-charset(?!-)/i
|
108
|
+
end
|
109
|
+
|
110
|
+
it "transcodes File entity bodies if the charset is not acceptable" do
|
111
|
+
pending "implementation of IO transcoding" do
|
112
|
+
@req.headers.accept_charset = 'koi8-r, koi8-u;q=0.9, utf-8;q=0.8'
|
113
|
+
|
114
|
+
@res.body = File.open( __FILE__, 'r:iso-8859-5' )
|
115
|
+
@res.content_type = 'text/plain'
|
116
|
+
|
117
|
+
@res.negotiated_body.encoding.should == Encoding::KOI8_R
|
118
|
+
@res.header_data.should =~ /accept-charset(?!-)/i
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
describe "language alternative callback methods" do
|
126
|
+
|
127
|
+
it "can provide blocks for alternative bodies of several different languages" do
|
128
|
+
@req.headers.accept = 'text/plain'
|
129
|
+
@req.headers.accept_language = 'de'
|
130
|
+
|
131
|
+
@res.puts( "the English body" )
|
132
|
+
@res.languages << :en
|
133
|
+
@res.content_type = 'text/plain'
|
134
|
+
@res.for_language( :de ) { "German translation" }
|
135
|
+
@res.for_language( :sl ) { "Slovenian translation" }
|
136
|
+
|
137
|
+
@res.negotiated_body.should == "German translation"
|
138
|
+
@res.languages.should == ["de"]
|
139
|
+
@res.header_data.should =~ /accept-language/i
|
140
|
+
end
|
141
|
+
|
142
|
+
it "can provide blocks for bodies of several different languages without setting a " +
|
143
|
+
"default entity body" do
|
144
|
+
@req.headers.accept = 'text/plain'
|
145
|
+
@req.headers.accept_language = 'de, en-gb;q=0.9, en;q=0.7'
|
146
|
+
|
147
|
+
@res.content_type = 'text/plain'
|
148
|
+
@res.for_language( :en ) { "English translation" }
|
149
|
+
@res.for_language( :de ) { "German translation" }
|
150
|
+
@res.for_language( :sl ) { "Slovenian translation" }
|
151
|
+
|
152
|
+
@res.negotiated_body.should == "German translation"
|
153
|
+
@res.languages.should == ["de"]
|
154
|
+
@res.header_data.should =~ /accept-language/i
|
155
|
+
end
|
156
|
+
|
157
|
+
it "can provide a single block for bodies of several different languages" do
|
158
|
+
@req.headers.accept_language = 'fr;q=0.9, de;q=0.7, en;q=0.7, pt'
|
159
|
+
translations = {
|
160
|
+
:pt => "Portuguese translation",
|
161
|
+
:fr => "French translation",
|
162
|
+
:de => "German translation",
|
163
|
+
:en => "English translation",
|
164
|
+
}
|
165
|
+
|
166
|
+
@res.content_type = 'text/plain'
|
167
|
+
@res.for_language( translations.keys ) do |lang|
|
168
|
+
translations[ lang.to_sym ]
|
169
|
+
end
|
170
|
+
|
171
|
+
@res.negotiated_body.should == "Portuguese translation"
|
172
|
+
@res.languages.should == ["pt"]
|
173
|
+
@res.header_data.should =~ /accept-language/i
|
174
|
+
end
|
175
|
+
|
176
|
+
it "calls the first block for requests with no accept-language header" do
|
177
|
+
@req.headers.delete( :accept_language )
|
178
|
+
@req.headers.accept = 'text/plain'
|
179
|
+
|
180
|
+
@res.content_type = 'text/plain'
|
181
|
+
@res.for_language( :en ) { "English translation" }
|
182
|
+
@res.for_language( :de ) { "German translation" }
|
183
|
+
@res.for_language( :sl ) { "Slovenian translation" }
|
184
|
+
|
185
|
+
@res.negotiated_body.should == "English translation"
|
186
|
+
@res.languages.should == ["en"]
|
187
|
+
@res.header_data.should =~ /accept-language/i
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
describe "content coding alternative callback methods" do
|
193
|
+
|
194
|
+
it "can provide blocks for content coding" do
|
195
|
+
@req.headers.accept = 'text/plain'
|
196
|
+
@req.headers.accept_encoding = 'gzip'
|
197
|
+
|
198
|
+
@res << "the text body"
|
199
|
+
@res.content_type = 'text/plain'
|
200
|
+
@res.for_encoding( :deflate ) { @res.body + " (deflated)" }
|
201
|
+
@res.for_encoding( :gzip ) { @res.body + " (gzipped)" }
|
202
|
+
|
203
|
+
@res.negotiated_body.should == "the text body (gzipped)"
|
204
|
+
@res.encodings.should include( "gzip" )
|
205
|
+
@res.header_data.should =~ /accept-encoding/i
|
206
|
+
@res.header_data.should_not =~ /identity/i
|
207
|
+
end
|
208
|
+
|
209
|
+
it "chooses the content coding with the highest qvalue" do
|
210
|
+
@req.headers.accept = 'text/plain'
|
211
|
+
@req.headers.accept_encoding = 'gzip;q=0.7, deflate'
|
212
|
+
|
213
|
+
@res << "the text body"
|
214
|
+
@res.content_type = 'text/plain'
|
215
|
+
@res.for_encoding( :deflate ) { @res.body + " (deflated)" }
|
216
|
+
@res.for_encoding( :gzip ) { @res.body + " (gzipped)" }
|
217
|
+
|
218
|
+
@res.negotiated_body.should == "the text body (deflated)"
|
219
|
+
@res.encodings.should include( "deflate" )
|
220
|
+
@res.header_data.should =~ /accept-encoding/i
|
221
|
+
@res.header_data.should_not =~ /identity/i
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
225
|
+
|
226
|
+
|
227
|
+
describe "content-type acceptance predicates" do
|
228
|
+
|
229
|
+
it "knows that it is acceptable if its content_type is in the list of accepted types " +
|
230
|
+
"in its request" do
|
231
|
+
@req.headers.accept = 'application/x-yaml, application/json; q=0.7'
|
232
|
+
@res.content_type = 'application/json'
|
233
|
+
|
234
|
+
@res.should have_acceptable_content_type()
|
235
|
+
end
|
236
|
+
|
237
|
+
it "knows that it is acceptable if its request doesn't have accepted types" do
|
238
|
+
@req.headers.delete( :accept )
|
239
|
+
@res.content_type = 'application/x-ruby-marshalled'
|
240
|
+
|
241
|
+
@res.should have_acceptable_content_type()
|
242
|
+
end
|
243
|
+
|
244
|
+
it "knows that it is acceptable if it doesn't have an originating request" do
|
245
|
+
res = Strelka::HTTPResponse.new( 'appid', 88 )
|
246
|
+
res.extend( Strelka::HTTPResponse::Negotiation )
|
247
|
+
res.content_type = 'application/x-ruby-marshalled'
|
248
|
+
|
249
|
+
res.should have_acceptable_content_type()
|
250
|
+
end
|
251
|
+
|
252
|
+
it "knows that it is not acceptable if its content_type isn't in the list of " +
|
253
|
+
"accepted types in its request" do
|
254
|
+
@req.headers.accept = 'application/x-yaml, application/json; q=0.7'
|
255
|
+
@res.content_type = 'application/x-ruby-marshalled'
|
256
|
+
|
257
|
+
@res.should_not have_acceptable_content_type()
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
describe "charset acceptance predicates" do
|
264
|
+
|
265
|
+
it "knows that it is acceptable if its explicit charset is in the list of accepted " +
|
266
|
+
"charsets in its request" do
|
267
|
+
@req.headers.accept_charset = 'iso-8859-5, utf-8;q=0.8'
|
268
|
+
@res.charset = 'iso-8859-5'
|
269
|
+
|
270
|
+
@res.should have_acceptable_charset()
|
271
|
+
end
|
272
|
+
|
273
|
+
it "knows that it is acceptable if its request doesn't have accepted types" do
|
274
|
+
@req.headers.delete( :accept_charset )
|
275
|
+
@res.charset = 'koi8-u'
|
276
|
+
@res.should have_acceptable_charset()
|
277
|
+
end
|
278
|
+
|
279
|
+
it "knows that it is acceptable if it doesn't have an originating request" do
|
280
|
+
res = Strelka::HTTPResponse.new( 'appid', 88 )
|
281
|
+
res.charset = 'iso8859-15'
|
282
|
+
|
283
|
+
res.should have_acceptable_charset()
|
284
|
+
end
|
285
|
+
|
286
|
+
it "knows that it is acceptable if its explicit charset is set to ascii-8bit" do
|
287
|
+
@req.headers.accept_charset = 'iso-8859-1, utf-8;q=0.8'
|
288
|
+
@res.content_type = 'image/jpeg'
|
289
|
+
@res.charset = Encoding::ASCII_8BIT
|
290
|
+
|
291
|
+
@res.should have_acceptable_charset()
|
292
|
+
end
|
293
|
+
|
294
|
+
it "knows that it is acceptable if no charset can be derived, but the list of " +
|
295
|
+
"acceptable charsets includes ISO8859-1" do
|
296
|
+
@req.headers.accept_charset = 'iso-8859-1, utf-8;q=0.8'
|
297
|
+
@res.content_type = 'text/plain'
|
298
|
+
@res.body = "some stuff".force_encoding( Encoding::ASCII_8BIT )
|
299
|
+
|
300
|
+
@res.should have_acceptable_charset()
|
301
|
+
end
|
302
|
+
|
303
|
+
it "knows that it is not acceptable if no charset can be derived, the content is a " +
|
304
|
+
"text subtype, and the list of acceptable charsets doesn't include ISO8859-1" do
|
305
|
+
@req.headers.accept_charset = 'iso-8859-15, utf-8;q=0.8'
|
306
|
+
@res.content_type = 'text/plain'
|
307
|
+
@res.body = "some stuff".force_encoding( Encoding::ASCII_8BIT )
|
308
|
+
|
309
|
+
@res.should_not have_acceptable_charset()
|
310
|
+
end
|
311
|
+
|
312
|
+
it "knows that it is not acceptable if its explicit charset isn't in the list of " +
|
313
|
+
"accepted charsets in its request" do
|
314
|
+
@req.headers.accept_charset = 'iso-8859-5, utf-8;q=0.8'
|
315
|
+
@res.charset = 'sjis'
|
316
|
+
|
317
|
+
@res.should_not have_acceptable_charset()
|
318
|
+
end
|
319
|
+
|
320
|
+
it "knows that it is not acceptable if the charset in its content-type header isn't in " +
|
321
|
+
"the list of accepted charsets in its request" do
|
322
|
+
@req.headers.accept_charset = 'iso-8859-5, utf-8;q=0.8'
|
323
|
+
@res.content_type = 'text/plain; charset=sjis'
|
324
|
+
|
325
|
+
@res.should_not have_acceptable_charset()
|
326
|
+
end
|
327
|
+
|
328
|
+
it "knows that it is not acceptable if the charset derived from its entity body isn't in " +
|
329
|
+
"the list of accepted charsets in its request" do
|
330
|
+
@req.headers.accept_charset = 'iso-8859-1, utf-8;q=0.8'
|
331
|
+
@res.content_type = 'text/plain'
|
332
|
+
@res.body = File.open( __FILE__, 'r:iso8859-5' )
|
333
|
+
|
334
|
+
@res.should_not have_acceptable_charset()
|
335
|
+
end
|
336
|
+
|
337
|
+
end
|
338
|
+
|
339
|
+
|
340
|
+
describe "language acceptance predicates" do
|
341
|
+
|
342
|
+
it "knows that it is acceptable if it has a single language that's in the list of " +
|
343
|
+
"languages accepted by its originating request" do
|
344
|
+
@req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
|
345
|
+
@res.languages << 'ja'
|
346
|
+
|
347
|
+
@res.should have_acceptable_language()
|
348
|
+
end
|
349
|
+
|
350
|
+
it "knows that it is acceptable if all of its multiple languages are in the list of " +
|
351
|
+
"languages accepted by its originating request" do
|
352
|
+
@req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
|
353
|
+
@res.languages << 'ja' << 'en-us'
|
354
|
+
|
355
|
+
@res.should have_acceptable_language()
|
356
|
+
end
|
357
|
+
|
358
|
+
# I'm not sure if this is what RFC1616 means. It might be that *all* of its languages
|
359
|
+
# have to be in the accept-language: list.
|
360
|
+
it "knows that it is acceptable if one of its multiple languages is in the " +
|
361
|
+
"list of languages accepted by its originating request" do
|
362
|
+
@req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
|
363
|
+
@res.languages << 'pt' << 'en'
|
364
|
+
|
365
|
+
@res.should have_acceptable_language()
|
366
|
+
end
|
367
|
+
|
368
|
+
it "knows that it is acceptable if it has a body but doesn't have a language set" do
|
369
|
+
@req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
|
370
|
+
@res.languages.clear
|
371
|
+
@res.puts( "Some content in an unspecified language." )
|
372
|
+
|
373
|
+
@res.should have_acceptable_language()
|
374
|
+
end
|
375
|
+
|
376
|
+
it "knows that it is acceptable if it has no body yet" do
|
377
|
+
@req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
|
378
|
+
@res.languages.clear
|
379
|
+
|
380
|
+
@res.should have_acceptable_language()
|
381
|
+
end
|
382
|
+
|
383
|
+
it "knows that it is acceptable if it doesn't have an originating request" do
|
384
|
+
res = Strelka::HTTPResponse.new( 'appid', 88 )
|
385
|
+
res.extend( Strelka::HTTPResponse::Negotiation )
|
386
|
+
res.languages << 'kh'
|
387
|
+
|
388
|
+
res.should have_acceptable_language()
|
389
|
+
end
|
390
|
+
|
391
|
+
it "knows that it is not acceptable if it has a single language that isn't in the " +
|
392
|
+
"list of languages accepted by its originating request" do
|
393
|
+
@req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
|
394
|
+
@res.languages << 'pt'
|
395
|
+
|
396
|
+
@res.should_not have_acceptable_language()
|
397
|
+
end
|
398
|
+
|
399
|
+
it "knows that it is not acceptable if it has multiple languages, none of which are " +
|
400
|
+
"in the list of languages accepted by its originating request" do
|
401
|
+
@req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
|
402
|
+
@res.languages << 'pt-br' << 'fr-ca'
|
403
|
+
|
404
|
+
@res.should_not have_acceptable_language()
|
405
|
+
end
|
406
|
+
|
407
|
+
end
|
408
|
+
|
409
|
+
|
410
|
+
describe "encoding acceptance predicates" do
|
411
|
+
|
412
|
+
it "knows that it is acceptable if its content coding is in the list of accepted " +
|
413
|
+
"codings in its originating request" do
|
414
|
+
@req.headers.accept_encoding = 'gzip;q=1.0, identity; q=0.5, *;q=0'
|
415
|
+
@res.encodings << 'gzip'
|
416
|
+
|
417
|
+
@res.should have_acceptable_encoding()
|
418
|
+
end
|
419
|
+
|
420
|
+
it "knows that it is acceptable if all of its content codings are in the list of accepted " +
|
421
|
+
"codings in its originating request" do
|
422
|
+
@req.headers.accept_encoding = 'gzip;q=1.0, frobnify;q=0.9, identity; q=0.5, *;q=0'
|
423
|
+
@res.encodings << 'gzip' << 'frobnify'
|
424
|
+
|
425
|
+
@res.should have_acceptable_encoding()
|
426
|
+
end
|
427
|
+
|
428
|
+
it "knows that it is not acceptable if one of its content codings is not in the list " +
|
429
|
+
"of accepted codings in its originating request" do
|
430
|
+
@req.headers.accept_encoding = 'gzip;q=1.0, identity; q=0.5, *;q=0'
|
431
|
+
@res.encodings << 'gzip' << 'frobnify'
|
432
|
+
|
433
|
+
@res.should_not have_acceptable_encoding()
|
434
|
+
end
|
435
|
+
|
436
|
+
it "knows that it is not acceptable if it doesn't have any explicit content codings " +
|
437
|
+
"and 'identity' is explicitly not accepted in its originating request" do
|
438
|
+
@req.headers.accept_encoding = 'gzip;q=1.0, identity; q=0'
|
439
|
+
@res.encodings.clear
|
440
|
+
|
441
|
+
@res.should_not have_acceptable_encoding()
|
442
|
+
end
|
443
|
+
|
444
|
+
it "knows that it is not acceptable if it doesn't have any explicit content codings " +
|
445
|
+
"and 'identity' is explicitly not accepted in its originating request" do
|
446
|
+
@req.headers.accept_encoding = 'gzip;q=1.0, identity; q=0'
|
447
|
+
@res.encodings.clear
|
448
|
+
|
449
|
+
@res.should_not have_acceptable_encoding()
|
450
|
+
end
|
451
|
+
|
452
|
+
it "knows that it is not acceptable if it doesn't have any explicit content codings, " +
|
453
|
+
"the wildcard content-coding is disallowed, and 'identity' is not explicitly accepted" do
|
454
|
+
@req.headers.accept_encoding = 'gzip;q=1.0, *;q=0'
|
455
|
+
@res.encodings.clear
|
456
|
+
|
457
|
+
@res.should_not have_acceptable_encoding()
|
458
|
+
end
|
459
|
+
|
460
|
+
end
|
461
|
+
|
462
|
+
|
463
|
+
end
|
464
|
+
|