rack-api 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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