sinatra-rest-addons 0.1.4 → 1.0.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.1.4
1
+ 1.0.0
@@ -6,11 +6,103 @@ module Sinatra
6
6
  # class App < Sinatra::Base
7
7
  # helpers Sinatra::REST::Helpers
8
8
  # end
9
- module Helpers
9
+ module Helpers
10
+
11
+ INFINITY = 1/0.0
12
+
10
13
  def compute_etag(*args)
11
14
  raise ArgumentError, "You must provide at least one parameter for the ETag computation" if args.empty?
12
15
  Digest::SHA1.hexdigest(args.join("."))
13
16
  end
17
+
18
+ # e.g.
19
+ # get '/' do
20
+ # provides :xml, "text/html;level=5"
21
+ # "Hello"
22
+ # end
23
+ # will accept requests having an Accept header containing at least one of the following value:
24
+ # * application/xml
25
+ # * application/*
26
+ # * text/html
27
+ # * text/html;level=5
28
+ # * text/html;level=6
29
+ def provides(*formats)
30
+ generate_type_hash = Proc.new{ |header|
31
+ type, *params = header.split(/;\s*/)
32
+ Hash[*params.map{|p| p.split(/\s*=\s*/)}.flatten].merge("type" => type)
33
+ }
34
+ supported_formats = formats.map do |f|
35
+ # selects the correct mime type if a symbol is given
36
+ f.is_a?(Symbol) ? ::Rack::Mime::MIME_TYPES[".#{f.to_s}"] : f
37
+ end.compact.map do |f|
38
+ generate_type_hash.call(f)
39
+ end
40
+ # request.accept is an Array
41
+ accepted_formats = request.accept.map do |f|
42
+ generate_type_hash.call(f)
43
+ end
44
+ selected_format = nil
45
+ accepted_formats.each{ |accepted_format|
46
+ selected_format = supported_formats.detect{ |supported_format|
47
+ Regexp.new(Regexp.escape(accepted_format["type"]).gsub("\\*", ".*?"), Regexp::IGNORECASE) =~ supported_format["type"] &&
48
+ (accepted_format["level"] || INFINITY).to_f >= (supported_format["level"] || 0).to_f
49
+ }
50
+ break unless selected_format.nil?
51
+ }
52
+ if selected_format.nil?
53
+ content_type :txt
54
+ halt 406, supported_formats.map{|f|
55
+ output = f["type"]
56
+ output.concat(";level=#{f["level"]}") if f.has_key?("level")
57
+ output
58
+ }.join(",")
59
+ else
60
+ response.headers['Content-Type'] = "#{selected_format["type"]}#{selected_format["level"].nil? ? "" : ";level=#{selected_format["level"]}"}"
61
+ end
62
+ end # def provides
63
+
64
+
65
+ # Automatically decode the input data (coming from a POST or PUT request) based on the request's content-type.
66
+ # You must pass a Proc that will return the parser object to use to decode the payload.
67
+ # The parser object must respond to a <tt>parse</tt> method.
68
+ # You may also pass a hash of options:
69
+ # * <tt>:size_range</tt>: byte range specifying the minimum and maximum length (in bytes) of the payload [default=(1..1024**3)]
70
+ #
71
+ # e.g.
72
+ # post '/resource' do
73
+ # data = decode lambda{|content_type| if content_type =~ /^application\/.*json$/i then JSON}, :size_range => (1..2*1024**3)
74
+ # end
75
+ #
76
+ # The processing of the request will be halted in the following cases:
77
+ # * 400 if the payload is not within the specified size range.
78
+ # * 400 if the payload cannot be correctly parsed.
79
+ # * 415 if the payload's content type is not supported (i.e. the given Proc returns nil)
80
+ #
81
+ def decode(proc, config = {})
82
+ raise ArgumentError, "You must pass an object that responds to #call" unless proc.respond_to?(:call)
83
+ size_range = config.delete(:size_range) || (1..(1024**3))
84
+ case (mime_type = request.env['CONTENT_TYPE'])
85
+ when /^application\/x-www-form-urlencoded/i
86
+ request.env['sinatra.decoded_input'] = request.env['rack.request.form_hash']
87
+ else
88
+ content = ""
89
+ request.env['rack.input'].each do |block|
90
+ content.concat(block)
91
+ break if content.length > size_range.end
92
+ end
93
+ if not size_range.include?(content.length)
94
+ halt 400, "Input data size must be between #{size_range.begin} and #{size_range.end} bytes."
95
+ elsif parser = proc.call(mime_type)
96
+ begin
97
+ parser.parse(content)
98
+ rescue StandardError => e
99
+ halt 400, "#{e.class.name}: #{e.message}"
100
+ end
101
+ else
102
+ halt 415, "Format #{mime_type} not supported"
103
+ end
104
+ end
105
+ end # def decode
14
106
  end # module Helpers
15
107
  end # module REST
16
108
  end # module Sinatra
@@ -11,51 +11,6 @@ module Sinatra
11
11
 
12
12
  # Allow the definition of OPTIONS routes
13
13
  def options(path, opts={}, &bk); route 'OPTIONS', path, opts, &bk end
14
-
15
- # e.g.
16
- # get '/', :provides => [:xml, "text/html;level=5"] { "hello" }
17
- # will accept requests with Accept header =
18
- # * application/xml
19
- # * application/*
20
- # * text/html
21
- # * text/html;level=5
22
- # * text/html;level=6
23
- def provides(*formats)
24
- generate_type_hash = Proc.new{ |header|
25
- type, *params = header.split(/;\s*/)
26
- Hash[*params.map{|p| p.split(/\s*=\s*/)}.flatten].merge("type" => type)
27
- }
28
- condition {
29
- supported_formats = formats.map do |f|
30
- # selects the correct mime type if a symbol is given
31
- f.is_a?(Symbol) ? ::Rack::Mime::MIME_TYPES[".#{f.to_s}"] : f
32
- end.compact.map do |f|
33
- generate_type_hash.call(f)
34
- end
35
- # request.accept is an Array
36
- accepted_formats = request.accept.map do |f|
37
- generate_type_hash.call(f)
38
- end
39
- selected_format = nil
40
- accepted_formats.each{ |accepted_format|
41
- selected_format = supported_formats.detect{ |supported_format|
42
- Regexp.new(Regexp.escape(accepted_format["type"]).gsub("\\*", ".*?"), Regexp::IGNORECASE) =~ supported_format["type"] &&
43
- (accepted_format["level"] || INFINITY).to_f >= (supported_format["level"] || 0).to_f
44
- }
45
- break unless selected_format.nil?
46
- }
47
- if selected_format.nil?
48
- content_type :txt
49
- halt 406, supported_formats.map{|f|
50
- output = f["type"]
51
- output.concat(";level=#{f["level"]}") if f.has_key?("level")
52
- output
53
- }.join(",")
54
- else
55
- response.headers['Content-Type'] = "#{selected_format["type"]}#{selected_format["level"].nil? ? "" : ";level=#{selected_format["level"]}"}"
56
- end
57
- }
58
- end # def provides
59
14
 
60
15
  # Allow access to the route based on the result of the given proc, whose argument is a <tt>credentials</tt> object.
61
16
  # You MUST declare a helper function named <tt>credentials</tt> that will return an object (of your choice) containing the client's credentials,
@@ -76,46 +31,6 @@ module Sinatra
76
31
  end
77
32
  }
78
33
  end # def allow
79
-
80
- # Automatically decode the input data (coming from a POST or PUT request) based on the request's content-type.
81
- # First argument must be a parsing procedure that will be used to parse the input data according to its content type.
82
- # The decoded input will be available in request.env['sinatra.decoded_input']
83
- # e.g.
84
- # post '/',
85
- # :decode => Proc.new{ |content_type, content|
86
- # content_type =~ /^application\/.*json$/i ? JSON.parse(content) : throw(:halt, [400, "Don't know how to parse '#{content_type}' content."])
87
- # } do
88
- # "#{request.env['sinatra.decoded_input']}"
89
- # end
90
- def decode(*args)
91
- args = [args] unless args.kind_of? Array
92
- parsing_proc = args.shift
93
- raise ArgumentError, "You must provide a proc to parse the input data" unless parsing_proc.kind_of?(Proc)
94
- size_range = args.shift || (1..(1024**3))
95
- condition {
96
- begin
97
- case (mime_type = request.env['CONTENT_TYPE'])
98
- when /^application\/x-www-form-urlencoded/i
99
- request.env['sinatra.decoded_input'] = request.env['rack.request.form_hash']
100
- else
101
- content = ""
102
- request.env['rack.input'].each do |block|
103
- content.concat(block)
104
- break if content.length > size_range.end
105
- end
106
- if not size_range.include?(content.length)
107
- content_type :txt
108
- halt 400, "Input data size must be between #{size_range.begin} and #{size_range.end} bytes."
109
- else
110
- request.env['sinatra.decoded_input'] = parsing_proc.call(mime_type, content)
111
- end
112
- end
113
- rescue StandardError => e
114
- content_type :txt
115
- halt 400, "#{e.class.name}: #{e.message}"
116
- end
117
- }
118
- end # def decode
119
34
 
120
35
  end # module Routes
121
36
  end # module REST
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{sinatra-rest-addons}
8
- s.version = "0.1.4"
8
+ s.version = "1.0.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-11-23}
12
+ s.date = %q{2010-01-07}
13
13
  s.description = %q{A set of helpers and extensions for sinatra apps that expose REST resources.}
14
14
  s.email = %q{cyril.rohr@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
26
26
  "lib/sinatra/rest/routes.rb",
27
27
  "sinatra-rest-addons.gemspec",
28
28
  "test/helper.rb",
29
+ "test/test_helpers.rb",
29
30
  "test/test_routes.rb"
30
31
  ]
31
32
  s.homepage = %q{http://github.com/crohr/sinatra-rest-addons}
@@ -35,6 +36,7 @@ Gem::Specification.new do |s|
35
36
  s.summary = %q{A set of helpers and extensions for sinatra apps that expose REST resources.}
36
37
  s.test_files = [
37
38
  "test/helper.rb",
39
+ "test/test_helpers.rb",
38
40
  "test/test_routes.rb"
39
41
  ]
40
42
 
@@ -0,0 +1,182 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+ require 'json'
3
+
4
+ class HelpersTest < Test::Unit::TestCase
5
+
6
+ context "helper :provides" do
7
+
8
+ test "should return 406 and the list of supported types, if the server does not support the types accepted by the client [simple matching]" do
9
+ mock_app {
10
+ helpers Sinatra::REST::Helpers
11
+ get '/' do
12
+ provides "application/xml", "application/vnd.x.y.z+xml"
13
+ request.env['HTTP_ACCEPT']
14
+ end
15
+ }
16
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/json' }
17
+ assert_equal 406, last_response.status
18
+ assert_equal 'application/xml,application/vnd.x.y.z+xml', last_response.body
19
+ assert_equal 'text/plain', last_response.headers['Content-Type']
20
+ end
21
+ test "should return 406 if the accepted type has a level lower than what is supported" do
22
+ mock_app {
23
+ helpers Sinatra::REST::Helpers
24
+ get '/' do
25
+ provides "application/xml;level=5"
26
+ request.env['HTTP_ACCEPT']
27
+ end
28
+ }
29
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/xml;level=4' }
30
+ assert_equal 406, last_response.status
31
+ assert_equal 'application/xml;level=5', last_response.body
32
+ assert_equal 'text/plain', last_response.headers['Content-Type']
33
+ end
34
+ test "should return the first matching type if the accepted type contains a *" do
35
+ mock_app {
36
+ helpers Sinatra::REST::Helpers
37
+ get '/' do
38
+ provides "application/xml", "application/vnd.x.y.z+xml"
39
+ request.env['HTTP_ACCEPT']
40
+ end
41
+ }
42
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/*' }
43
+ assert_equal 200, last_response.status
44
+ assert_equal 'application/*', last_response.body
45
+ assert_equal 'application/xml', last_response.headers['Content-Type']
46
+ end
47
+ test "should respect the order in which the accepted formats are declared when looking for the format to select" do
48
+ mock_app {
49
+ helpers Sinatra::REST::Helpers
50
+ get '/' do
51
+ provides "application/json", "application/xml", "application/vnd.x.y.z+xml"
52
+ request.env['HTTP_ACCEPT']
53
+ end
54
+ }
55
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/xml, */*' }
56
+ assert_equal 200, last_response.status
57
+ assert_equal 'application/xml, */*', last_response.body
58
+ assert_equal 'application/xml', last_response.headers['Content-Type']
59
+ end
60
+ test "should be successful if the accepted type does not require a specific level" do
61
+ mock_app {
62
+ helpers Sinatra::REST::Helpers
63
+ get '/' do
64
+ provides "application/xml;level=5"
65
+ request.env['HTTP_ACCEPT']
66
+ end
67
+ }
68
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
69
+ assert last_response.ok?
70
+ assert_equal 'application/xml', last_response.body
71
+ assert_equal 'application/xml;level=5', last_response.headers['Content-Type']
72
+ end
73
+ test "should be successful if the accepted type level is greater than what is supported" do
74
+ mock_app {
75
+ helpers Sinatra::REST::Helpers
76
+ get '/' do
77
+ provides "application/xml;level=5"
78
+ request.env['HTTP_ACCEPT']
79
+ end
80
+ }
81
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/xml;level=6' }
82
+ assert last_response.ok?
83
+ assert_equal 'application/xml;level=6', last_response.body
84
+ assert_equal 'application/xml;level=5', last_response.headers['Content-Type']
85
+ end
86
+ end
87
+
88
+ context "helper :decode" do
89
+ setup do
90
+ PARSING_PROC = Proc.new{|content_type| content_type =~ /^application\/.*json$/i ? JSON : nil} unless defined?(PARSING_PROC)
91
+ end
92
+ test "should return 400 if the input content is empty" do
93
+ mock_app {
94
+ helpers Sinatra::REST::Helpers
95
+ post '/' do
96
+ data = decode PARSING_PROC
97
+ end
98
+ error 400 do
99
+ content_type :txt
100
+ "#{response.body.to_s}"
101
+ end
102
+ }
103
+ post '/', "", {'CONTENT_TYPE' => "application/json"}
104
+ assert_equal 400, last_response.status
105
+ assert_equal 'Input data size must be between 1 and 1073741824 bytes.', last_response.body
106
+ assert_equal 'text/plain', last_response.headers['Content-Type']
107
+ end
108
+ test "should return 400 if the input content is too large" do
109
+ mock_app {
110
+ helpers Sinatra::REST::Helpers
111
+ post '/' do
112
+ decode PARSING_PROC, :size_range => 2...30
113
+ end
114
+ }
115
+ post '/', '{"key1": ["value1", "value2"]}', {'CONTENT_TYPE' => "application/json"}
116
+ assert_equal 400, last_response.status
117
+ assert_equal 'Input data size must be between 2 and 30 bytes.', last_response.body
118
+ end
119
+ test "should return 400 if the input content can be parsed but is malformed" do
120
+ mock_app {
121
+ helpers Sinatra::REST::Helpers
122
+ post '/'do
123
+ decode PARSING_PROC, :size_range => 2...30
124
+ end
125
+ }
126
+ post '/', '{"key1": ["value1", "value2"]', {'CONTENT_TYPE' => "application/json"}
127
+ assert_equal 400, last_response.status
128
+ assert_equal "JSON::ParserError: 618: unexpected token at '{\"key1\": [\"value1\", \"value2\"]'", last_response.body
129
+ end
130
+ test "should return 415 if the parsing proc does not return a parser" do
131
+ mock_app {
132
+ helpers Sinatra::REST::Helpers
133
+ post '/' do
134
+ decode PARSING_PROC
135
+ end
136
+ }
137
+ post '/', '<item></item>', {'CONTENT_TYPE' => "application/xml"}
138
+ assert_equal 415, last_response.status
139
+ assert_equal "Format application/xml not supported", last_response.body
140
+ end
141
+ test "should correctly parse the input content if a parser can be found for the specified content type, and the decoded input must be returned" do
142
+ input = {"key1" => ["value1", "value2"]}
143
+ mock_app {
144
+ helpers Sinatra::REST::Helpers
145
+ post '/' do
146
+ data = decode PARSING_PROC, :size_range => 2...30
147
+ content_type 'application/json'
148
+ JSON.dump data
149
+ end
150
+ }
151
+ post '/', JSON.dump(input), {'CONTENT_TYPE' => "application/json"}
152
+ assert_equal 200, last_response.status
153
+ assert_equal JSON.dump(input), last_response.body
154
+ assert_equal 'application/json', last_response.headers['Content-Type']
155
+ end
156
+ test "should set the decoded input to the params hash already decoded by Sinatra, if the request's content type is application/x-www-form-urlencoded" do
157
+ input = {"key1" => ["value1", "value2"]}
158
+ mock_app {
159
+ helpers Sinatra::REST::Helpers
160
+ post '/' do
161
+ data = decode PARSING_PROC, :size_range => 2...30
162
+ content_type 'application/json'
163
+ JSON.dump data
164
+ end
165
+ }
166
+ post '/', input
167
+ assert_equal 200, last_response.status
168
+ assert_equal JSON.dump(input), last_response.body
169
+ assert_equal 'application/json', last_response.headers['Content-Type']
170
+ end
171
+ test "should raise an error if the first argument is not a proc" do
172
+ mock_app {
173
+ helpers Sinatra::REST::Helpers
174
+ post '/' do
175
+ decode :whatever
176
+ end
177
+ }
178
+ assert_raise(ArgumentError){ post '/' }
179
+ end
180
+ end
181
+
182
+ end
@@ -19,200 +19,6 @@ class RoutesTest < Test::Unit::TestCase
19
19
  assert_equal 'remove me', response.body
20
20
  end
21
21
 
22
- context "route option :provides" do
23
-
24
- test "should return 406 and the list of supported types, if the server does not support the types accepted by the client [simple matching]" do
25
- mock_app {
26
- register Sinatra::REST::Routes
27
- get '/', :provides => ["application/xml", "application/vnd.x.y.z+xml"] do
28
- request.env['HTTP_ACCEPT']
29
- end
30
- }
31
- get '/', {}, { 'HTTP_ACCEPT' => 'application/json' }
32
- assert_equal 406, last_response.status
33
- assert_equal 'application/xml,application/vnd.x.y.z+xml', last_response.body
34
- assert_equal 'text/plain', last_response.headers['Content-Type']
35
- end
36
- test "should return 406 if the accepted type has a level lower than what is supported" do
37
- mock_app {
38
- register Sinatra::REST::Routes
39
- get '/', :provides => ["application/xml;level=5"] do
40
- request.env['HTTP_ACCEPT']
41
- end
42
- }
43
- get '/', {}, { 'HTTP_ACCEPT' => 'application/xml;level=4' }
44
- assert_equal 406, last_response.status
45
- assert_equal 'application/xml;level=5', last_response.body
46
- assert_equal 'text/plain', last_response.headers['Content-Type']
47
- end
48
- test "should return the first matching type if the accepted type contains a *" do
49
- mock_app {
50
- register Sinatra::REST::Routes
51
- get '/', :provides => ["application/xml", "application/vnd.x.y.z+xml"] do
52
- request.env['HTTP_ACCEPT']
53
- end
54
- }
55
- get '/', {}, { 'HTTP_ACCEPT' => 'application/*' }
56
- assert_equal 200, last_response.status
57
- assert_equal 'application/*', last_response.body
58
- assert_equal 'application/xml', last_response.headers['Content-Type']
59
- end
60
- test "should respect the order in which the accepted formats are declared when looking for the format to select" do
61
- mock_app {
62
- register Sinatra::REST::Routes
63
- get '/', :provides => ["application/json", "application/xml", "application/vnd.x.y.z+xml"] do
64
- request.env['HTTP_ACCEPT']
65
- end
66
- }
67
- get '/', {}, { 'HTTP_ACCEPT' => 'application/xml, */*' }
68
- assert_equal 200, last_response.status
69
- assert_equal 'application/xml, */*', last_response.body
70
- assert_equal 'application/xml', last_response.headers['Content-Type']
71
- end
72
- test "should be successful if the accepted type does not require a specific level" do
73
- mock_app {
74
- register Sinatra::REST::Routes
75
- get '/', :provides => ["application/xml;level=5"] do
76
- request.env['HTTP_ACCEPT']
77
- end
78
- }
79
- get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
80
- assert last_response.ok?
81
- assert_equal 'application/xml', last_response.body
82
- assert_equal 'application/xml;level=5', last_response.headers['Content-Type']
83
- end
84
- test "should be successful if the accepted type level is greater than what is supported" do
85
- mock_app {
86
- register Sinatra::REST::Routes
87
- get '/', :provides => ["application/xml;level=5"] do
88
- request.env['HTTP_ACCEPT']
89
- end
90
- }
91
- get '/', {}, { 'HTTP_ACCEPT' => 'application/xml;level=6' }
92
- assert last_response.ok?
93
- assert_equal 'application/xml;level=6', last_response.body
94
- assert_equal 'application/xml;level=5', last_response.headers['Content-Type']
95
- end
96
- end
97
-
98
- context "route option :decode" do
99
- setup do
100
- PARSING_PROC = Proc.new{|content_type, content| content_type =~ /^application\/.*json$/i ? JSON.parse(content) : throw(:halt, [400, "Cannot parse"])} unless defined? PARSING_PROC
101
- end
102
- test "should return 400 if the input content is empty" do
103
- mock_app {
104
- register Sinatra::REST::Routes
105
- post '/', :decode => PARSING_PROC do
106
- request.env['sinatra.decoded_input'].inspect
107
- end
108
- }
109
- post '/', "", {'CONTENT_TYPE' => "application/json"}
110
- assert_equal 400, last_response.status
111
- assert_equal 'Input data size must be between 1 and 1073741824 bytes.', last_response.body
112
- assert_equal 'text/plain', last_response.headers['Content-Type']
113
- end
114
- test "should return 400 if the input content is too large" do
115
- mock_app {
116
- register Sinatra::REST::Routes
117
- post '/', :decode => [PARSING_PROC, 2...30] do
118
- request.env['sinatra.decoded_input'].inspect
119
- end
120
- }
121
- post '/', '{"key1": ["value1", "value2"]}', {'CONTENT_TYPE' => "application/json"}
122
- assert_equal 400, last_response.status
123
- assert_equal 'Input data size must be between 2 and 30 bytes.', last_response.body
124
- assert_equal 'text/plain', last_response.headers['Content-Type']
125
- end
126
- test "should return 400 if the input content can be parsed but is malformed" do
127
- mock_app {
128
- register Sinatra::REST::Routes
129
- post '/', :decode => [PARSING_PROC, 2..30] do
130
- request.env['sinatra.decoded_input'].inspect
131
- end
132
- }
133
- post '/', '{"key1": ["value1", "value2"]', {'CONTENT_TYPE' => "application/json"}
134
- assert_equal 400, last_response.status
135
- assert_equal "JSON::ParserError: 618: unexpected token at '{\"key1\": [\"value1\", \"value2\"]'", last_response.body
136
- assert_equal 'text/plain', last_response.headers['Content-Type']
137
- end
138
- test "should allow the parsing proc to throw an exception if needed (e.g. no parser can be found for the requested content type)" do
139
- mock_app {
140
- register Sinatra::REST::Routes
141
- post '/', :decode => [PARSING_PROC, 2..30] do
142
- request.env['sinatra.decoded_input'].inspect
143
- end
144
- }
145
- post '/', '<item></item>', {'CONTENT_TYPE' => "application/xml"}
146
- assert_equal 400, last_response.status
147
- assert_equal "Cannot parse", last_response.body
148
- assert_equal 'text/html', last_response.headers['Content-Type']
149
- end
150
- test "should correctly parse the input content if a parser can be found for the specified content type, and the decoded input must be made available in env['sinatra.decoded_input']" do
151
- input = {"key1" => ["value1", "value2"]}
152
- mock_app {
153
- register Sinatra::REST::Routes
154
- post '/', :decode => [PARSING_PROC, 2...30] do
155
- content_type 'application/json'
156
- JSON.dump request.env['sinatra.decoded_input']
157
- end
158
- }
159
- post '/', JSON.dump(input), {'CONTENT_TYPE' => "application/json"}
160
- assert_equal 200, last_response.status
161
- assert_equal JSON.dump(input), last_response.body
162
- assert_equal 'application/json', last_response.headers['Content-Type']
163
- end
164
- test "should work" do
165
- input = "{\"walltime\":3600,\"resources\":\"/nodes=1\",\"at\":1258552306,\"on_launch\":{\"in\":\"/home/crohr\",\"do\":\"id\"}}"
166
- mock_app {
167
- register Sinatra::REST::Routes
168
- post '/', :decode => [PARSING_PROC, 2...3000] do
169
- content_type 'application/json'
170
- JSON.dump request.env['sinatra.decoded_input']
171
- end
172
- }
173
- post '/', input, {'CONTENT_TYPE' => "application/json"}
174
- p last_response.body
175
- assert_equal 200, last_response.status
176
- assert_equal JSON.parse(input), JSON.parse(last_response.body)
177
- assert_equal 'application/json', last_response.headers['Content-Type']
178
- end
179
- test "should set the decoded input to the params hash already decoded by Sinatra, if the request's content type is application/x-www-form-urlencoded" do
180
- input = {"key1" => ["value1", "value2"]}
181
- mock_app {
182
- register Sinatra::REST::Routes
183
- post '/', :decode => [PARSING_PROC, 2...30] do
184
- content_type 'application/json'
185
- JSON.dump request.env['sinatra.decoded_input']
186
- end
187
- }
188
- post '/', input
189
- assert_equal 200, last_response.status
190
- assert_equal JSON.dump(input), last_response.body
191
- assert_equal 'application/json', last_response.headers['Content-Type']
192
- end
193
- test "should return 400 if the parsing proc raises a StandardError" do
194
- mock_app {
195
- register Sinatra::REST::Routes
196
- post '/', :decode => Proc.new{|content_type, content| raise(StandardError, "error message") } do
197
- request.env['sinatra.decoded_input'].inspect
198
- end
199
- }
200
- post '/', 'whatever content', {'CONTENT_TYPE' => "whatever type"}
201
- assert_equal 400, last_response.status
202
- assert_equal "StandardError: error message", last_response.body
203
- assert_equal 'text/plain', last_response.headers['Content-Type']
204
- end
205
- test "should raise an error if the first argument is not a proc" do
206
- assert_raise(ArgumentError){ mock_app {
207
- register Sinatra::REST::Routes
208
- disable :raise_errors, :show_exceptions
209
- post '/', :decode => :whatever do
210
- JSON.dump request.env['sinatra.decoded_input']
211
- end
212
- } }
213
- end
214
- end
215
-
216
22
  context "route option :allow" do
217
23
  test "should return 403 if the proc returns false" do
218
24
  mock_app {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sinatra-rest-addons
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 1.0.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-11-23 00:00:00 +01:00
12
+ date: 2010-01-07 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -51,6 +51,7 @@ files:
51
51
  - lib/sinatra/rest/routes.rb
52
52
  - sinatra-rest-addons.gemspec
53
53
  - test/helper.rb
54
+ - test/test_helpers.rb
54
55
  - test/test_routes.rb
55
56
  has_rdoc: true
56
57
  homepage: http://github.com/crohr/sinatra-rest-addons
@@ -82,4 +83,5 @@ specification_version: 3
82
83
  summary: A set of helpers and extensions for sinatra apps that expose REST resources.
83
84
  test_files:
84
85
  - test/helper.rb
86
+ - test/test_helpers.rb
85
87
  - test/test_routes.rb