openlogic-resourceful 1.2.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/History.txt +45 -0
- data/MIT-LICENSE +21 -0
- data/Manifest +46 -0
- data/README.markdown +92 -0
- data/Rakefile +91 -0
- data/lib/resourceful.rb +27 -0
- data/lib/resourceful/abstract_form_data.rb +30 -0
- data/lib/resourceful/authentication_manager.rb +107 -0
- data/lib/resourceful/cache_manager.rb +242 -0
- data/lib/resourceful/exceptions.rb +34 -0
- data/lib/resourceful/header.rb +355 -0
- data/lib/resourceful/http_accessor.rb +103 -0
- data/lib/resourceful/memcache_cache_manager.rb +75 -0
- data/lib/resourceful/multipart_form_data.rb +46 -0
- data/lib/resourceful/net_http_adapter.rb +84 -0
- data/lib/resourceful/promiscuous_basic_authenticator.rb +18 -0
- data/lib/resourceful/request.rb +235 -0
- data/lib/resourceful/resource.rb +179 -0
- data/lib/resourceful/response.rb +221 -0
- data/lib/resourceful/simple.rb +36 -0
- data/lib/resourceful/stubbed_resource_proxy.rb +47 -0
- data/lib/resourceful/urlencoded_form_data.rb +19 -0
- data/lib/resourceful/util.rb +6 -0
- data/openlogic-resourceful.gemspec +51 -0
- data/resourceful.gemspec +51 -0
- data/spec/acceptance/authorization_spec.rb +16 -0
- data/spec/acceptance/caching_spec.rb +190 -0
- data/spec/acceptance/header_spec.rb +24 -0
- data/spec/acceptance/redirecting_spec.rb +12 -0
- data/spec/acceptance/resource_spec.rb +84 -0
- data/spec/acceptance/resourceful_spec.rb +56 -0
- data/spec/acceptance_shared_specs.rb +44 -0
- data/spec/caching_spec.rb +89 -0
- data/spec/old_acceptance_specs.rb +378 -0
- data/spec/resourceful/header_spec.rb +153 -0
- data/spec/resourceful/http_accessor_spec.rb +56 -0
- data/spec/resourceful/multipart_form_data_spec.rb +84 -0
- data/spec/resourceful/promiscuous_basic_authenticator_spec.rb +30 -0
- data/spec/resourceful/resource_spec.rb +20 -0
- data/spec/resourceful/response_spec.rb +51 -0
- data/spec/resourceful/urlencoded_form_data_spec.rb +64 -0
- data/spec/resourceful_spec.rb +79 -0
- data/spec/simple_sinatra_server.rb +74 -0
- data/spec/simple_sinatra_server_spec.rb +98 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +31 -0
- metadata +192 -0
@@ -0,0 +1,153 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper.rb"
|
2
|
+
|
3
|
+
module Resourceful
|
4
|
+
describe Header do
|
5
|
+
def self.should_support_header(name)
|
6
|
+
const_name = name.upcase.gsub('-', '_')
|
7
|
+
meth_name = name.downcase.gsub('-', '_')
|
8
|
+
|
9
|
+
eval <<-RUBY
|
10
|
+
it "should have constant `#{const_name}` for header `#{name}`" do
|
11
|
+
Resourceful::Header::#{const_name}.should == '#{name}'
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should have accessor method `#{meth_name}` for header `#{name}`" do
|
15
|
+
Resourceful::Header.new.should respond_to(:#{meth_name})
|
16
|
+
end
|
17
|
+
|
18
|
+
RUBY
|
19
|
+
end
|
20
|
+
|
21
|
+
should_support_header('Accept')
|
22
|
+
should_support_header('Accept-Charset')
|
23
|
+
should_support_header('Accept-Encoding')
|
24
|
+
should_support_header('Accept-Language')
|
25
|
+
should_support_header('Accept-Ranges')
|
26
|
+
should_support_header('Age')
|
27
|
+
should_support_header('Allow')
|
28
|
+
should_support_header('Authorization')
|
29
|
+
should_support_header('Cache-Control')
|
30
|
+
should_support_header('Connection')
|
31
|
+
should_support_header('Content-Encoding')
|
32
|
+
should_support_header('Content-Language')
|
33
|
+
should_support_header('Content-Length')
|
34
|
+
should_support_header('Content-Location')
|
35
|
+
should_support_header('Content-MD5')
|
36
|
+
should_support_header('Content-Range')
|
37
|
+
should_support_header('Content-Type')
|
38
|
+
should_support_header('Date')
|
39
|
+
should_support_header('ETag')
|
40
|
+
should_support_header('Expect')
|
41
|
+
should_support_header('Expires')
|
42
|
+
should_support_header('From')
|
43
|
+
should_support_header('Host')
|
44
|
+
should_support_header('If-Match')
|
45
|
+
should_support_header('If-Modified-Since')
|
46
|
+
should_support_header('If-None-Match')
|
47
|
+
should_support_header('If-Range')
|
48
|
+
should_support_header('If-Unmodified-Since')
|
49
|
+
should_support_header('Keep-Alive')
|
50
|
+
should_support_header('Last-Modified')
|
51
|
+
should_support_header('Location')
|
52
|
+
should_support_header('Max-Forwards')
|
53
|
+
should_support_header('Pragma')
|
54
|
+
should_support_header('Proxy-Authenticate')
|
55
|
+
should_support_header('Proxy-Authorization')
|
56
|
+
should_support_header('Range')
|
57
|
+
should_support_header('Referer')
|
58
|
+
should_support_header('Retry-After')
|
59
|
+
should_support_header('Server')
|
60
|
+
should_support_header('TE')
|
61
|
+
should_support_header('Trailer')
|
62
|
+
should_support_header('Transfer-Encoding')
|
63
|
+
should_support_header('Upgrade')
|
64
|
+
should_support_header('User-Agent')
|
65
|
+
should_support_header('Vary')
|
66
|
+
should_support_header('Via')
|
67
|
+
should_support_header('Warning')
|
68
|
+
should_support_header('WWW-Authenticate')
|
69
|
+
|
70
|
+
|
71
|
+
it "should be instantiatable w/ single valued header fields" do
|
72
|
+
Header.new('Host' => 'foo.example').
|
73
|
+
host.should eql('foo.example')
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should gracefully handle repeated values for single valued header fields" do
|
77
|
+
lambda {
|
78
|
+
Header.new('Host' => ['foo.example', 'bar.example'])
|
79
|
+
}.should raise_error(ArgumentError, 'Host field may only have one value')
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should provide #each_fields to iterate through all header fields and values as strings" do
|
83
|
+
field_names = []
|
84
|
+
Header.new('Accept' => "this", :content_type => "that", 'pragma' => 'test').each_field do |fname, _|
|
85
|
+
field_names << fname
|
86
|
+
end
|
87
|
+
|
88
|
+
field_names.should include('Accept')
|
89
|
+
field_names.should include('Content-Type')
|
90
|
+
field_names.should include('Pragma')
|
91
|
+
field_names.should have(3).items
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should provide #to_hash as a way to dump the header fields" do
|
95
|
+
Header.new('Accept' => "this", :content_type => "that", 'date' => 'today').to_hash.tap do |h|
|
96
|
+
h.should have_pair('Accept', ['this'])
|
97
|
+
h.should have_pair('Content-Type', 'that')
|
98
|
+
h.should have_pair('Date', 'today')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should provide a list of hop-by-hop fields" do
|
103
|
+
Header.header_field('X-Hop-By-Hop-Header', :hop_by_hop => true)
|
104
|
+
Header.hop_by_hop_fields.should include('X-Hop-By-Hop-Header')
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should provide a list of not modified fields" do
|
108
|
+
Header.header_field('X-Dont-Modify-Me', :modifiable => false)
|
109
|
+
Header.non_modifiable_fields.should include('X-Dont-Modify-Me')
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "multi-valued fields" do
|
113
|
+
it "should be instantiatable w/ repeated multi-valued header fields" do
|
114
|
+
Header.new('Accept' => ['application/foo', 'application/bar']).
|
115
|
+
accept.should eql(['application/foo', 'application/bar'])
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should be instantiatable w/ repeated multi-valued header fields w/ multiple values" do
|
119
|
+
Header.new('Accept' => ['application/foo, application/bar', 'text/plain']).
|
120
|
+
accept.should eql(['application/foo', 'application/bar', 'text/plain'])
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should be instantiatable w/ multi-valued header fields w/ multiple values" do
|
124
|
+
Header.new('Accept' => 'application/foo, application/bar').
|
125
|
+
accept.should eql(['application/foo', 'application/bar'])
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should be instantiatable w/ multi-valued header fields w/ one value" do
|
129
|
+
Header.new('Accept' => 'application/foo').
|
130
|
+
accept.should eql(['application/foo'])
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should provide values to #each_field as a comma separated string" do
|
134
|
+
Header.new('Accept' => ['this', 'that']).each_field do |fname, fval|
|
135
|
+
fval.should == 'this, that'
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should provide #each as a way to iterate through fields as w/ higher level values" do
|
140
|
+
Header.new('Accept' => ['this', 'that']).each do |fname, fval|
|
141
|
+
fval.should == ['this', 'that']
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
Spec::Matchers.define :have_pair do |name, value|
|
147
|
+
match do |header_hash|
|
148
|
+
header_hash.has_key?(name)
|
149
|
+
header_hash[name] == value
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
require "resourceful/http_accessor"
|
4
|
+
|
5
|
+
module Resourceful
|
6
|
+
describe HttpAccessor do
|
7
|
+
describe "instantiation" do
|
8
|
+
it "should accept logger option" do
|
9
|
+
test_logger = stub('logger', :debug => false)
|
10
|
+
ha = HttpAccessor.new(:logger => test_logger)
|
11
|
+
ha.logger.should equal(test_logger)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should accept array user_agent option" do
|
15
|
+
ha = HttpAccessor.new(:user_agent => ['foo/3.2', 'bar/1.0'])
|
16
|
+
ha.user_agent_string.should match(/^foo\/3.2 bar\/1.0 Resourceful/)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should accept string user_agent option" do
|
20
|
+
ha = HttpAccessor.new(:user_agent => 'foo')
|
21
|
+
ha.user_agent_string.should match(/^foo Resourceful/)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should accept cache_manager option" do
|
25
|
+
test_cache_manager = stub('cache_manager', :debug => false)
|
26
|
+
ha = HttpAccessor.new(:cache_manager => test_cache_manager)
|
27
|
+
ha.cache_manager.should equal(test_cache_manager)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should accept http_adapter option" do
|
31
|
+
test_http_adapter = stub('http_adapter', :debug => false)
|
32
|
+
ha = HttpAccessor.new(:http_adapter => test_http_adapter)
|
33
|
+
ha.http_adapter.should equal(test_http_adapter)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should accept authenticator option" do
|
37
|
+
test_authenticator = stub('authenticator', :debug => false)
|
38
|
+
ha = HttpAccessor.new(:authenticator => test_authenticator)
|
39
|
+
# cannot really be tested safely so we just rely on the fact that the option was accepted
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should accept authenticators option" do
|
43
|
+
test_authenticator1 = stub('authenticator1', :debug => false)
|
44
|
+
test_authenticator2 = stub('authenticator2', :debug => false)
|
45
|
+
ha = HttpAccessor.new(:authenticator => [test_authenticator1, test_authenticator2])
|
46
|
+
# cannot really be tested safely so we just rely on the fact that the option was accepted
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should reject unrecognized options" do
|
50
|
+
lambda {
|
51
|
+
HttpAccessor.new(:not_a_valid_option => "this")
|
52
|
+
}.should raise_error(ArgumentError)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
require 'tempfile'
|
3
|
+
require "resourceful/multipart_form_data.rb"
|
4
|
+
|
5
|
+
describe Resourceful::MultipartFormData do
|
6
|
+
|
7
|
+
before do
|
8
|
+
@form_data = Resourceful::MultipartFormData.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should know its content-type" do
|
12
|
+
@form_data.content_type.should match(/^multipart\/form-data/i)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should know its boundary string" do
|
16
|
+
@form_data.content_type.should match(/; boundary=[0-9A-Za-z]{10,}/i)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should allow simple parameters to be added" do
|
20
|
+
@form_data.add(:foo, "testing")
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "with multiple simple parameters" do
|
24
|
+
before do
|
25
|
+
@form_data.add('foo', 'bar')
|
26
|
+
@form_data.add('baz', 'this')
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should render a multipart form-data document when #read is called" do
|
30
|
+
boundary = /boundary=(\w+)/.match(@form_data.content_type)[1]
|
31
|
+
@form_data.read.should eql(<<MPFD[0..-2])
|
32
|
+
--#{boundary}\r
|
33
|
+
Content-Disposition: form-data; name="foo"\r
|
34
|
+
\r
|
35
|
+
bar\r
|
36
|
+
--#{boundary}\r
|
37
|
+
Content-Disposition: form-data; name="baz"\r
|
38
|
+
\r
|
39
|
+
this\r
|
40
|
+
--#{boundary}--
|
41
|
+
MPFD
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should be rewindable" do
|
45
|
+
first_read = @form_data.read
|
46
|
+
@form_data.rewind
|
47
|
+
@form_data.read.should eql(first_read)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should add file parameters to be added" do
|
51
|
+
Tempfile.open('resourceful-post-file-tests') do |file_to_upload|
|
52
|
+
file_to_upload << "This is a test"
|
53
|
+
file_to_upload.flush
|
54
|
+
|
55
|
+
@form_data.add_file(:foo, file_to_upload.path)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "with file parameter" do
|
61
|
+
before do
|
62
|
+
@file_to_upload = Tempfile.new('resourceful-post-file-tests')
|
63
|
+
@file_to_upload << "This is a test"
|
64
|
+
@file_to_upload.flush
|
65
|
+
|
66
|
+
@form_data.add_file(:foo, @file_to_upload.path)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should render a multipart form-data document when #read is called" do
|
70
|
+
boundary = /boundary=(\w+)/.match(@form_data.content_type)[1]
|
71
|
+
@form_data.read.should eql(<<MPFD[0..-2])
|
72
|
+
--#{boundary}\r
|
73
|
+
Content-Disposition: form-data; name="foo"; filename="#{File.basename(@file_to_upload.path)}"\r
|
74
|
+
Content-Type: application/octet-stream\r
|
75
|
+
\r
|
76
|
+
This is a test\r
|
77
|
+
--#{boundary}--
|
78
|
+
MPFD
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.expand_path("../spec_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Resourceful::PromiscuousBasicAuthenticator do
|
4
|
+
before do
|
5
|
+
@authenticator = Resourceful::PromiscuousBasicAuthenticator.new('jim', 'mypasswd')
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should always claim to be valid for a challenge response" do
|
9
|
+
challenge_resp = mock("a challenge response")
|
10
|
+
@authenticator.valid_for?(challenge_resp).should eql(true)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should always claim to handle any request" do
|
14
|
+
a_req = mock("a request")
|
15
|
+
@authenticator.valid_for?(a_req).should eql(true)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "be creatable with just a username and password" do
|
19
|
+
Resourceful::PromiscuousBasicAuthenticator.new('jim', 'mypasswd').should be_instance_of(Resourceful::PromiscuousBasicAuthenticator)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "add credentials to request" do
|
23
|
+
header = mock('header')
|
24
|
+
header.should_receive(:[]=).with("Authorization", "Basic amltOm15cGFzc3dk")
|
25
|
+
a_req = mock("a request", :header => header)
|
26
|
+
|
27
|
+
@authenticator.add_credentials_to(a_req)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
module Resourceful
|
4
|
+
describe Resource do
|
5
|
+
before do
|
6
|
+
@http_adapter = stub(:http_adapter)
|
7
|
+
http = Resourceful::HttpAccessor.new(:http_adapter => @http_adapter)
|
8
|
+
@resource = http.resource('http://foo.example')
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "POSTing" do
|
12
|
+
it "should use bodies content type as the request content-type if it is known" do
|
13
|
+
@http_adapter.should_receive(:make_request).with(anything, anything, anything, hash_including('Content-Type' => 'application/x-special-type')).and_return([200, {}, ""])
|
14
|
+
body = stub(:body, :content_type => 'application/x-special-type', :read => "hello there")
|
15
|
+
@resource.post(body)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
require 'resourceful/response'
|
4
|
+
require 'resourceful/header'
|
5
|
+
|
6
|
+
module Resourceful
|
7
|
+
describe Response do
|
8
|
+
|
9
|
+
it "should know when it is expired" do
|
10
|
+
resp = Response.new(nil, nil, Header.new('Cache-Control' => 'max-age=2', 'Date' => (Time.now - 2).httpdate), nil)
|
11
|
+
resp.request_time = Time.now
|
12
|
+
|
13
|
+
resp.expired?.should be_true
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should know when it is not expired" do
|
17
|
+
resp = Response.new(nil, nil, Header.new('Cache-Control' => 'max-age=1', 'Date' => Time.now.httpdate), nil)
|
18
|
+
resp.request_time = Time.now
|
19
|
+
|
20
|
+
resp.expired?.should be_false
|
21
|
+
end
|
22
|
+
|
23
|
+
it "know when it is stale due to expiration" do
|
24
|
+
resp = Response.new(nil, nil, Header.new('Cache-Control' => 'max-age=1', 'Date' => (Time.now - 2).httpdate), nil)
|
25
|
+
resp.request_time = Time.now
|
26
|
+
|
27
|
+
resp.stale?.should be_true
|
28
|
+
end
|
29
|
+
|
30
|
+
it "know when it is stale due to no-cache" do
|
31
|
+
resp = Response.new(nil, nil, Header.new('Cache-Control' => 'no-cache', 'Date' => Time.now.httpdate), nil)
|
32
|
+
resp.request_time = Time.now
|
33
|
+
|
34
|
+
resp.stale?.should be_true
|
35
|
+
end
|
36
|
+
|
37
|
+
it "know when it is stale due to must-revalidate" do
|
38
|
+
resp = Response.new(nil, nil, Header.new('Cache-Control' => 'must-revalidate', 'Date' => Time.now.httpdate), nil)
|
39
|
+
resp.request_time = Time.now
|
40
|
+
|
41
|
+
resp.stale?.should be_true
|
42
|
+
end
|
43
|
+
|
44
|
+
it "know when it is not stale" do
|
45
|
+
resp = Response.new(nil, nil, Header.new('Cache-Control' => 'max-age=1', 'Date' => Time.now.httpdate), nil)
|
46
|
+
resp.request_time = Time.now
|
47
|
+
|
48
|
+
resp.stale?.should be_false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
require 'tempfile'
|
3
|
+
require "resourceful/urlencoded_form_data.rb"
|
4
|
+
|
5
|
+
describe Resourceful::UrlencodedFormData do
|
6
|
+
|
7
|
+
before do
|
8
|
+
@form_data = Resourceful::UrlencodedFormData.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should know its content-type" do
|
12
|
+
@form_data.content_type.should match(/^application\/x-www-form-urlencoded$/i)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "instantiation" do
|
16
|
+
it "should be creatable with hash" do
|
17
|
+
Resourceful::UrlencodedFormData.new(:foo => 'testing').read.should eql("foo=testing")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should allow simple parameters to be added" do
|
22
|
+
@form_data.add(:foo, "testing")
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "with multiple items" do
|
26
|
+
before do
|
27
|
+
@form_data.add('foo', 'bar')
|
28
|
+
@form_data.add('baz', 'this')
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should render itself correctly" do
|
32
|
+
@form_data.read.should eql("foo=bar&baz=this")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should be rewindable" do
|
36
|
+
first = @form_data.read
|
37
|
+
@form_data.rewind
|
38
|
+
|
39
|
+
@form_data.read.should eql(first)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "with unsafe characters in name" do
|
44
|
+
before do
|
45
|
+
@form_data.add('foo=bar', 'this')
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should render itself correctly" do
|
49
|
+
@form_data.read.should eql("foo%3Dbar=this")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
describe "with unsafe characters in value" do
|
55
|
+
before do
|
56
|
+
@form_data.add('foo', 'this & that')
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should render itself correctly" do
|
60
|
+
@form_data.read.should eql("foo=this+%26+that")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|