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 +1 -0
- data/Gemfile.lock +1 -1
- data/README.rdoc +98 -0
- data/Rakefile +7 -0
- data/examples/custom_format.rb +32 -0
- data/examples/formats.rb +19 -0
- data/lib/rack/api.rb +8 -35
- data/lib/rack/api/app.rb +9 -0
- data/lib/rack/api/middleware.rb +7 -0
- data/lib/rack/api/middleware/format.rb +23 -0
- data/lib/rack/api/runner.rb +118 -11
- data/lib/rack/api/version.rb +1 -1
- data/spec/rack-api/basic_auth_spec.rb +65 -10
- data/spec/rack-api/format_spec.rb +11 -1
- data/spec/rack-api/helpers_spec.rb +33 -0
- data/spec/rack-api/runner_spec.rb +4 -9
- data/spec/rack-api/settings_spec.rb +50 -0
- metadata +10 -2
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/README.rdoc
CHANGED
|
@@ -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
|
data/examples/formats.rb
ADDED
|
@@ -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
|
data/lib/rack/api.rb
CHANGED
|
@@ -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!
|
data/lib/rack/api/app.rb
CHANGED
|
@@ -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,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
|
data/lib/rack/api/runner.rb
CHANGED
|
@@ -7,22 +7,76 @@ module Rack
|
|
|
7
7
|
|
|
8
8
|
def initialize
|
|
9
9
|
@settings = {
|
|
10
|
-
:
|
|
11
|
-
:
|
|
12
|
-
:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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([
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
data/lib/rack/api/version.rb
CHANGED
|
@@ -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 == "
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
21
|
-
|
|
45
|
+
last_response.status.should == 200
|
|
46
|
+
JSON.load(last_response.body).should == {"success" => true}
|
|
47
|
+
end
|
|
22
48
|
end
|
|
23
49
|
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
28
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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-
|
|
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
|