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