rack-api 0.1.0 → 0.1.1

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/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  .DS_Store
2
2
  pkg
3
3
  tmp
4
+ docs
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rack-api (0.1.0)
4
+ rack-api (0.1.1)
5
5
  activesupport (~> 3.0.6)
6
6
  rack (~> 1.2.1)
7
7
  rack-mount (~> 0.6.14)
@@ -24,8 +24,106 @@ Create web app APIs that respond to one or more formats using an elegant DSL.
24
24
  end
25
25
  end
26
26
 
27
+ === Rails Integration
28
+
29
+ First, set up your Gemfile like this:
30
+
31
+ gem "rack-api", "~> 0.1.0", :require => "rack/api"
32
+
33
+ Create your API somewhere. In this example, we'll add it to <tt>lib/api.rb</tt>.
34
+
35
+ Rack::API.app do
36
+ prefix "api"
37
+
38
+ version :v1 do
39
+ get "status(.:format)" do
40
+ {:success => true, :time => Time.now}
41
+ end
42
+ end
43
+ end
44
+
45
+ Load this file somehow. I'd create a <tt>config/initializers/dependencies.rb</tt> with something like
46
+
47
+ require "lib/api"
48
+
49
+ Finally, you can set up the API routing. Open <tt>config/routes.rb</tt> and add the following line:
50
+
51
+ mount Rack::API => "/"
52
+
53
+ If you define your API by inheriting from the Rack::API class, remember to mount your class instead.
54
+
55
+ mount MyAPI => "/"
56
+
27
57
  For additional examples, see https://github.com/fnando/rack-api/tree/master/examples.
28
58
 
59
+ === Using RSpec with Rack::API
60
+
61
+ You can easily test Rack::API apps by using Rack::Test. This applies to both RSpec and Test Unit. See what you need to do if you want to use it with RSpec.
62
+
63
+ First, open your <tt>spec/spec_helper.rb</tt> and add something like this:
64
+
65
+ require "rspec"
66
+ require "rack/test"
67
+
68
+ RSpec.configure do |config|
69
+ config.include Rack::Test::Methods
70
+ end
71
+
72
+ Then you can go to your spec file, say, <tt>spec/api_spec.rb</tt>. You need to define a helper method called +app+, which will point to your Rack::API (the class itself or your own class).
73
+
74
+ require "spec_helper"
75
+
76
+ describe Rack::API do
77
+ # Remember to use your own class if you
78
+ # inherited from Rack::API
79
+ def app; Rack::API; end
80
+
81
+ it "renders status page" do
82
+ get "/api/v1/status"
83
+ JSON.load(last_response.body).should == {:status => "running"}
84
+ last_response.status.should == 200
85
+ end
86
+ end
87
+
88
+ If you want to do expectations over basic authentication, you'll have some like this:
89
+
90
+ require "spec_helper"
91
+
92
+ describe Rack::API do
93
+ def basic_auth(username, password)
94
+ "Basic " + Base64.encode64("#{username}:#{password}")
95
+ end
96
+
97
+ it "requires authentication" do
98
+ get "/api/v1/status"
99
+ last_response.status.should == 401
100
+ end
101
+
102
+ it "grants access" do
103
+ get "/api/v1/status", {"HTTP_AUTHORIZATION" => basic_auth("john", "test")}
104
+ last_response.status.should == 200
105
+ end
106
+ end
107
+
108
+ To reduce duplication, you can move both <tt>basic_auth</tt> and <tt>app</tt> methods to a module, which will be included on RSpec.
109
+
110
+ RSpec.configure do |config|
111
+ config.include Rack::Test::Methods
112
+ config.include Helpers
113
+ end
114
+
115
+ Your <tt>Helpers</tt> module may look like this:
116
+
117
+ module Helpers
118
+ def app
119
+ Rack::API
120
+ end
121
+
122
+ def basic_auth(username, password)
123
+ "Basic " + Base64.encode64("#{username}:#{password}")
124
+ end
125
+ end
126
+
29
127
  == Maintainer
30
128
 
31
129
  * Nando Vieira (http://nandovieira.com.br)
data/Rakefile CHANGED
@@ -3,3 +3,10 @@ Bundler::GemHelper.install_tasks
3
3
 
4
4
  require "rspec/core/rake_task"
5
5
  RSpec::Core::RakeTask.new
6
+
7
+ require "rake/rdoctask"
8
+ Rake::RDocTask.new do |t|
9
+ t.rdoc_dir = "docs"
10
+ t.main = "README.rdoc"
11
+ t.rdoc_files.include "README.rdoc", *Dir["lib/**/*.rb"]
12
+ end
@@ -0,0 +1,32 @@
1
+ $:.push(File.dirname(__FILE__) + "/../lib")
2
+
3
+ # Just run `ruby examples/formats.rb` and then use something like
4
+ # `curl http://localhost:2345/api/v1/hello.xml`.
5
+
6
+ require "rack/api"
7
+ require "active_support/all"
8
+
9
+ module Rack
10
+ class API
11
+ module Formatter
12
+ class Xml < Base
13
+ def to_format
14
+ object.to_xml(:root => :messages)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ Rack::API.app do
22
+ prefix "api"
23
+ respond_to :xml
24
+
25
+ version :v1 do
26
+ get "/hello(.:format)" do
27
+ {:message => "Hello from Rack API"}
28
+ end
29
+ end
30
+ end
31
+
32
+ Rack::Handler::Thin.run Rack::API, :Port => 2345
@@ -0,0 +1,19 @@
1
+ $:.push(File.dirname(__FILE__) + "/../lib")
2
+
3
+ # Just run `ruby examples/formats.rb` and then use something like
4
+ # `curl http://localhost:2345/api/v1/hello.json` or
5
+ # `curl http://localhost:2345/api/v1/hello.jsonp?callback=myJSHandler`.
6
+
7
+ require "rack/api"
8
+
9
+ Rack::API.app do
10
+ prefix "api"
11
+
12
+ version :v1 do
13
+ get "/hello(.:format)" do
14
+ {:message => "Hello from Rack API"}
15
+ end
16
+ end
17
+ end
18
+
19
+ Rack::Handler::Thin.run Rack::API, :Port => 2345
@@ -3,15 +3,23 @@ require "rack/mount"
3
3
  require "active_support/hash_with_indifferent_access"
4
4
  require "json"
5
5
  require "logger"
6
+ require "forwardable"
6
7
 
7
8
  module Rack
8
9
  class API
9
10
  autoload :App, "rack/api/app"
10
11
  autoload :Formatter, "rack/api/formatter"
12
+ autoload :Middleware, "rack/api/middleware"
11
13
  autoload :Runner, "rack/api/runner"
12
14
  autoload :Response, "rack/api/response"
13
15
  autoload :Version, "rack/api/version"
14
16
 
17
+ class << self
18
+ extend Forwardable
19
+
20
+ def_delegators :runner, :version, :use, :prefix, :basic_auth, :helper, :respond_to
21
+ end
22
+
15
23
  # A shortcut for defining new APIs. Instead of creating a
16
24
  # class that inherits from Rack::API, you can simply pass a
17
25
  # block to the Rack::API.app method.
@@ -25,41 +33,6 @@ module Rack
25
33
  runner
26
34
  end
27
35
 
28
- # Add a middleware to the stack execution.
29
- #
30
- # Rack::API.app do
31
- # use MyMiddleware
32
- # end
33
- #
34
- def self.use(m)
35
- runner.use(m)
36
- end
37
-
38
- # Create a new API version.
39
- #
40
- # Rack::API.app do
41
- # version "v1" do
42
- # # define your API
43
- # end
44
- # end
45
- #
46
- def self.version(name, &block)
47
- runner.version(name, &block)
48
- end
49
-
50
- # Set an additional url prefix.
51
- #
52
- # Rack::API.app do
53
- # prefix "api"
54
- # version("v1") {}
55
- # end
56
- #
57
- # This API will be available through <tt>/api/v1</tt> path.
58
- #
59
- def self.prefix(name)
60
- runner.prefix(name)
61
- end
62
-
63
36
  # Reset all API definitions while using the Rack::API.app method.
64
37
  #
65
38
  def self.reset!
@@ -117,6 +117,15 @@ module Rack
117
117
  @headers = nil
118
118
  end
119
119
 
120
+ # Return credentials for Basic Authentication request.
121
+ #
122
+ def credentials
123
+ @credentials ||= begin
124
+ request = Rack::Auth::Basic::Request.new(env)
125
+ request.provided? ? request.credentials : []
126
+ end
127
+ end
128
+
120
129
  # Render the result of block.
121
130
  #
122
131
  def call(env) # :nodoc:
@@ -0,0 +1,7 @@
1
+ module Rack
2
+ class API
3
+ module Middleware
4
+ autoload :Format, "rack/api/middleware/format"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ module Rack
2
+ class API
3
+ module Middleware
4
+ class Format
5
+ def initialize(app, formats)
6
+ @app, @formats = app, formats.collect {|f| f.to_s}
7
+ end
8
+
9
+ def call(env)
10
+ request = Rack::Request.new(env)
11
+ params = request.env["rack.routing_args"].merge(request.params)
12
+ requested_format = params.fetch(:format, "json")
13
+
14
+ if @formats.include?(requested_format)
15
+ @app.call(env)
16
+ else
17
+ [406, {"Content-Type" => "text/plain"}, ["Invalid format"]]
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -7,22 +7,76 @@ module Rack
7
7
 
8
8
  def initialize
9
9
  @settings = {
10
- :prefix => "/",
11
- :formats => %w[json jsonp],
12
- :middlewares => []
10
+ :middlewares => [],
11
+ :helpers => [],
12
+ :global => {
13
+ :prefix => "/",
14
+ :formats => %w[json jsonp],
15
+ :middlewares => [],
16
+ :helpers => []
17
+ }
13
18
  }
14
19
  end
15
20
 
21
+ # Set configuration based on scope. When defining values outside version block,
22
+ # will set configuration using <tt>settings[:global]</tt> namespace.
23
+ #
24
+ # Use the Rack::API::Runner#option method to access a given setting.
25
+ #
26
+ def set(name, value, mode = :override)
27
+ target = settings[:version] ? settings : settings[:global]
28
+
29
+ if mode == :override
30
+ target[name] = value
31
+ else
32
+ target[name] << value
33
+ end
34
+ end
35
+
36
+ # Try to fetch local configuration, defaulting to the global setting.
37
+ # Return +nil+ when no configuration is defined.
38
+ #
39
+ def option(name, mode = :any)
40
+ if mode == :merge && (settings[name].kind_of?(Array) || settings[:global][name].kind_of?(Array))
41
+ settings[:global].fetch(name, []) | settings.fetch(name, [])
42
+ else
43
+ settings.fetch(name, settings[:global][name])
44
+ end
45
+ end
46
+
16
47
  # Add a middleware to the execution stack.
17
48
  #
49
+ # Global middlewares will be merged with local middlewares.
50
+ #
51
+ # Rack::API.app do
52
+ # use ResponseTime
53
+ #
54
+ # version :v1 do
55
+ # use Gzip
56
+ # end
57
+ # end
58
+ #
59
+ # The middleware stack will be something like <tt>[ResponseTime, Gzip]</tt>.
60
+ #
18
61
  def use(middleware, *args)
19
- settings[:middlewares] << [middleware, *args]
62
+ set :middlewares, [middleware, *args], :append
20
63
  end
21
64
 
22
65
  # Set an additional url prefix.
23
66
  #
24
67
  def prefix(name)
25
- settings[:prefix] = name
68
+ set :prefix, name
69
+ end
70
+
71
+ # Add a helper to application.
72
+ #
73
+ # helper MyHelpers
74
+ # helper { }
75
+ #
76
+ def helper(mod = nil, &block)
77
+ mod = Module.new(&block) if block_given?
78
+ raise ArgumentError, "you need to pass a module or block" unless mod
79
+ set :helpers, mod, :append
26
80
  end
27
81
 
28
82
  # Create a new API version.
@@ -48,8 +102,34 @@ module Rack
48
102
  # User.authenticate(user, pass)
49
103
  # end
50
104
  # end
105
+ #
106
+ # You can disable basic authentication by providing <tt>:none</tt> as
107
+ # realm.
108
+ #
109
+ # Rack::API.app do
110
+ # basic_auth "Protected Area" do |user, pass|
111
+ # User.authenticate(user, pass)
112
+ # end
113
+ #
114
+ # version :v1 do
115
+ # # this version is protected by the
116
+ # # global basic auth block above.
117
+ # end
118
+ #
119
+ # version :v2 do
120
+ # basic_auth :none
121
+ # # this version is now public
122
+ # end
123
+ #
124
+ # version :v3 do
125
+ # basic_auth "Admin" do |user, pass|
126
+ # user == "admin" && pass == "test"
127
+ # end
128
+ # end
129
+ # end
130
+ #
51
131
  def basic_auth(realm = "Restricted Area", &block)
52
- settings[:auth] = [realm, block]
132
+ set :auth, (realm == :none ? :none : [realm, block])
53
133
  end
54
134
 
55
135
  # Define the formats that this app implements.
@@ -65,8 +145,20 @@ module Rack
65
145
  # See Rack::API::Formatter::Jsonp for an example on how to create additional
66
146
  # formatters.
67
147
  #
148
+ # Local formats will override the global configuration on that context.
149
+ #
150
+ # Rack::API.app do
151
+ # respond_to :json, :xml, :jsonp
152
+ #
153
+ # version :v1 do
154
+ # respond_to :json
155
+ # end
156
+ # end
157
+ #
158
+ # The code above will accept only <tt>:json</tt> as format on version <tt>:v1</tt>.
159
+ #
68
160
  def respond_to(*formats)
69
- settings[:formats] = formats
161
+ set :formats, formats
70
162
  end
71
163
 
72
164
  # Hold all routes.
@@ -102,15 +194,30 @@ module Rack
102
194
  RUBY
103
195
  end
104
196
 
197
+ private
105
198
  def mount_path(path) # :nodoc:
106
- Rack::Mount::Utils.normalize_path([settings[:prefix], settings[:version], path].join("/"))
199
+ Rack::Mount::Utils.normalize_path([option(:prefix), settings[:version], path].join("/"))
107
200
  end
108
201
 
109
202
  def build_app(block) # :nodoc:
203
+ app = App.new(:block => block)
110
204
  builder = Rack::Builder.new
111
- builder.use Rack::Auth::Basic, settings[:auth][0], &settings[:auth][1] if settings[:auth]
112
- settings[:middlewares].each {|middleware| builder.use(*middleware)}
113
- builder.run App.new(:block => block)
205
+
206
+ # Add middleware for basic authentication.
207
+ auth = option(:auth)
208
+ builder.use Rack::Auth::Basic, auth[0], &auth[1] if auth && auth != :none
209
+
210
+ # Add middleware for format validation.
211
+ builder.use Rack::API::Middleware::Format, option(:formats)
212
+
213
+ # Add middlewares to executation stack.
214
+ option(:middlewares, :merged).each {|middleware| builder.use(*middleware)}
215
+
216
+ # Apply helpers to app.
217
+ helpers = option(:helpers)
218
+ app.extend *helpers unless helpers.empty?
219
+
220
+ builder.run(app)
114
221
  builder.to_app
115
222
  end
116
223
  end
@@ -3,7 +3,7 @@ module Rack
3
3
  module Version
4
4
  MAJOR = 0
5
5
  MINOR = 1
6
- PATCH = 0
6
+ PATCH = 1
7
7
  STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
8
8
  end
9
9
  end
@@ -3,9 +3,23 @@ require "spec_helper"
3
3
  describe Rack::API, "Basic Authentication" do
4
4
  before do
5
5
  Rack::API.app do
6
+ basic_auth do |user, pass|
7
+ user == "admin" && pass == "test"
8
+ end
9
+
6
10
  version :v1 do
11
+ get("/") { {:success => true} }
12
+ end
13
+
14
+ version :v2 do
15
+ basic_auth :none
16
+ get("/") { {:success => true} }
17
+ get("/credentials") { credentials }
18
+ end
19
+
20
+ version :v3 do
7
21
  basic_auth do |user, pass|
8
- user == "admin" && pass == "test"
22
+ user == "john" && pass == "test"
9
23
  end
10
24
 
11
25
  get("/") { {:success => true} }
@@ -13,18 +27,59 @@ describe Rack::API, "Basic Authentication" do
13
27
  end
14
28
  end
15
29
 
16
- it "denies access" do
17
- get "/v1/"
18
- last_response.status.should == 401
30
+ context "global authorization" do
31
+ it "denies access" do
32
+ get "/v1/"
33
+ last_response.status.should == 401
34
+
35
+ get "/v1/", {}, "HTTP_AUTHORIZATION" => basic_auth("admin", "invalid")
36
+ last_response.status.should == 401
37
+
38
+ get "/v1/", {}, "HTTP_AUTHORIZATION" => basic_auth("john", "test")
39
+ last_response.status.should == 401
40
+ end
41
+
42
+ it "grants access" do
43
+ get "/v1/", {}, "HTTP_AUTHORIZATION" => basic_auth("admin", "test")
19
44
 
20
- get "/v1/", {}, "HTTP_AUTHORIZATION" => basic_auth("admin", "invalid")
21
- last_response.status.should == 401
45
+ last_response.status.should == 200
46
+ JSON.load(last_response.body).should == {"success" => true}
47
+ end
22
48
  end
23
49
 
24
- it "grants access" do
25
- get "/v1/", {}, "HTTP_AUTHORIZATION" => basic_auth("admin", "test")
50
+ context "no authorization" do
51
+ it "grants access" do
52
+ get "/v2/"
53
+
54
+ last_response.status.should == 200
55
+ JSON.load(last_response.body).should == {"success" => true}
56
+ end
57
+ end
58
+
59
+ context "local authorization" do
60
+ it "denies access" do
61
+ get "/v3/"
62
+ last_response.status.should == 401
63
+
64
+ get "/v3/", {}, "HTTP_AUTHORIZATION" => basic_auth("admin", "test")
65
+ last_response.status.should == 401
66
+ end
67
+
68
+ it "grants access" do
69
+ get "/v3/", {}, "HTTP_AUTHORIZATION" => basic_auth("john", "test")
70
+
71
+ last_response.status.should == 200
72
+ JSON.load(last_response.body).should == {"success" => true}
73
+ end
74
+ end
75
+
76
+ it "returns credentials" do
77
+ get "/v2/credentials", {}, "HTTP_AUTHORIZATION" => basic_auth("admin", "test")
78
+ JSON.load(last_response.body).should == ["admin", "test"]
79
+ end
26
80
 
27
- last_response.status.should == 200
28
- JSON.load(last_response.body).should == {"success" => true}
81
+ it "returns empty array when no credentials are provided" do
82
+ get "/v2/credentials"
83
+ JSON.load(last_response.body).should == []
29
84
  end
30
85
  end
@@ -11,7 +11,7 @@ describe Rack::API, "Format" do
11
11
  end
12
12
  end
13
13
 
14
- it "ignores unknown formats" do
14
+ it "ignores unknown paths/formats" do
15
15
  get "/users.xml"
16
16
  last_response.status.should == 404
17
17
  end
@@ -26,6 +26,16 @@ describe Rack::API, "Format" do
26
26
  end
27
27
  end
28
28
 
29
+ context "invalid format" do
30
+ it "renders 406" do
31
+ get "/v1/users.invalid"
32
+
33
+ last_response.status.should == 406
34
+ last_response.body.should == "Invalid format"
35
+ last_response.headers["Content-Type"].should == "text/plain"
36
+ end
37
+ end
38
+
29
39
  context "JSONP" do
30
40
  it "renders when set through query string" do
31
41
  get "/v1", :format => "jsonp"
@@ -0,0 +1,33 @@
1
+ require "spec_helper"
2
+
3
+ describe Rack::API, "Helpers" do
4
+ before do
5
+ Rack::API.app do
6
+ version :v1 do
7
+ helper Module.new {
8
+ def helper_from_module
9
+ "module"
10
+ end
11
+ }
12
+
13
+ helper do
14
+ def helper_from_block
15
+ "block"
16
+ end
17
+ end
18
+
19
+ get("/") { [helper_from_block, helper_from_module] }
20
+ end
21
+ end
22
+ end
23
+
24
+ it "adds module helper" do
25
+ get "/v1"
26
+ JSON.load(last_response.body).should include("module")
27
+ end
28
+
29
+ it "adds block helper" do
30
+ get "/v1"
31
+ JSON.load(last_response.body).should include("block")
32
+ end
33
+ end
@@ -11,28 +11,23 @@ describe Rack::API::Runner do
11
11
 
12
12
  it "sets available formats" do
13
13
  subject.respond_to(:json, :jsonp, :atom)
14
- subject.settings[:formats].should == [:json, :jsonp, :atom]
14
+ subject.option(:formats).should == [:json, :jsonp, :atom]
15
15
  end
16
16
 
17
17
  it "sets prefix option" do
18
18
  subject.prefix("my/awesome/api")
19
- subject.settings[:prefix].should == "my/awesome/api"
20
- end
21
-
22
- it "considers prefix and version when building paths" do
23
- subject.settings.merge!(:prefix => "api", :version => "v1")
24
- subject.mount_path("users").should == "/api/v1/users"
19
+ subject.option(:prefix).should == "my/awesome/api"
25
20
  end
26
21
 
27
22
  it "stores middleware" do
28
23
  subject.use Rack::Auth::Basic
29
- subject.settings[:middlewares].should == [[Rack::Auth::Basic]]
24
+ subject.option(:middlewares, :merge).should == [[Rack::Auth::Basic]]
30
25
  end
31
26
 
32
27
  it "stores basic auth info" do
33
28
  handler = proc {}
34
29
 
35
30
  subject.basic_auth("Get out!", &handler)
36
- subject.settings[:auth].should == ["Get out!", handler]
31
+ subject.settings[:global][:auth].should == ["Get out!", handler]
37
32
  end
38
33
  end
@@ -0,0 +1,50 @@
1
+ require "spec_helper"
2
+
3
+ describe Rack::API::Runner, "Settings" do
4
+ it "uses global namespace when no version is defined" do
5
+ subject.set :foo, :bar
6
+ subject.settings[:global][:foo].should == :bar
7
+ end
8
+
9
+ it "uses local namespace when version is defined" do
10
+ subject.settings[:version] = "v1"
11
+ subject.set :foo, :bar
12
+
13
+ subject.settings[:foo].should == :bar
14
+ end
15
+
16
+ it "appends item when mode is :append" do
17
+ subject.settings[:global][:list] = []
18
+ subject.set :list, :item, :append
19
+
20
+ subject.settings[:global][:list].should == [:item]
21
+ end
22
+
23
+ it "overrides item when mode is :override" do
24
+ subject.settings[:global][:list] = []
25
+ subject.set :list, [:item], :override
26
+
27
+ subject.settings[:global][:list].should == [:item]
28
+ end
29
+
30
+ it "returns global value" do
31
+ subject.set :name, "John Doe"
32
+ subject.option(:name).should == "John Doe"
33
+ end
34
+
35
+ it "returns local value" do
36
+ subject.settings[:version] = "v1"
37
+ subject.set :name, "John Doe"
38
+
39
+ subject.option(:name).should == "John Doe"
40
+ end
41
+
42
+ it "prefers local setting over global one" do
43
+ subject.set :name, "Mary Doe"
44
+
45
+ subject.settings[:version] = "v1"
46
+ subject.set :name, "John Doe"
47
+
48
+ subject.option(:name).should == "John Doe"
49
+ end
50
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: rack-api
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.0
5
+ version: 0.1.1
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-04-07 00:00:00 Z
13
+ date: 2011-04-08 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -96,7 +96,9 @@ files:
96
96
  - Rakefile
97
97
  - examples/basic_auth.rb
98
98
  - examples/custom_class.rb
99
+ - examples/custom_format.rb
99
100
  - examples/custom_headers.rb
101
+ - examples/formats.rb
100
102
  - examples/middleware.rb
101
103
  - examples/multiple_versions.rb
102
104
  - examples/params.rb
@@ -106,6 +108,8 @@ files:
106
108
  - lib/rack/api/formatter.rb
107
109
  - lib/rack/api/formatter/base.rb
108
110
  - lib/rack/api/formatter/jsonp.rb
111
+ - lib/rack/api/middleware.rb
112
+ - lib/rack/api/middleware/format.rb
109
113
  - lib/rack/api/response.rb
110
114
  - lib/rack/api/runner.rb
111
115
  - lib/rack/api/version.rb
@@ -113,12 +117,14 @@ files:
113
117
  - spec/rack-api/basic_auth_spec.rb
114
118
  - spec/rack-api/format_spec.rb
115
119
  - spec/rack-api/headers_spec.rb
120
+ - spec/rack-api/helpers_spec.rb
116
121
  - spec/rack-api/http_methods_spec.rb
117
122
  - spec/rack-api/inheritance_spec.rb
118
123
  - spec/rack-api/middlewares_spec.rb
119
124
  - spec/rack-api/params_spec.rb
120
125
  - spec/rack-api/paths_spec.rb
121
126
  - spec/rack-api/runner_spec.rb
127
+ - spec/rack-api/settings_spec.rb
122
128
  - spec/rack-api/short_circuit_spec.rb
123
129
  - spec/spec_helper.rb
124
130
  - spec/support/awesome_middleware.rb
@@ -156,12 +162,14 @@ test_files:
156
162
  - spec/rack-api/basic_auth_spec.rb
157
163
  - spec/rack-api/format_spec.rb
158
164
  - spec/rack-api/headers_spec.rb
165
+ - spec/rack-api/helpers_spec.rb
159
166
  - spec/rack-api/http_methods_spec.rb
160
167
  - spec/rack-api/inheritance_spec.rb
161
168
  - spec/rack-api/middlewares_spec.rb
162
169
  - spec/rack-api/params_spec.rb
163
170
  - spec/rack-api/paths_spec.rb
164
171
  - spec/rack-api/runner_spec.rb
172
+ - spec/rack-api/settings_spec.rb
165
173
  - spec/rack-api/short_circuit_spec.rb
166
174
  - spec/spec_helper.rb
167
175
  - spec/support/awesome_middleware.rb