sinatra-rest-helpers 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.0
1
+ 0.5.0
@@ -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.4.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-21}
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
- it "should throw a 406 if the client requested only unsupported formats" do
28
- request = mock("request", :accept => ["application/json"])
29
- app = App.new(request)
30
- app.should_receive(:halt).with(406, "")
31
- app.test_provides!(:json)
32
- end
33
- it "should not throw a 406 if the client requested a supported format" do
34
- request = mock("request", :accept => ["application/json"])
35
- mime :json, "application/json"
36
- app = App.new(request)
37
- app.should_not_receive(:halt)
38
- app.test_provides!(:json)
39
- app.response.headers['Content-Type'].should == "application/json"
40
- end
41
- it "should be case insensitive" do
42
- request = mock("request", :accept => ["application/json"])
43
- app = App.new(request)
44
- app.should_not_receive(:halt)
45
- app.test_provides!("application/JSON")
46
- app.response.headers['Content-Type'].should == "application/JSON"
47
- end
48
- it "should not accept a request with a type level lower than what is supported" do
49
- request = mock("request", :accept => ["application/json;level=1"])
50
- app = App.new(request)
51
- app.should_receive(:halt).with(406, "application/json;level=3, application/json;level=2")
52
- app.test_provides!("application/json;level=3", "application/json;level=2")
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
- it "should accept a request having a supported mime type, but with no level" do
55
- request = mock("request", :accept => ["application/json"])
56
- app = App.new(request)
57
- app.should_not_receive(:halt)
58
- app.test_provides!("application/json;level=2")
59
- app.response.headers['Content-Type'].should == "application/json;level=2"
60
- end
61
- it "should select the first type matching the criteria" do
62
- request = mock("request", :accept => ["application/json;level=2", "application/xml", "application/vnd.fr.grid5000.api.Cluster+json;level=2"])
63
- app = App.new(request)
64
- app.should_not_receive(:halt)
65
- app.test_provides!("application/json;level=3", "application/vnd.fr.grid5000.api.Cluster+json;level=2")
66
- app.response.headers['Content-Type'].should == "application/vnd.fr.grid5000.api.Cluster+json;level=2"
67
- end
68
- it "should accept requests with a level, even if the developer didn't explicitely defined one" do
69
- request = mock("request", :accept => ["application/json;level=1"])
70
- app = App.new(request)
71
- app.should_not_receive(:halt)
72
- app.test_provides!("application/json")
73
- app.response.headers['Content-Type'].should == "application/json"
74
- end
75
- it "should correctly deal with widlcard characters [client-side, I]" do
76
- request = mock("request", :accept => ["application/vnd.fr.grid5000.api.*+json"])
77
- app = App.new(request)
78
- app.should_not_receive(:halt)
79
- app.test_provides!("application/vnd.fr.grid5000.api.Cluster+json;level=1")
80
- app.response.headers['Content-Type'].should == "application/vnd.fr.grid5000.api.Cluster+json;level=1"
81
- end
82
- it "should correctly deal with widlcard characters [client-side, II]" do
83
- request = mock("request", :accept => ["application/*"])
84
- app = App.new(request)
85
- app.should_not_receive(:halt)
86
- app.test_provides!("application/vnd.fr.grid5000.api.Cluster+json;level=1")
87
- app.response.headers['Content-Type'].should == "application/vnd.fr.grid5000.api.Cluster+json;level=1"
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
- it "should description" do
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.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-21 00:00:00 +02:00
12
+ date: 2009-10-22 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies: []
15
15