rack-api 0.2.2 → 0.3.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/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rack-api (0.2.2)
4
+ rack-api (0.3.0)
5
5
  activesupport (>= 3.0.0)
6
6
  rack (>= 1.0.0)
7
7
  rack-mount (>= 0.6.0)
@@ -0,0 +1,25 @@
1
+ $:.push(File.dirname(__FILE__) + "/../lib")
2
+
3
+ # Just run `ruby examples/basic_auth.rb` and then use something like
4
+ # `curl http://localhost:2345/api/v1/` or
5
+ # `curl http://localhost:2345/api/v1/This%20is%20so%20cool`.
6
+
7
+ require "rack/api"
8
+
9
+ Rack::API.app do
10
+ prefix "api"
11
+
12
+ helper do
13
+ def default_message
14
+ "Hello from Rack API"
15
+ end
16
+ end
17
+
18
+ version :v1 do
19
+ get "/(:message)" do
20
+ {:message => params.fetch(:message, default_message)}
21
+ end
22
+ end
23
+ end
24
+
25
+ Rack::Handler::Thin.run Rack::API, :Port => 2345
@@ -0,0 +1,33 @@
1
+ $:.push(File.dirname(__FILE__) + "/../lib")
2
+
3
+ # Just run `ruby examples/simple.rb` and then use something like
4
+ # `curl http://localhost:2345/api/v1/`.
5
+
6
+ require "rack/api"
7
+ require "logger"
8
+
9
+ $logger = Logger.new(STDOUT)
10
+
11
+ # Simulate ActiveRecord's exception.
12
+ module ActiveRecord
13
+ class RecordNotFound < StandardError
14
+ end
15
+ end
16
+
17
+ Rack::API.app do
18
+ prefix "api"
19
+
20
+ rescue_from ActiveRecord::RecordNotFound, :status => 404
21
+ rescue_from Exception do |error|
22
+ $logger.error error.message
23
+ [500, {"Content-Type" => "text/plain"}, []]
24
+ end
25
+
26
+ version :v1 do
27
+ get "/" do
28
+ raise "Oh no! Something is really wrong!"
29
+ end
30
+ end
31
+ end
32
+
33
+ Rack::Handler::Thin.run Rack::API, :Port => 2345
data/lib/rack/api/app.rb CHANGED
@@ -48,6 +48,11 @@ module Rack
48
48
  #
49
49
  attr_accessor :url_options
50
50
 
51
+ # Hold handlers, that will wrap exceptions
52
+ # into a normalized response.
53
+ #
54
+ attr_accessor :rescuers
55
+
51
56
  def initialize(options)
52
57
  options.each do |name, value|
53
58
  instance_variable_set("@#{name}", value)
@@ -158,6 +163,8 @@ module Rack
158
163
  end
159
164
 
160
165
  response.respond_to?(:to_rack) ? response.to_rack : response
166
+ rescue Exception => exception
167
+ handle_exception exception
161
168
  end
162
169
 
163
170
  # Return response content type based on extension.
@@ -229,6 +236,21 @@ module Rack
229
236
  throw :error, Response.new(:status => 406, :message => "Unknown format")
230
237
  end
231
238
  end
239
+
240
+ def handle_exception(error) # :nodoc:
241
+ rescuer = rescuers.find do |r|
242
+ error_class = eval("::#{r[:class_name]}") rescue nil
243
+ error_class && error.kind_of?(error_class)
244
+ end
245
+
246
+ raise error unless rescuer
247
+
248
+ if rescuer[:block]
249
+ instance_exec(error, &rescuer[:block])
250
+ else
251
+ [rescuer[:options].fetch(:status, 500), {"Content-Type" => "text/plain"}, []]
252
+ end
253
+ end
232
254
  end
233
255
  end
234
256
  end
@@ -4,7 +4,7 @@ module Rack
4
4
  HTTP_METHODS = %w[get post put delete head]
5
5
 
6
6
  DELEGATE_METHODS = %w[
7
- version use prefix basic_auth
7
+ version use prefix basic_auth rescue_from
8
8
  helper respond_to default_url_options
9
9
  ]
10
10
 
@@ -14,11 +14,13 @@ module Rack
14
14
  @settings = {
15
15
  :middlewares => [],
16
16
  :helpers => [],
17
+ :rescuers => [],
17
18
  :global => {
18
- :prefix => "/",
19
- :formats => %w[json jsonp],
19
+ :prefix => "/",
20
+ :formats => %w[json jsonp],
20
21
  :middlewares => [],
21
- :helpers => []
22
+ :helpers => [],
23
+ :rescuers => []
22
24
  }
23
25
  }
24
26
  end
@@ -234,6 +236,19 @@ module Rack
234
236
  RUBY
235
237
  end
236
238
 
239
+ # Rescue from the specified exception.
240
+ #
241
+ # rescue_from ActiveRecord::RecordNotFound, :status => 404
242
+ # rescue_from Exception, :status => 500
243
+ # rescue_from Exception do
244
+ # $logger.error error.inspect
245
+ # [500, {"Content-Type" => "text/plain"}, []]
246
+ # end
247
+ #
248
+ def rescue_from(exception, options = {}, &block)
249
+ set :rescuers, {:class_name => exception.name, :options => options, :block => block}, :append
250
+ end
251
+
237
252
  private
238
253
  def mount_path(path) # :nodoc:
239
254
  Rack::Mount::Utils.normalize_path([option(:prefix), settings[:version], path].join("/"))
@@ -249,7 +264,8 @@ module Rack
249
264
  :default_format => default_format,
250
265
  :version => option(:version),
251
266
  :prefix => option(:prefix),
252
- :url_options => option(:url_options)
267
+ :url_options => option(:url_options),
268
+ :rescuers => option(:rescuers, :merge).reverse
253
269
  })
254
270
 
255
271
  builder = Rack::Builder.new
@@ -2,8 +2,8 @@ module Rack
2
2
  class API
3
3
  module Version
4
4
  MAJOR = 0
5
- MINOR = 2
6
- PATCH = 2
5
+ MINOR = 3
6
+ PATCH = 0
7
7
  STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
8
8
  end
9
9
  end
@@ -9,4 +9,5 @@ describe Rack::API, "delegators" do
9
9
  it { should respond_to(:basic_auth) }
10
10
  it { should respond_to(:helper) }
11
11
  it { should respond_to(:default_url_options) }
12
+ it { should respond_to(:rescue_from) }
12
13
  end
@@ -0,0 +1,103 @@
1
+ require "spec_helper"
2
+
3
+ describe Rack::API, "Rescue from exceptions" do
4
+ class NotFound < StandardError; end
5
+
6
+ it "rescues from NotFound exception" do
7
+ Rack::API.app do
8
+ rescue_from NotFound, :status => 404
9
+
10
+ version :v1 do
11
+ get("/404") { raise NotFound }
12
+ end
13
+ end
14
+
15
+ get "/v1/404"
16
+ last_response.headers["Content-Type"].should == "text/plain"
17
+ last_response.body.should == ""
18
+ last_response.status.should == 404
19
+ end
20
+
21
+ it "rescues from all exceptions" do
22
+ Rack::API.app do
23
+ rescue_from Exception
24
+
25
+ version :v1 do
26
+ get("/500") { raise "Oops!" }
27
+ end
28
+ end
29
+
30
+ get "/v1/500"
31
+ last_response.headers["Content-Type"].should == "text/plain"
32
+ last_response.body.should == ""
33
+ last_response.status.should == 500
34
+ end
35
+
36
+ it "rescues from exception by using a block" do
37
+ Rack::API.app do
38
+ rescue_from Exception do
39
+ [501, {"Content-Type" => "application/json"}, [{:error => true}.to_json]]
40
+ end
41
+
42
+ version :v1 do
43
+ get("/501") { raise "Oops!" }
44
+ end
45
+ end
46
+
47
+ get "/v1/501"
48
+ last_response.headers["Content-Type"].should == "application/json"
49
+ last_response.body.should == {:error => true}.to_json
50
+ last_response.status.should == 501
51
+ end
52
+
53
+ it "rescues from exception by using inner handler" do
54
+ Rack::API.app do
55
+ rescue_from Exception
56
+
57
+ version :v1 do
58
+ rescue_from Exception do
59
+ [500, {"Content-Type" => "text/plain"}, ["inner handler"]]
60
+ end
61
+
62
+ get("/500") { raise "Oops!" }
63
+ end
64
+ end
65
+
66
+ get "/v1/500"
67
+ last_response.body.should == "inner handler"
68
+ end
69
+
70
+ it "rescues from exception in app's context" do
71
+ Rack::API.app do
72
+ rescue_from Exception
73
+
74
+ version :v1 do
75
+ rescue_from Exception do
76
+ [500, {"Content-Type" => "text/plain"}, [self.class.name]]
77
+ end
78
+
79
+ get("/500") { raise "Oops!" }
80
+ end
81
+ end
82
+
83
+ get "/v1/500"
84
+ last_response.body.should == "Rack::API::App"
85
+ end
86
+
87
+ it "yields the exception object" do
88
+ Rack::API.app do
89
+ rescue_from Exception
90
+
91
+ version :v1 do
92
+ rescue_from Exception do |error|
93
+ [500, {"Content-Type" => "text/plain"}, [error.message]]
94
+ end
95
+
96
+ get("/500") { raise "Oops!" }
97
+ end
98
+ end
99
+
100
+ get "/v1/500"
101
+ last_response.body.should == "Oops!"
102
+ end
103
+ end
@@ -3,7 +3,7 @@ require "spec_helper"
3
3
  describe Rack::API::Runner do
4
4
  specify "sanity check for delegate methods" do
5
5
  # remember to update spec/method_delegation_spec.rb
6
- Rack::API::Runner::DELEGATE_METHODS.size.should == 7
6
+ Rack::API::Runner::DELEGATE_METHODS.size.should == 8
7
7
  end
8
8
 
9
9
  it "responds to http methods" do
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: rack-api
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.2.2
5
+ version: 0.3.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Nando Vieira
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-06-08 00:00:00 Z
13
+ date: 2011-06-09 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -110,9 +110,11 @@ files:
110
110
  - examples/custom_format.rb
111
111
  - examples/custom_headers.rb
112
112
  - examples/formats.rb
113
+ - examples/helpers.rb
113
114
  - examples/middleware.rb
114
115
  - examples/multiple_versions.rb
115
116
  - examples/params.rb
117
+ - examples/rescue_from.rb
116
118
  - examples/simple.rb
117
119
  - lib/rack/api.rb
118
120
  - lib/rack/api/app.rb
@@ -138,6 +140,7 @@ files:
138
140
  - spec/rack-api/middlewares_spec.rb
139
141
  - spec/rack-api/params_spec.rb
140
142
  - spec/rack-api/paths_spec.rb
143
+ - spec/rack-api/rescue_from_spec.rb
141
144
  - spec/rack-api/runner_spec.rb
142
145
  - spec/rack-api/settings_spec.rb
143
146
  - spec/rack-api/short_circuit_spec.rb
@@ -188,6 +191,7 @@ test_files:
188
191
  - spec/rack-api/middlewares_spec.rb
189
192
  - spec/rack-api/params_spec.rb
190
193
  - spec/rack-api/paths_spec.rb
194
+ - spec/rack-api/rescue_from_spec.rb
191
195
  - spec/rack-api/runner_spec.rb
192
196
  - spec/rack-api/settings_spec.rb
193
197
  - spec/rack-api/short_circuit_spec.rb