sinatra-rest-helpers 0.4.0 → 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/VERSION +1 -1
- data/lib/sinatra/rest_helpers.rb +22 -1
- data/sinatra-rest-helpers.gemspec +2 -2
- data/spec/sinatra_rest_helpers_spec.rb +117 -62
- metadata +2 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/lib/sinatra/rest_helpers.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rack'
|
2
|
+
require 'digest/sha1'
|
2
3
|
|
3
4
|
module Sinatra
|
4
5
|
#
|
@@ -43,6 +44,26 @@ module Sinatra
|
|
43
44
|
response.headers['Content-Type'] = "#{selected_format["type"]}#{selected_format["level"].nil? ? "" : ";level=#{selected_format["level"]}"}"
|
44
45
|
end
|
45
46
|
end
|
47
|
+
|
48
|
+
# parser_selector must respond to :select(content_type) and return a parser object with a :load method.
|
49
|
+
def parse_input_data!(parser_selector, options = {:limit => 10*1024})
|
50
|
+
case (mime_type = request.env['CONTENT_TYPE'])
|
51
|
+
when nil
|
52
|
+
halt 400, "You must provide a Content-Type HTTP header."
|
53
|
+
when /application\/x-www-form-urlencoded/i
|
54
|
+
request.env['rack.request.form_hash']
|
55
|
+
else
|
56
|
+
input_data = request.env['rack.input'].read
|
57
|
+
halt 400, "Input data size must not be empty and must not exceed #{options[:limit]} bytes." if (options[:limit] && input_data.length > options[:limit]) || input_data.length == 0
|
58
|
+
parser_selector.select(mime_type).load(input_data)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def compute_etag(*args) # :nodoc:
|
63
|
+
raise ArgumentError, "You must provide at least one parameter for the ETag computation" if args.empty?
|
64
|
+
Digest::SHA1.hexdigest(args.join("."))
|
65
|
+
end
|
66
|
+
|
46
67
|
end
|
47
|
-
|
68
|
+
|
48
69
|
end
|
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{sinatra-rest-helpers}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.5.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Cyril Rohr"]
|
12
|
-
s.date = %q{2009-10-
|
12
|
+
s.date = %q{2009-10-22}
|
13
13
|
s.description = %q{A set of helpers for sinatra apps that expose REST resources.}
|
14
14
|
s.email = %q{cyril.rohr@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -13,6 +13,11 @@ class App
|
|
13
13
|
def test_provides!(*params)
|
14
14
|
provides *params
|
15
15
|
end
|
16
|
+
|
17
|
+
def test_parse_input_data!(*params)
|
18
|
+
parse_input_data!(*params)
|
19
|
+
end
|
20
|
+
|
16
21
|
end
|
17
22
|
|
18
23
|
def mime(ext, type)
|
@@ -24,69 +29,119 @@ describe "SinatraRestHelpers" do
|
|
24
29
|
before(:each) do
|
25
30
|
Rack::Mime::MIME_TYPES.clear
|
26
31
|
end
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
32
|
+
describe ":provides" do
|
33
|
+
it "should throw a 406 if the client requested only unsupported formats" do
|
34
|
+
request = mock("request", :accept => ["application/json"])
|
35
|
+
app = App.new(request)
|
36
|
+
app.should_receive(:halt).with(406, "")
|
37
|
+
app.test_provides!(:json)
|
38
|
+
end
|
39
|
+
it "should not throw a 406 if the client requested a supported format" do
|
40
|
+
request = mock("request", :accept => ["application/json"])
|
41
|
+
mime :json, "application/json"
|
42
|
+
app = App.new(request)
|
43
|
+
app.should_not_receive(:halt)
|
44
|
+
app.test_provides!(:json)
|
45
|
+
app.response.headers['Content-Type'].should == "application/json"
|
46
|
+
end
|
47
|
+
it "should be case insensitive" do
|
48
|
+
request = mock("request", :accept => ["application/json"])
|
49
|
+
app = App.new(request)
|
50
|
+
app.should_not_receive(:halt)
|
51
|
+
app.test_provides!("application/JSON")
|
52
|
+
app.response.headers['Content-Type'].should == "application/JSON"
|
53
|
+
end
|
54
|
+
it "should not accept a request with a type level lower than what is supported" do
|
55
|
+
request = mock("request", :accept => ["application/json;level=1"])
|
56
|
+
app = App.new(request)
|
57
|
+
app.should_receive(:halt).with(406, "application/json;level=3, application/json;level=2")
|
58
|
+
app.test_provides!("application/json;level=3", "application/json;level=2")
|
59
|
+
end
|
60
|
+
it "should accept a request having a supported mime type, but with no level" do
|
61
|
+
request = mock("request", :accept => ["application/json"])
|
62
|
+
app = App.new(request)
|
63
|
+
app.should_not_receive(:halt)
|
64
|
+
app.test_provides!("application/json;level=2")
|
65
|
+
app.response.headers['Content-Type'].should == "application/json;level=2"
|
66
|
+
end
|
67
|
+
it "should select the first type matching the criteria" do
|
68
|
+
request = mock("request", :accept => ["application/json;level=2", "application/xml", "application/vnd.fr.grid5000.api.Cluster+json;level=2"])
|
69
|
+
app = App.new(request)
|
70
|
+
app.should_not_receive(:halt)
|
71
|
+
app.test_provides!("application/json;level=3", "application/vnd.fr.grid5000.api.Cluster+json;level=2")
|
72
|
+
app.response.headers['Content-Type'].should == "application/vnd.fr.grid5000.api.Cluster+json;level=2"
|
73
|
+
end
|
74
|
+
it "should accept requests with a level, even if the developer didn't explicitely defined one" do
|
75
|
+
request = mock("request", :accept => ["application/json;level=1"])
|
76
|
+
app = App.new(request)
|
77
|
+
app.should_not_receive(:halt)
|
78
|
+
app.test_provides!("application/json")
|
79
|
+
app.response.headers['Content-Type'].should == "application/json"
|
80
|
+
end
|
81
|
+
it "should correctly deal with widlcard characters [client-side, I]" do
|
82
|
+
request = mock("request", :accept => ["application/vnd.fr.grid5000.api.*+json"])
|
83
|
+
app = App.new(request)
|
84
|
+
app.should_not_receive(:halt)
|
85
|
+
app.test_provides!("application/vnd.fr.grid5000.api.Cluster+json;level=1")
|
86
|
+
app.response.headers['Content-Type'].should == "application/vnd.fr.grid5000.api.Cluster+json;level=1"
|
87
|
+
end
|
88
|
+
it "should correctly deal with widlcard characters [client-side, II]" do
|
89
|
+
request = mock("request", :accept => ["application/*"])
|
90
|
+
app = App.new(request)
|
91
|
+
app.should_not_receive(:halt)
|
92
|
+
app.test_provides!("application/vnd.fr.grid5000.api.Cluster+json;level=1")
|
93
|
+
app.response.headers['Content-Type'].should == "application/vnd.fr.grid5000.api.Cluster+json;level=1"
|
94
|
+
end
|
53
95
|
end
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
96
|
+
|
97
|
+
describe ":parse_input_data!" do
|
98
|
+
it "should load input data in application/x-www-form-urlencoded format" do
|
99
|
+
parser_selector = mock("parser_selector")
|
100
|
+
request = mock("request", :env => {'rack.request.form_hash' => {"foo" => "bar"}, 'CONTENT_TYPE' => 'application/x-www-form-urlencoded'})
|
101
|
+
app = App.new(request)
|
102
|
+
parser_selector.should_not_receive(:select)
|
103
|
+
app.test_parse_input_data!(parser_selector).should == {"foo" => "bar"}
|
104
|
+
end
|
105
|
+
it "should load input data in application/json format" do
|
106
|
+
require 'json'
|
107
|
+
parser_selector = mock("parser_selector")
|
108
|
+
request = mock("request", :env => {'rack.input' => StringIO.new({"foo" => "bar"}.to_json), 'CONTENT_TYPE' => 'application/json'})
|
109
|
+
app = App.new(request)
|
110
|
+
parser_selector.should_receive(:select).with('application/json').and_return(JSON)
|
111
|
+
app.test_parse_input_data!(parser_selector).should == {"foo" => "bar"}
|
112
|
+
end
|
113
|
+
it "should halt with a 400 status code if the input data is empty" do
|
114
|
+
parser_selector = mock("parser_selector")
|
115
|
+
request = mock("request", :env => {'rack.input' => StringIO.new(), 'CONTENT_TYPE' => 'application/json'})
|
116
|
+
app = App.new(request)
|
117
|
+
app.should_receive(:halt).with(400, /must not be empty/).and_raise(Exception.new)
|
118
|
+
lambda{app.test_parse_input_data!(parser_selector)}.should raise_error(Exception)
|
119
|
+
end
|
120
|
+
it "should halt with a 400 status code if the input data is over the limit" do
|
121
|
+
parser_selector = mock("parser_selector")
|
122
|
+
request = mock("request", :env => {'rack.input' => StringIO.new("123456789_1"), 'CONTENT_TYPE' => 'application/json'})
|
123
|
+
app = App.new(request)
|
124
|
+
app.should_receive(:halt).with(400, /must not exceed 10 bytes/).and_raise(Exception.new)
|
125
|
+
lambda{app.test_parse_input_data!(parser_selector, :limit => 10)}.should raise_error(Exception)
|
126
|
+
end
|
127
|
+
it "should halt with a 400 status code if the content type is not set" do
|
128
|
+
parser_selector = mock("parser_selector")
|
129
|
+
request = mock("request", :env => {'rack.input' => StringIO.new("123456789_1")})
|
130
|
+
app = App.new(request)
|
131
|
+
app.should_receive(:halt).with(400, /must provide a Content-Type HTTP header/)
|
132
|
+
app.test_parse_input_data!(parser_selector)
|
133
|
+
end
|
88
134
|
end
|
89
|
-
|
90
|
-
|
135
|
+
|
136
|
+
describe ":compute_etag" do
|
137
|
+
it "should correctly compute the etag with multiple parameters" do
|
138
|
+
App.new(nil).compute_etag([1,2,3], 2, "whatever", :xyz).should == Digest::SHA1.hexdigest([[1,2,3], 2, "whatever", :xyz].join("."))
|
139
|
+
end
|
140
|
+
it "should correctly compute the etag with one parameter" do
|
141
|
+
App.new(nil).compute_etag(1).should == Digest::SHA1.hexdigest([1].join("."))
|
142
|
+
end
|
143
|
+
it "should raise an Argument Error if no arguments are present" do
|
144
|
+
lambda{App.new(nil).compute_etag()}.should raise_error(ArgumentError, /must provide at least one parameter/)
|
145
|
+
end
|
91
146
|
end
|
92
147
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sinatra-rest-helpers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyril Rohr
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-10-
|
12
|
+
date: 2009-10-22 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|