rack-api 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/.gitignore +3 -0
- data/.rspec +1 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +49 -0
- data/README.rdoc +54 -0
- data/Rakefile +5 -0
- data/examples/basic_auth.rb +22 -0
- data/examples/custom_class.rb +28 -0
- data/examples/custom_headers.rb +19 -0
- data/examples/middleware.rb +34 -0
- data/examples/multiple_versions.rb +24 -0
- data/examples/params.rb +18 -0
- data/examples/simple.rb +18 -0
- data/lib/rack/api/app.rb +162 -0
- data/lib/rack/api/formatter/base.rb +20 -0
- data/lib/rack/api/formatter/jsonp.rb +11 -0
- data/lib/rack/api/formatter.rb +8 -0
- data/lib/rack/api/response.rb +21 -0
- data/lib/rack/api/runner.rb +118 -0
- data/lib/rack/api/version.rb +10 -0
- data/lib/rack/api.rb +83 -0
- data/rack-api.gemspec +26 -0
- data/spec/rack-api/basic_auth_spec.rb +30 -0
- data/spec/rack-api/format_spec.rb +110 -0
- data/spec/rack-api/headers_spec.rb +24 -0
- data/spec/rack-api/http_methods_spec.rb +28 -0
- data/spec/rack-api/inheritance_spec.rb +12 -0
- data/spec/rack-api/middlewares_spec.rb +17 -0
- data/spec/rack-api/params_spec.rb +27 -0
- data/spec/rack-api/paths_spec.rb +35 -0
- data/spec/rack-api/runner_spec.rb +38 -0
- data/spec/rack-api/short_circuit_spec.rb +33 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/awesome_middleware.rb +10 -0
- data/spec/support/core_ext.rb +5 -0
- data/spec/support/helpers.rb +9 -0
- data/spec/support/myapp.rb +7 -0
- metadata +170 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color --format documentation
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rack-api (0.1.0)
|
5
|
+
activesupport (~> 3.0.6)
|
6
|
+
rack (~> 1.2.1)
|
7
|
+
rack-mount (~> 0.6.14)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
activesupport (3.0.6)
|
13
|
+
archive-tar-minitar (0.5.2)
|
14
|
+
columnize (0.3.2)
|
15
|
+
diff-lcs (1.1.2)
|
16
|
+
linecache19 (0.5.12)
|
17
|
+
ruby_core_source (>= 0.1.4)
|
18
|
+
rack (1.2.2)
|
19
|
+
rack-mount (0.6.14)
|
20
|
+
rack (>= 1.0.0)
|
21
|
+
rack-test (0.5.7)
|
22
|
+
rack (>= 1.0)
|
23
|
+
rspec (2.5.0)
|
24
|
+
rspec-core (~> 2.5.0)
|
25
|
+
rspec-expectations (~> 2.5.0)
|
26
|
+
rspec-mocks (~> 2.5.0)
|
27
|
+
rspec-core (2.5.1)
|
28
|
+
rspec-expectations (2.5.0)
|
29
|
+
diff-lcs (~> 1.1.2)
|
30
|
+
rspec-mocks (2.5.0)
|
31
|
+
ruby-debug-base19 (0.11.25)
|
32
|
+
columnize (>= 0.3.1)
|
33
|
+
linecache19 (>= 0.5.11)
|
34
|
+
ruby_core_source (>= 0.1.4)
|
35
|
+
ruby-debug19 (0.11.6)
|
36
|
+
columnize (>= 0.3.1)
|
37
|
+
linecache19 (>= 0.5.11)
|
38
|
+
ruby-debug-base19 (>= 0.11.19)
|
39
|
+
ruby_core_source (0.1.5)
|
40
|
+
archive-tar-minitar (>= 0.5.2)
|
41
|
+
|
42
|
+
PLATFORMS
|
43
|
+
ruby
|
44
|
+
|
45
|
+
DEPENDENCIES
|
46
|
+
rack-api!
|
47
|
+
rack-test (~> 0.5.7)
|
48
|
+
rspec (~> 2.5.0)
|
49
|
+
ruby-debug19
|
data/README.rdoc
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
= Rack::API
|
2
|
+
|
3
|
+
Create web app APIs that respond to one or more formats using an elegant DSL.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
gem install rack-api
|
8
|
+
|
9
|
+
== Usage
|
10
|
+
|
11
|
+
=== Basic example
|
12
|
+
|
13
|
+
Rack::API.app do
|
14
|
+
prefix "api"
|
15
|
+
|
16
|
+
version :v1 do
|
17
|
+
get "users(.:format)" do
|
18
|
+
User.all
|
19
|
+
end
|
20
|
+
|
21
|
+
get "users/:id(.:format)" do
|
22
|
+
User.find(params[:id])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
For additional examples, see https://github.com/fnando/rack-api/tree/master/examples.
|
28
|
+
|
29
|
+
== Maintainer
|
30
|
+
|
31
|
+
* Nando Vieira (http://nandovieira.com.br)
|
32
|
+
|
33
|
+
== License
|
34
|
+
|
35
|
+
(The MIT License)
|
36
|
+
|
37
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
38
|
+
a copy of this software and associated documentation files (the
|
39
|
+
'Software'), to deal in the Software without restriction, including
|
40
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
41
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
42
|
+
permit persons to whom the Software is furnished to do so, subject to
|
43
|
+
the following conditions:
|
44
|
+
|
45
|
+
The above copyright notice and this permission notice shall be
|
46
|
+
included in all copies or substantial portions of the Software.
|
47
|
+
|
48
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
49
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
50
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
51
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
52
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
53
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
54
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
$:.push(File.dirname(__FILE__) + "/../lib")
|
2
|
+
|
3
|
+
# Just run `ruby examples/basic_auth.rb` and then use something like
|
4
|
+
# `curl -u admin:test http://localhost:2345/api/v1/`.
|
5
|
+
|
6
|
+
require "rack/api"
|
7
|
+
|
8
|
+
Rack::API.app do
|
9
|
+
prefix "api"
|
10
|
+
|
11
|
+
basic_auth do |user, pass|
|
12
|
+
user == "admin" && pass == "test"
|
13
|
+
end
|
14
|
+
|
15
|
+
version :v1 do
|
16
|
+
get "/" do
|
17
|
+
{:message => "Hello, awesome API!"}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Rack::Handler::Thin.run Rack::API, :Port => 2345
|
@@ -0,0 +1,28 @@
|
|
1
|
+
$:.push(File.dirname(__FILE__) + "/../lib")
|
2
|
+
|
3
|
+
# Just run `ruby examples/custom_class.rb` and then use something like
|
4
|
+
# `curl http://localhost:2345/api/v1/` and `curl http://localhost:2345/api/v2/`.
|
5
|
+
|
6
|
+
require "rack/api"
|
7
|
+
|
8
|
+
class MyApp < Rack::API
|
9
|
+
prefix "api"
|
10
|
+
|
11
|
+
version :v1 do
|
12
|
+
get "/" do
|
13
|
+
{:message => "Using API v1"}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class MyApp < Rack::API
|
19
|
+
prefix "api"
|
20
|
+
|
21
|
+
version :v2 do
|
22
|
+
get "/" do
|
23
|
+
{:message => "Using API v2"}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Rack::Handler::Thin.run MyApp, :Port => 2345
|
@@ -0,0 +1,19 @@
|
|
1
|
+
$:.push(File.dirname(__FILE__) + "/../lib")
|
2
|
+
|
3
|
+
# Just run `ruby examples/custom_headers.rb` and then use something like
|
4
|
+
# `curl -i http://localhost:2345/api/v1/`.
|
5
|
+
|
6
|
+
require "rack/api"
|
7
|
+
|
8
|
+
Rack::API.app do
|
9
|
+
prefix "api"
|
10
|
+
|
11
|
+
version :v1 do
|
12
|
+
get "/" do
|
13
|
+
headers["X-Awesome"] = "U R Awesome!"
|
14
|
+
{:message => "Hello, awesome API!"}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Rack::Handler::Thin.run Rack::API, :Port => 2345
|
@@ -0,0 +1,34 @@
|
|
1
|
+
$:.push(File.dirname(__FILE__) + "/../lib")
|
2
|
+
|
3
|
+
# Just run `ruby examples/middleware.rb` and then use something like
|
4
|
+
# `curl http://localhost:2345/api/v1/`.
|
5
|
+
|
6
|
+
require "rack/api"
|
7
|
+
require "json"
|
8
|
+
|
9
|
+
class ResponseTime
|
10
|
+
def initialize(app)
|
11
|
+
@app = app
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
start = Time.now
|
16
|
+
status, headers, response = @app.call(env)
|
17
|
+
elapsed = Time.now - start
|
18
|
+
response = JSON.load(response.first).merge(:response_time => elapsed)
|
19
|
+
[status, headers, [response.to_json]]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Rack::API.app do
|
24
|
+
prefix "api"
|
25
|
+
use ResponseTime
|
26
|
+
|
27
|
+
version :v1 do
|
28
|
+
get "/" do
|
29
|
+
{:message => "Hello, awesome API!"}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Rack::Handler::Thin.run Rack::API, :Port => 2345
|
@@ -0,0 +1,24 @@
|
|
1
|
+
$:.push(File.dirname(__FILE__) + "/../lib")
|
2
|
+
|
3
|
+
# Just run `ruby examples/multiple_versions.rb` and then use something like
|
4
|
+
# `curl http://localhost:2345/api/v1/` and `curl http://localhost:2345/api/v2`.
|
5
|
+
|
6
|
+
require "rack/api"
|
7
|
+
|
8
|
+
Rack::API.app do
|
9
|
+
prefix "api"
|
10
|
+
|
11
|
+
version :v1 do
|
12
|
+
get "/" do
|
13
|
+
{:message => "You're using API v1"}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
version :v2 do
|
18
|
+
get "/" do
|
19
|
+
{:message => "You're using API v2"}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Rack::Handler::Thin.run Rack::API, :Port => 2345
|
data/examples/params.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
$:.push(File.dirname(__FILE__) + "/../lib")
|
2
|
+
|
3
|
+
# Just run `ruby examples/params.rb` and then use something like
|
4
|
+
# `curl http://localhost:2345/api/v1/hello/John`.
|
5
|
+
|
6
|
+
require "rack/api"
|
7
|
+
|
8
|
+
Rack::API.app do
|
9
|
+
prefix "api"
|
10
|
+
|
11
|
+
version :v1 do
|
12
|
+
get "/hello/:name" do
|
13
|
+
{:message => "Hello, #{params[:name]}"}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Rack::Handler::Thin.run Rack::API, :Port => 2345
|
data/examples/simple.rb
ADDED
@@ -0,0 +1,18 @@
|
|
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
|
+
|
8
|
+
Rack::API.app do
|
9
|
+
prefix "api"
|
10
|
+
|
11
|
+
version :v1 do
|
12
|
+
get "/" do
|
13
|
+
{:message => "Hello, awesome API!"}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Rack::Handler::Thin.run Rack::API, :Port => 2345
|
data/lib/rack/api/app.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
module Rack
|
2
|
+
class API
|
3
|
+
class App
|
4
|
+
# Registered content types. If you want to use
|
5
|
+
# a custom formatter that is not listed here,
|
6
|
+
# you have to manually add it. Otherwise,
|
7
|
+
# Rack::API::App::DEFAULT_MIME_TYPE will be used
|
8
|
+
# as the content type.
|
9
|
+
#
|
10
|
+
MIME_TYPES = {
|
11
|
+
"json" => "application/json",
|
12
|
+
"jsonp" => "application/javascript",
|
13
|
+
"xml" => "application/xml",
|
14
|
+
"rss" => "application/rss+xml",
|
15
|
+
"atom" => "application/atom+xml",
|
16
|
+
"html" => "text/html",
|
17
|
+
"yaml" => "application/x-yaml",
|
18
|
+
"txt" => "text/plain"
|
19
|
+
}
|
20
|
+
|
21
|
+
# Default content type. Will be used when a given format
|
22
|
+
# hasn't been registered on Rack::API::App::MIME_TYPES.
|
23
|
+
#
|
24
|
+
DEFAULT_MIME_TYPE = "application/octet-stream"
|
25
|
+
|
26
|
+
attr_reader :block
|
27
|
+
attr_reader :env
|
28
|
+
|
29
|
+
# Hold block that will be executed in case the
|
30
|
+
# route is recognized.
|
31
|
+
#
|
32
|
+
def initialize(options)
|
33
|
+
options.each do |name, value|
|
34
|
+
instance_variable_set("@#{name}", value)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Always log to the standard output.
|
39
|
+
#
|
40
|
+
def logger
|
41
|
+
@logger ||= Logger.new(STDOUT)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Hold headers that will be sent on the response.
|
45
|
+
#
|
46
|
+
def headers
|
47
|
+
@headers ||= {}
|
48
|
+
end
|
49
|
+
|
50
|
+
# Merge all params into one single hash.
|
51
|
+
#
|
52
|
+
def params
|
53
|
+
@params ||= HashWithIndifferentAccess.new(request.params.merge(env["rack.routing_args"]))
|
54
|
+
end
|
55
|
+
|
56
|
+
# Return a request object.
|
57
|
+
#
|
58
|
+
def request
|
59
|
+
@request ||= Rack::Request.new(env)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Return the requested format. Defaults to JSON.
|
63
|
+
#
|
64
|
+
def format
|
65
|
+
params.fetch(:format, "json")
|
66
|
+
end
|
67
|
+
|
68
|
+
# Stop processing by rendering the provided information.
|
69
|
+
#
|
70
|
+
# Rack::API.app do
|
71
|
+
# version :v1 do
|
72
|
+
# get "/" do
|
73
|
+
# error(:status => 403, :message => "Not here!")
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# Valid options are:
|
79
|
+
#
|
80
|
+
# # <tt>:status</tt>: a HTTP status code. Defaults to 403.
|
81
|
+
# # <tt>:message</tt>: a message that will be rendered as the response body. Defaults to "Forbidden".
|
82
|
+
# # <tt>:headers</tt>: the response headers. Defaults to <tt>{"Content-Type" => "text/plain"}</tt>.
|
83
|
+
#
|
84
|
+
# You can also provide a object that responds to <tt>to_rack</tt>. In this case, this
|
85
|
+
# method must return a valid Rack response (a 3-item array).
|
86
|
+
#
|
87
|
+
# class MyError
|
88
|
+
# def self.to_rack
|
89
|
+
# [500, {"Content-Type" => "text/plain"}, ["Internal Server Error"]]
|
90
|
+
# end
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# Rack::API.app do
|
94
|
+
# version :v1 do
|
95
|
+
# get "/" do
|
96
|
+
# error(MyError)
|
97
|
+
# end
|
98
|
+
# end
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
def error(options = {})
|
102
|
+
throw :error, Response.new(options)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Set response status code.
|
106
|
+
#
|
107
|
+
def status(*args)
|
108
|
+
@status = args.first unless args.empty?
|
109
|
+
@status || 200
|
110
|
+
end
|
111
|
+
|
112
|
+
# Reset environment between requests.
|
113
|
+
#
|
114
|
+
def reset! # :nodoc:
|
115
|
+
@params = nil
|
116
|
+
@request = nil
|
117
|
+
@headers = nil
|
118
|
+
end
|
119
|
+
|
120
|
+
# Render the result of block.
|
121
|
+
#
|
122
|
+
def call(env) # :nodoc:
|
123
|
+
reset!
|
124
|
+
@env = env
|
125
|
+
|
126
|
+
response = catch(:error) do
|
127
|
+
render instance_eval(&block)
|
128
|
+
end
|
129
|
+
|
130
|
+
response.respond_to?(:to_rack) ? response.to_rack : response
|
131
|
+
end
|
132
|
+
|
133
|
+
# Return response content type based on extension.
|
134
|
+
# If you're using an unknown extension that wasn't registered on
|
135
|
+
# Rack::API::App::MIME_TYPES, it will return Rack::API::App::DEFAULT_MIME_TYPE,
|
136
|
+
# which defaults to <tt>application/octet-stream</tt>.
|
137
|
+
#
|
138
|
+
def content_type
|
139
|
+
mime = MIME_TYPES.fetch(format, DEFAULT_MIME_TYPE)
|
140
|
+
headers.fetch("Content-Type", mime)
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
def render(response) # :nodoc:
|
145
|
+
[status, headers.merge("Content-Type" => content_type), [format_response(response)]]
|
146
|
+
end
|
147
|
+
|
148
|
+
def format_response(response) # :nodoc:
|
149
|
+
formatter_name = format.split("_").collect {|word| word[0,1].upcase + word[1,word.size].downcase}.join("")
|
150
|
+
|
151
|
+
if Rack::API::Formatter.const_defined?(formatter_name)
|
152
|
+
formatter = Rack::API::Formatter.const_get(formatter_name).new(response, params)
|
153
|
+
formatter.to_format
|
154
|
+
elsif response.respond_to?("to_#{format}")
|
155
|
+
response.__send__("to_#{format}")
|
156
|
+
else
|
157
|
+
throw :error, Response.new(:status => 406, :message => "Unknown format")
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rack
|
2
|
+
class API
|
3
|
+
module Formatter
|
4
|
+
class Base
|
5
|
+
attr_accessor :object
|
6
|
+
attr_accessor :params
|
7
|
+
|
8
|
+
class AbstractMethodError < StandardError; end
|
9
|
+
|
10
|
+
def initialize(object, params)
|
11
|
+
@object, @params = object, params
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_format
|
15
|
+
raise AbstractMethodError
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Rack
|
2
|
+
class API
|
3
|
+
class Response
|
4
|
+
attr_reader :options
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_rack
|
11
|
+
return options.to_rack if options.respond_to?(:to_rack)
|
12
|
+
|
13
|
+
[
|
14
|
+
options.fetch(:status, 403),
|
15
|
+
{"Content-Type" => "text/plain"}.merge(options.fetch(:headers, {})),
|
16
|
+
[options.fetch(:message, "Forbidden")]
|
17
|
+
]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Rack
|
2
|
+
class API
|
3
|
+
class Runner
|
4
|
+
HTTP_METHODS = %w[get post put delete head]
|
5
|
+
|
6
|
+
attr_accessor :settings
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@settings = {
|
10
|
+
:prefix => "/",
|
11
|
+
:formats => %w[json jsonp],
|
12
|
+
:middlewares => []
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
# Add a middleware to the execution stack.
|
17
|
+
#
|
18
|
+
def use(middleware, *args)
|
19
|
+
settings[:middlewares] << [middleware, *args]
|
20
|
+
end
|
21
|
+
|
22
|
+
# Set an additional url prefix.
|
23
|
+
#
|
24
|
+
def prefix(name)
|
25
|
+
settings[:prefix] = name
|
26
|
+
end
|
27
|
+
|
28
|
+
# Create a new API version.
|
29
|
+
#
|
30
|
+
def version(name, &block)
|
31
|
+
raise ArgumentError, "you need to pass a block" unless block_given?
|
32
|
+
settings[:version] = name.to_s
|
33
|
+
instance_eval(&block)
|
34
|
+
settings.delete(:version)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Run all routes.
|
38
|
+
#
|
39
|
+
def call(env) # :nodoc:
|
40
|
+
route_set.freeze.call(env)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Require basic authentication before processing other requests.
|
44
|
+
# The authentication reques must be defined before routes.
|
45
|
+
#
|
46
|
+
# Rack::API.app do
|
47
|
+
# basic_auth "Protected Area" do |user, pass|
|
48
|
+
# User.authenticate(user, pass)
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
def basic_auth(realm = "Restricted Area", &block)
|
52
|
+
settings[:auth] = [realm, block]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Define the formats that this app implements.
|
56
|
+
# Respond only to <tt>:json</tt> by default.
|
57
|
+
#
|
58
|
+
# When setting a format you have some alternatives on how this object
|
59
|
+
# will be formated.
|
60
|
+
#
|
61
|
+
# First, Rack::API will look for a formatter defined on Rack::API::Formatter
|
62
|
+
# namespace. If there's no formatter, it will look for a method <tt>to_<format></tt>.
|
63
|
+
# It will raise an exception if no formatter method has been defined.
|
64
|
+
#
|
65
|
+
# See Rack::API::Formatter::Jsonp for an example on how to create additional
|
66
|
+
# formatters.
|
67
|
+
#
|
68
|
+
def respond_to(*formats)
|
69
|
+
settings[:formats] = formats
|
70
|
+
end
|
71
|
+
|
72
|
+
# Hold all routes.
|
73
|
+
#
|
74
|
+
def route_set # :nodoc:
|
75
|
+
@route_set ||= Rack::Mount::RouteSet.new
|
76
|
+
end
|
77
|
+
|
78
|
+
# Define a new routing that will be triggered when both request method and
|
79
|
+
# path are recognized.
|
80
|
+
#
|
81
|
+
# You're better off using all verb shortcut methods. Implemented verbs are
|
82
|
+
# +get+, +post+, +put+, +delete+ and +head+.
|
83
|
+
#
|
84
|
+
# class MyAPI < Rack::API
|
85
|
+
# version "v1" do
|
86
|
+
# get "users(.:format)" do
|
87
|
+
# # do something
|
88
|
+
# end
|
89
|
+
# end
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
def route(method, path, requirements = {}, &block)
|
93
|
+
path = Rack::Mount::Strexp.compile mount_path(path), requirements, %w[ / . ? ]
|
94
|
+
route_set.add_route(build_app(block), :path_info => path, :request_method => method)
|
95
|
+
end
|
96
|
+
|
97
|
+
HTTP_METHODS.each do |method|
|
98
|
+
class_eval <<-RUBY, __FILE__, __LINE__
|
99
|
+
def #{method}(*args, &block) # def get(*args, &block)
|
100
|
+
route("#{method.upcase}", *args, &block) # route("GET", *args, &block)
|
101
|
+
end # end
|
102
|
+
RUBY
|
103
|
+
end
|
104
|
+
|
105
|
+
def mount_path(path) # :nodoc:
|
106
|
+
Rack::Mount::Utils.normalize_path([settings[:prefix], settings[:version], path].join("/"))
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_app(block) # :nodoc:
|
110
|
+
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)
|
114
|
+
builder.to_app
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|