sinatra-rest-addons 0.1.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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Cyril Rohr
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,8 @@
1
+ = sinatra-rest-addons
2
+
3
+ A set of helpers and extensions for sinatra apps that expose REST resources.
4
+
5
+ == Copyright
6
+
7
+ Copyright (c) 2009 Cyril Rohr. See LICENSE for details.
8
+ Copyright (c) 2009 INRIA Rennes - Bretagne Atlantique. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "sinatra-rest-addons"
8
+ gem.summary = %Q{A set of helpers and extensions for sinatra apps that expose REST resources.}
9
+ gem.description = %Q{A set of helpers and extensions for sinatra apps that expose REST resources.}
10
+ gem.email = "cyril.rohr@gmail.com"
11
+ gem.homepage = "http://github.com/crohr/sinatra-rest-addons"
12
+ gem.authors = ["Cyril Rohr"]
13
+ gem.add_development_dependency "contest", ">= 0"
14
+ gem.add_dependency "sinatra", ">= 0.9.0"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
20
+ end
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/**/test_*.rb'
26
+ test.verbose = true
27
+ end
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
+ end
40
+ end
41
+
42
+ task :test => :check_dependencies
43
+
44
+ task :default => :test
45
+
46
+ require 'rake/rdoctask'
47
+ Rake::RDocTask.new do |rdoc|
48
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "sinatra-rest-addons #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,16 @@
1
+ require 'digest/sha1'
2
+
3
+ module Sinatra
4
+ module REST
5
+ # Include it with:
6
+ # class App < Sinatra::Base
7
+ # helpers Sinatra::REST::Helpers
8
+ # end
9
+ module Helpers
10
+ def compute_etag(*args)
11
+ raise ArgumentError, "You must provide at least one parameter for the ETag computation" if args.empty?
12
+ Digest::SHA1.hexdigest(args.join("."))
13
+ end
14
+ end # module Helpers
15
+ end # module REST
16
+ end # module Sinatra
@@ -0,0 +1,116 @@
1
+ require 'sinatra/base'
2
+
3
+ module Sinatra
4
+ module REST
5
+ # Include it with:
6
+ # class App < Sinatra::Base
7
+ # register Sinatra::REST::Routes
8
+ # end
9
+ module Routes
10
+ INFINITY = 1/0.0
11
+
12
+ # Allow the definition of OPTIONS routes
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 = supported_formats.detect{ |supported_format|
40
+ !accepted_formats.detect{ |accepted_format|
41
+ Regexp.new(Regexp.escape(accepted_format["type"]).gsub("\\*", ".*?"), Regexp::IGNORECASE) =~ supported_format["type"] &&
42
+ (accepted_format["level"] || INFINITY).to_f >= (supported_format["level"] || 0).to_f
43
+ }.nil?
44
+ }
45
+ if selected_format.nil?
46
+ content_type :txt
47
+ halt 406, supported_formats.map{|f|
48
+ output = f["type"]
49
+ output.concat(";level=#{f["level"]}") if f.has_key?("level")
50
+ output
51
+ }.join(",")
52
+ else
53
+ response.headers['Content-Type'] = "#{selected_format["type"]}#{selected_format["level"].nil? ? "" : ";level=#{selected_format["level"]}"}"
54
+ end
55
+ }
56
+ end # def provides
57
+
58
+ # Allow access to the route based on the result of the given proc, whose argument is a <tt>credentials</tt> object.
59
+ # You MUST declare a helper function named <tt>credentials</tt> that will return an object (of your choice) containing the client's credentials,
60
+ # that will be passed as an argument to the given Proc.
61
+ # Halts the response process with a 403 status code if the given proc returns false (you may then process the error with a <tt>error 403 {}</tt> block).
62
+ # e.g.
63
+ # helpers do
64
+ # def credentials; [params["user"], params["password"]]; end
65
+ # end
66
+ # get '/', :allow => Proc.new{ |credentials| credentials.first == "someone" && credentials.last == "password" } do
67
+ # "allowed"
68
+ # end
69
+ def allow(proc)
70
+ raise ArgumentError, "You must provide a Proc that returns true or false when given the result of a call to the 'credentials' helper" unless proc.kind_of?(Proc)
71
+ condition {
72
+ unless proc.call(credentials)
73
+ halt 403, "You cannot access this resource"
74
+ end
75
+ }
76
+ end # def allow
77
+
78
+ # Automatically decode the input data (coming from a POST or PUT request) based on the request's content-type.
79
+ # First argument must be a parsing procedure that will be used to parse the input data according to its content type.
80
+ # The decoded input will be available in request.env['sinatra.decoded_input']
81
+ # e.g.
82
+ # post '/',
83
+ # :decode => Proc.new{ |content_type, content|
84
+ # content_type =~ /^application\/.*json$/i ? JSON.parse(content) : throw(:halt, [400, "Don't know how to parse '#{content_type}' content."])
85
+ # } do
86
+ # "#{request.env['sinatra.decoded_input']}"
87
+ # end
88
+ def decode(*args)
89
+ args = [args] unless args.kind_of? Array
90
+ parsing_proc = args.shift
91
+ raise ArgumentError, "You must provide a proc to parse the input data" unless parsing_proc.kind_of?(Proc)
92
+ size_range = args.shift || (1..(1024**3))
93
+ condition {
94
+ begin
95
+ case (mime_type = request.env['CONTENT_TYPE'])
96
+ when /^application\/x-www-form-urlencoded/i
97
+ request.env['sinatra.decoded_input'] = request.env['rack.request.form_hash']
98
+ else
99
+ if not size_range.include?(request.env['rack.input'].size)
100
+ content_type :txt
101
+ halt 400, "Input data size must be between #{size_range.begin} and #{size_range.end} bytes."
102
+ else
103
+ content = request.env['rack.input'].read
104
+ request.env['sinatra.decoded_input'] = parsing_proc.call(mime_type, content)
105
+ end
106
+ end
107
+ rescue StandardError => e
108
+ content_type :txt
109
+ halt 400, "#{e.class.name}: #{e.message}"
110
+ end
111
+ }
112
+ end # def decode
113
+
114
+ end # module Routes
115
+ end # module REST
116
+ end # module Sinatra
@@ -0,0 +1,56 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{sinatra-rest-addons}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Cyril Rohr"]
12
+ s.date = %q{2009-11-13}
13
+ s.description = %q{A set of helpers and extensions for sinatra apps that expose REST resources.}
14
+ s.email = %q{cyril.rohr@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ "LICENSE",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "lib/sinatra/rest/helpers.rb",
25
+ "lib/sinatra/rest/routes.rb",
26
+ "sinatra-rest-addons.gemspec",
27
+ "test/helper.rb",
28
+ "test/test_routes.rb"
29
+ ]
30
+ s.homepage = %q{http://github.com/crohr/sinatra-rest-addons}
31
+ s.rdoc_options = ["--charset=UTF-8"]
32
+ s.require_paths = ["lib"]
33
+ s.rubygems_version = %q{1.3.5}
34
+ s.summary = %q{A set of helpers and extensions for sinatra apps that expose REST resources.}
35
+ s.test_files = [
36
+ "test/helper.rb",
37
+ "test/test_routes.rb"
38
+ ]
39
+
40
+ if s.respond_to? :specification_version then
41
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
42
+ s.specification_version = 3
43
+
44
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
45
+ s.add_development_dependency(%q<contest>, [">= 0"])
46
+ s.add_runtime_dependency(%q<sinatra>, [">= 0.9.0"])
47
+ else
48
+ s.add_dependency(%q<contest>, [">= 0"])
49
+ s.add_dependency(%q<sinatra>, [">= 0.9.0"])
50
+ end
51
+ else
52
+ s.add_dependency(%q<contest>, [">= 0"])
53
+ s.add_dependency(%q<sinatra>, [">= 0.9.0"])
54
+ end
55
+ end
56
+
data/test/helper.rb ADDED
@@ -0,0 +1,38 @@
1
+ # Adapted from the Sinatra test suite
2
+ ENV['RACK_ENV'] = 'test'
3
+
4
+ begin
5
+ require 'rack'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rack'
9
+ end
10
+
11
+ libdir = File.dirname(File.dirname(__FILE__)) + '/lib'
12
+ $LOAD_PATH.unshift libdir unless $LOAD_PATH.include?(libdir)
13
+
14
+ require 'contest'
15
+ require 'rack/test'
16
+ require 'sinatra/base'
17
+
18
+ class Sinatra::Base
19
+ # Allow assertions in request context
20
+ include Test::Unit::Assertions
21
+ end
22
+
23
+ Sinatra::Base.set :environment, :test
24
+
25
+ class Test::Unit::TestCase
26
+ include Rack::Test::Methods
27
+
28
+ # Sets up a Sinatra::Base subclass defined with the block
29
+ # given. Used in setup or individual spec methods to establish
30
+ # the application.
31
+ def mock_app(base=Sinatra::Base, &block)
32
+ @app = Sinatra.new(base, &block)
33
+ end
34
+
35
+ def app
36
+ Rack::Lint.new(@app)
37
+ end
38
+ end
@@ -0,0 +1,240 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+ require 'sinatra/rest/routes'
3
+ require 'json'
4
+
5
+ class RoutesTest < Test::Unit::TestCase
6
+
7
+ test "defines OPTIONS request handlers with options" do
8
+ mock_app {
9
+ register Sinatra::REST::Routes
10
+ options '/hello' do
11
+ response['X-Hello'] = 'World!'
12
+ 'remove me'
13
+ end
14
+ }
15
+
16
+ request = Rack::MockRequest.new(@app)
17
+ response = request.request("OPTIONS", '/hello', {})
18
+ assert response.ok?
19
+ assert_equal 'World!', response.headers['X-Hello']
20
+ assert_equal 'remove me', response.body
21
+ end
22
+
23
+ context "route option :provides" do
24
+
25
+
26
+ 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
27
+ mock_app {
28
+ register Sinatra::REST::Routes
29
+ get '/', :provides => ["application/xml", "application/vnd.x.y.z+xml"] do
30
+ request.env['HTTP_ACCEPT']
31
+ end
32
+ }
33
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/json' }
34
+ assert_equal 406, last_response.status
35
+ assert_equal 'application/xml,application/vnd.x.y.z+xml', last_response.body
36
+ assert_equal 'text/plain', last_response.headers['Content-Type']
37
+ end
38
+ test "should return 406 if the accepted type has a level lower than what is supported" do
39
+ mock_app {
40
+ register Sinatra::REST::Routes
41
+ get '/', :provides => ["application/xml;level=5"] do
42
+ request.env['HTTP_ACCEPT']
43
+ end
44
+ }
45
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/xml;level=4' }
46
+ assert_equal 406, last_response.status
47
+ assert_equal 'application/xml;level=5', last_response.body
48
+ assert_equal 'text/plain', last_response.headers['Content-Type']
49
+ end
50
+ test "should return the first matching type if the accepted type contains a *" do
51
+ mock_app {
52
+ register Sinatra::REST::Routes
53
+ get '/', :provides => ["application/xml", "application/vnd.x.y.z+xml"] do
54
+ request.env['HTTP_ACCEPT']
55
+ end
56
+ }
57
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/*' }
58
+ assert_equal 200, last_response.status
59
+ assert_equal 'application/*', last_response.body
60
+ assert_equal 'application/xml', last_response.headers['Content-Type']
61
+ end
62
+ test "should be successful if the accepted type does not require a specific level" do
63
+ mock_app {
64
+ register Sinatra::REST::Routes
65
+ get '/', :provides => ["application/xml;level=5"] do
66
+ request.env['HTTP_ACCEPT']
67
+ end
68
+ }
69
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
70
+ assert last_response.ok?
71
+ assert_equal 'application/xml', last_response.body
72
+ assert_equal 'application/xml;level=5', last_response.headers['Content-Type']
73
+ end
74
+ test "should be successful if the accepted type level is greater than what is supported" do
75
+ mock_app {
76
+ register Sinatra::REST::Routes
77
+ get '/', :provides => ["application/xml;level=5"] do
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 "route option :decode" do
89
+ setup do
90
+ PARSING_PROC = Proc.new{|content_type, content| content_type =~ /^application\/.*json$/i ? JSON.parse(content) : throw(:halt, [400, "Cannot parse"])} unless defined? PARSING_PROC
91
+ end
92
+ test "should return 400 if the input content is empty" do
93
+ mock_app {
94
+ register Sinatra::REST::Routes
95
+ post '/', :decode => PARSING_PROC do
96
+ request.env['sinatra.decoded_input'].inspect
97
+ end
98
+ }
99
+ post '/', "", {'CONTENT_TYPE' => "application/json"}
100
+ assert_equal 400, last_response.status
101
+ assert_equal 'Input data size must be between 1 and 1073741824 bytes.', last_response.body
102
+ assert_equal 'text/plain', last_response.headers['Content-Type']
103
+ end
104
+ test "should return 400 if the input content is too large" do
105
+ mock_app {
106
+ register Sinatra::REST::Routes
107
+ post '/', :decode => [PARSING_PROC, 2...30] do
108
+ request.env['sinatra.decoded_input'].inspect
109
+ end
110
+ }
111
+ post '/', '{"key1": ["value1", "value2"]}', {'CONTENT_TYPE' => "application/json"}
112
+ assert_equal 400, last_response.status
113
+ assert_equal 'Input data size must be between 2 and 30 bytes.', last_response.body
114
+ assert_equal 'text/plain', last_response.headers['Content-Type']
115
+ end
116
+ test "should return 400 if the input content can be parsed but is malformed" do
117
+ mock_app {
118
+ register Sinatra::REST::Routes
119
+ post '/', :decode => [PARSING_PROC, 2..30] do
120
+ request.env['sinatra.decoded_input'].inspect
121
+ end
122
+ }
123
+ post '/', '{"key1": ["value1", "value2"]', {'CONTENT_TYPE' => "application/json"}
124
+ assert_equal 400, last_response.status
125
+ assert_equal "JSON::ParserError: 618: unexpected token at '{\"key1\": [\"value1\", \"value2\"]'", last_response.body
126
+ assert_equal 'text/plain', last_response.headers['Content-Type']
127
+ end
128
+ 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
129
+ mock_app {
130
+ register Sinatra::REST::Routes
131
+ post '/', :decode => [PARSING_PROC, 2..30] do
132
+ request.env['sinatra.decoded_input'].inspect
133
+ end
134
+ }
135
+ post '/', '<item></item>', {'CONTENT_TYPE' => "application/xml"}
136
+ assert_equal 400, last_response.status
137
+ assert_equal "Cannot parse", last_response.body
138
+ assert_equal 'text/html', last_response.headers['Content-Type']
139
+ end
140
+ 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
141
+ input = {"key1" => ["value1", "value2"]}
142
+ mock_app {
143
+ register Sinatra::REST::Routes
144
+ post '/', :decode => [PARSING_PROC, 2...30] do
145
+ content_type 'application/json'
146
+ JSON.dump request.env['sinatra.decoded_input']
147
+ end
148
+ }
149
+ post '/', JSON.dump(input), {'CONTENT_TYPE' => "application/json"}
150
+ assert_equal 200, last_response.status
151
+ assert_equal JSON.dump(input), last_response.body
152
+ assert_equal 'application/json', last_response.headers['Content-Type']
153
+ end
154
+ 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
155
+ input = {"key1" => ["value1", "value2"]}
156
+ mock_app {
157
+ register Sinatra::REST::Routes
158
+ post '/', :decode => [PARSING_PROC, 2...30] do
159
+ content_type 'application/json'
160
+ JSON.dump request.env['sinatra.decoded_input']
161
+ end
162
+ }
163
+ post '/', input
164
+ assert_equal 200, last_response.status
165
+ assert_equal JSON.dump(input), last_response.body
166
+ assert_equal 'application/json', last_response.headers['Content-Type']
167
+ end
168
+ test "should return 400 if the parsing proc raises a StandardError" do
169
+ mock_app {
170
+ register Sinatra::REST::Routes
171
+ post '/', :decode => Proc.new{|content_type, content| raise(StandardError, "error message") } do
172
+ request.env['sinatra.decoded_input'].inspect
173
+ end
174
+ }
175
+ post '/', 'whatever content', {'CONTENT_TYPE' => "whatever type"}
176
+ assert_equal 400, last_response.status
177
+ assert_equal "StandardError: error message", last_response.body
178
+ assert_equal 'text/plain', last_response.headers['Content-Type']
179
+ end
180
+ test "should raise an error if the first argument is not a proc" do
181
+ assert_raise(ArgumentError){ mock_app {
182
+ register Sinatra::REST::Routes
183
+ disable :raise_errors, :show_exceptions
184
+ post '/', :decode => :whatever do
185
+ JSON.dump request.env['sinatra.decoded_input']
186
+ end
187
+ } }
188
+ end
189
+ end
190
+
191
+ context "route option :allow" do
192
+ test "should return 403 if the proc returns false" do
193
+ mock_app {
194
+ register Sinatra::REST::Routes
195
+ helpers do
196
+ def credentials; ["nobody"]; end
197
+ end
198
+ get '/', :allow => lambda{|credentials| credentials.first == "crohr" } do
199
+ "allowed"
200
+ end
201
+ }
202
+ get '/'
203
+ assert_equal 403, last_response.status
204
+ assert_equal "You cannot access this resource", last_response.body
205
+ end
206
+ test "should return 200 if the proc returns true" do
207
+ mock_app {
208
+ register Sinatra::REST::Routes
209
+ helpers do
210
+ def credentials; ["crohr", "1234x"]; end
211
+ end
212
+ get '/', :allow => lambda{|credentials| credentials.first == "crohr" && credentials.last == "1234x" } do
213
+ "allowed"
214
+ end
215
+ }
216
+ get '/'
217
+ assert_equal 200, last_response.status
218
+ assert_equal "allowed", last_response.body
219
+ end
220
+ test "should raise an error if the argument is not a proc" do
221
+ assert_raise(ArgumentError) {
222
+ mock_app {
223
+ register Sinatra::REST::Routes
224
+ get '/', :allow => :whatever do
225
+ "allowed"
226
+ end
227
+ }
228
+ }
229
+ end
230
+ test "should raise a NoMethodError if 'credentials' helpers are not defined" do
231
+ mock_app {
232
+ register Sinatra::REST::Routes
233
+ get '/', :allow => lambda{} do
234
+ "allowed"
235
+ end
236
+ }
237
+ assert_raise(NameError) { get '/' }
238
+ end
239
+ end
240
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sinatra-rest-addons
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Cyril Rohr
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-13 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: contest
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: sinatra
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.9.0
34
+ version:
35
+ description: A set of helpers and extensions for sinatra apps that expose REST resources.
36
+ email: cyril.rohr@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.rdoc
44
+ files:
45
+ - LICENSE
46
+ - README.rdoc
47
+ - Rakefile
48
+ - VERSION
49
+ - lib/sinatra/rest/helpers.rb
50
+ - lib/sinatra/rest/routes.rb
51
+ - sinatra-rest-addons.gemspec
52
+ - test/helper.rb
53
+ - test/test_routes.rb
54
+ has_rdoc: true
55
+ homepage: http://github.com/crohr/sinatra-rest-addons
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options:
60
+ - --charset=UTF-8
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ version:
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: "0"
74
+ version:
75
+ requirements: []
76
+
77
+ rubyforge_project:
78
+ rubygems_version: 1.3.5
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: A set of helpers and extensions for sinatra apps that expose REST resources.
82
+ test_files:
83
+ - test/helper.rb
84
+ - test/test_routes.rb