useless 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/useless.rb +11 -1
- data/lib/useless/rack.rb +18 -26
- data/lib/useless/rack/authentication/access_token.rb +39 -0
- data/lib/useless/rack/authentication/query_string.rb +19 -0
- data/lib/useless/rack/authentication/request_header.rb +19 -0
- data/lib/useless/rack/exceptions.rb +28 -0
- data/lib/useless/rack/files.rb +37 -0
- data/lib/useless/rack/fs.rb +17 -0
- data/lib/useless/rack/mongo.rb +16 -0
- data/lib/useless/version.rb +1 -1
- data/spec/spec_helper.rb +4 -3
- data/spec/useless/fs_spec.rb +1 -0
- data/spec/useless/mongo_spec.rb +1 -0
- data/spec/useless/rack/{middleware/authentication → authentication}/query_string_spec.rb +8 -7
- data/spec/useless/rack/{middleware/authentication → authentication}/request_header_spec.rb +8 -7
- data/spec/useless/rack/exceptions_spec.rb +31 -0
- data/spec/useless/rack/files_spec.rb +49 -0
- data/spec/useless/rack/fs_spec.rb +18 -0
- data/spec/useless/rack/mongo_spec.rb +18 -0
- data/spec/useless/rack_spec.rb +77 -0
- data/useless.gemspec +7 -7
- metadata +49 -50
- data/assets/DOC.html +0 -39
- data/assets/application.css +0 -134
- data/lib/useless/logger.rb +0 -48
- data/lib/useless/rack/base.rb +0 -26
- data/lib/useless/rack/base/files.rb +0 -33
- data/lib/useless/rack/middleware/assets.rb +0 -38
- data/lib/useless/rack/middleware/authentication/access_token.rb +0 -41
- data/lib/useless/rack/middleware/authentication/query_string.rb +0 -21
- data/lib/useless/rack/middleware/authentication/request_header.rb +0 -21
- data/lib/useless/rack/middleware/exceptions.rb +0 -41
- data/lib/useless/rack/middleware/fs.rb +0 -17
- data/lib/useless/rack/middleware/mongo.rb +0 -18
- data/spec/useless/logger_spec.rb +0 -87
- data/spec/useless/rack/base/files_spec.rb +0 -42
- data/spec/useless/rack/middleware/exceptions_spec.rb +0 -47
data/lib/useless/logger.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'logger'
|
2
|
-
|
3
|
-
module Useless
|
4
|
-
# Useless::Logger is a wrapper around Logger for the purposes of decorating.
|
5
|
-
class Logger
|
6
|
-
attr_accessor :request_id
|
7
|
-
|
8
|
-
def initialize(io = $stdout)
|
9
|
-
@logger = ::Logger.new(io)
|
10
|
-
end
|
11
|
-
|
12
|
-
def level=(level)
|
13
|
-
@logger.level = level
|
14
|
-
end
|
15
|
-
|
16
|
-
def level
|
17
|
-
@logger.level
|
18
|
-
end
|
19
|
-
|
20
|
-
def fatal(message)
|
21
|
-
add ::Logger::FATAL, message
|
22
|
-
end
|
23
|
-
|
24
|
-
def error(message)
|
25
|
-
add ::Logger::ERROR, message
|
26
|
-
end
|
27
|
-
|
28
|
-
def warn(message)
|
29
|
-
add ::Logger::WARN, message
|
30
|
-
end
|
31
|
-
|
32
|
-
def info(message)
|
33
|
-
add ::Logger::INFO, message
|
34
|
-
end
|
35
|
-
|
36
|
-
def debug(message)
|
37
|
-
add ::Logger::DEBUG, message
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def add(level, message)
|
43
|
-
formatted_message = message
|
44
|
-
formatted_message = "[#{@request_id}] #{formatted_message}" if @request_id
|
45
|
-
@logger.add level, formatted_message
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
data/lib/useless/rack/base.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'useless/rack/base/files'
|
2
|
-
|
3
|
-
module Useless
|
4
|
-
class Rack
|
5
|
-
# Useless::Base is the Rack endpoint for useless.io proper. It has two
|
6
|
-
# responsibilties: show documentation, and serve file from 'useless.fs'.
|
7
|
-
class Base
|
8
|
-
def call(env)
|
9
|
-
# Show platform documentation at root
|
10
|
-
app = ::Rack::URLMap.new '/' => platform_documentation,
|
11
|
-
|
12
|
-
# serve files out of /files/* via the Files Rack endpoint.
|
13
|
-
'/files/' => Files.new
|
14
|
-
|
15
|
-
app.call(env)
|
16
|
-
end
|
17
|
-
|
18
|
-
def platform_documentation
|
19
|
-
@root_app ||= begin
|
20
|
-
file = File.expand_path('../../../../assets/DOC.html', __FILE__)
|
21
|
-
[200, {'Content-Type' => 'text/html'}, file]
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
require 'useless/fs'
|
2
|
-
|
3
|
-
module Useless
|
4
|
-
class Rack
|
5
|
-
class Base
|
6
|
-
# Useless::Base::Files simply retrieves files from Useless::FS and serves
|
7
|
-
# them up.
|
8
|
-
class Files
|
9
|
-
# Provide a helper method for file URLs to keep things consistent.
|
10
|
-
def self.url_for(id)
|
11
|
-
"http://useless.io/files/#{id}"
|
12
|
-
end
|
13
|
-
|
14
|
-
def call(env)
|
15
|
-
# The file ID is everything after the initial '/' in the path
|
16
|
-
id = env['PATH_INFO'][1..-1]
|
17
|
-
|
18
|
-
# Retrieve the file from FS
|
19
|
-
file = env['useless.fs'].get(BSON::ObjectId(id))
|
20
|
-
|
21
|
-
# and serve it up with the associated content type
|
22
|
-
return [200, {'Content-Type' => file.content_type}, file.read]
|
23
|
-
|
24
|
-
# Two things can go wrong, and they'll both raise an error:
|
25
|
-
# * the specified ID is not a valid object ID
|
26
|
-
# * there is no file corresponding to the ID in the FS
|
27
|
-
rescue BSON::InvalidObjectId, Useless::FS::FileNotFound
|
28
|
-
[404, {'Content-Type' => 'text/plain'}, "File not found: #{id}"]
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
module Useless
|
2
|
-
class Rack
|
3
|
-
module Middleware
|
4
|
-
# `Assets` maps requests to files in the assets directory.
|
5
|
-
class Assets
|
6
|
-
def initialize(app)
|
7
|
-
@app = app
|
8
|
-
end
|
9
|
-
|
10
|
-
def call(env)
|
11
|
-
# If the path couldn't be a file in assets, just proxy the request.
|
12
|
-
unless env['PATH_INFO'] =~ /\/\w+/
|
13
|
-
return @app.call(env)
|
14
|
-
end
|
15
|
-
|
16
|
-
# The path is relative to the assets directory in the gem root
|
17
|
-
path = File.expand_path("../../../../assets#{env['PATH_INFO']}", __FILE__)
|
18
|
-
|
19
|
-
# If there's a corresponding file:
|
20
|
-
if File.exists?(path) and !File.directory?(path)
|
21
|
-
# Try to infer the type from the extension, resorting to 'plain'
|
22
|
-
# if we can't.
|
23
|
-
type = path[/\.(\w+)$/, 1] || 'plain'
|
24
|
-
|
25
|
-
# Read the file,
|
26
|
-
response = File.read(path)
|
27
|
-
|
28
|
-
# and serve it up
|
29
|
-
[200, {'Content-Type' => "text/#{type}"}, [response]]
|
30
|
-
else
|
31
|
-
# Otherwise, just pass the request along
|
32
|
-
@app.call(env)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
module Useless
|
2
|
-
class Rack
|
3
|
-
module Middleware
|
4
|
-
module Authentication
|
5
|
-
# The `Authentication::AccessToken` module defines the behavior for access-
|
6
|
-
# token-based authentication middleware. The middlewares are responsible
|
7
|
-
# only for providing the access token via the `#access_token_for_env`
|
8
|
-
# method.
|
9
|
-
module AccessToken
|
10
|
-
def initialize(app)
|
11
|
-
@app = app
|
12
|
-
end
|
13
|
-
|
14
|
-
def call(env)
|
15
|
-
# If we don't already have a user set in the environment,
|
16
|
-
unless env['useless.user']
|
17
|
-
# check to see if an access token was specified.
|
18
|
-
if access_token = access_token_for_env(env)
|
19
|
-
# If so, and a corresponding user can be found,
|
20
|
-
if user = env['useless.mongo']['users'].find_one('access_token' => access_token)
|
21
|
-
# set 'useless.user' in the environment.
|
22
|
-
env['useless.user'] = user
|
23
|
-
else
|
24
|
-
# Otherwise, return a 401 Unauthorized.
|
25
|
-
return [401, {'Content-Type' => 'text/plain'}, ["Invalid access token: #{access_token}"]]
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
@app.call(env)
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def access_token_for_env(env)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'useless/rack/middleware/authentication/access_token'
|
2
|
-
|
3
|
-
module Useless
|
4
|
-
class Rack
|
5
|
-
module Middleware
|
6
|
-
module Authentication
|
7
|
-
# The `Authentication::QueryString` middleware attempt to retrieve the
|
8
|
-
# access token from the query string.
|
9
|
-
class QueryString
|
10
|
-
include AccessToken
|
11
|
-
|
12
|
-
private
|
13
|
-
|
14
|
-
def access_token_for_env(env)
|
15
|
-
::Rack::Utils.parse_query(env['QUERY_STRING'])['access_token']
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'useless/rack/middleware/authentication/access_token'
|
2
|
-
|
3
|
-
module Useless
|
4
|
-
class Rack
|
5
|
-
module Middleware
|
6
|
-
module Authentication
|
7
|
-
# The `Authentication::RequestHeader` middleware attempts to retrieve the
|
8
|
-
# access token from the `Authorization` request header.
|
9
|
-
class RequestHeader
|
10
|
-
include AccessToken
|
11
|
-
|
12
|
-
private
|
13
|
-
|
14
|
-
def access_token_for_env(env)
|
15
|
-
env['HTTP_AUTHORIZATION']
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
module Useless
|
2
|
-
class Rack
|
3
|
-
module Middleware
|
4
|
-
# `Exceptions` is a Rack middleware that handles any exceptions raised by
|
5
|
-
# it's app. It has two responsibilities:
|
6
|
-
# 1. log the exception trace, if a logger is available, and
|
7
|
-
# 2. return a 500 response with the appropriate message
|
8
|
-
class Exceptions
|
9
|
-
def initialize(app)
|
10
|
-
@app = app
|
11
|
-
end
|
12
|
-
|
13
|
-
def call(env)
|
14
|
-
@app.call(env)
|
15
|
-
rescue => exception
|
16
|
-
# First, format the exception trace
|
17
|
-
trace = exception.message + "\n" + exception.backtrace.join("\n")
|
18
|
-
|
19
|
-
# If a logger is available,
|
20
|
-
if env['useless.logger']
|
21
|
-
# log the trace as fatal.
|
22
|
-
env['useless.logger'].fatal trace
|
23
|
-
end
|
24
|
-
|
25
|
-
# Next, if the request is authenticated by an admin or we are in development or test,
|
26
|
-
if (env['useless.user'] and env['useless.user']['admin']) or
|
27
|
-
ENV['RACK_ENV'] == 'development'
|
28
|
-
# we will return the trace;
|
29
|
-
message = trace
|
30
|
-
else
|
31
|
-
# otherwise, return a generic message.
|
32
|
-
message = 'An internal server error occurred. Please try again later.'
|
33
|
-
end
|
34
|
-
|
35
|
-
# Finally, return the 500 error.
|
36
|
-
[500, {'Content-Type' => 'text/plain'}, [message]]
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Useless
|
2
|
-
class Rack
|
3
|
-
module Middleware
|
4
|
-
# `FS` simply adds the Useless.fs instance to env as 'useless.fs'.
|
5
|
-
class FS
|
6
|
-
def initialize(app)
|
7
|
-
@app = app
|
8
|
-
end
|
9
|
-
|
10
|
-
def call(env)
|
11
|
-
env['useless.fs'] = Useless::Rack.fs
|
12
|
-
@app.call(env)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module Useless
|
2
|
-
class Rack
|
3
|
-
module Middleware
|
4
|
-
# `Useless::Mongo` simply adds the Useless.mongo instance to env as
|
5
|
-
# 'useless.mongo'.
|
6
|
-
class Mongo
|
7
|
-
def initialize(app)
|
8
|
-
@app = app
|
9
|
-
end
|
10
|
-
|
11
|
-
def call(env)
|
12
|
-
env['useless.mongo'] = Useless::Rack.mongo
|
13
|
-
@app.call(env)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
data/spec/useless/logger_spec.rb
DELETED
@@ -1,87 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
-
require 'useless/logger'
|
3
|
-
|
4
|
-
describe Useless::Logger do
|
5
|
-
describe '#fatal' do
|
6
|
-
it 'should emit a fatal message' do
|
7
|
-
io = StringIO.new
|
8
|
-
logger = Useless::Logger.new(io)
|
9
|
-
logger.fatal 'Oh noes!'
|
10
|
-
|
11
|
-
io.rewind; message = io.read
|
12
|
-
message.should =~ /FATAL/
|
13
|
-
message.should =~ /Oh noes!$/
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe '#error' do
|
18
|
-
it 'should emit an error message' do
|
19
|
-
io = StringIO.new
|
20
|
-
logger = Useless::Logger.new(io)
|
21
|
-
logger.error 'Wrong!'
|
22
|
-
|
23
|
-
io.rewind; message = io.read
|
24
|
-
message.should =~ /ERROR/
|
25
|
-
message.should =~ /Wrong!$/
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
describe '#warn' do
|
30
|
-
it 'should emit a warn message' do
|
31
|
-
io = StringIO.new
|
32
|
-
logger = Useless::Logger.new(io)
|
33
|
-
logger.warn 'Watchout!'
|
34
|
-
|
35
|
-
io.rewind; message = io.read
|
36
|
-
message.should =~ /WARN/
|
37
|
-
message.should =~ /Watchout!$/
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
describe '#info' do
|
42
|
-
it 'should emit an info message' do
|
43
|
-
io = StringIO.new
|
44
|
-
logger = Useless::Logger.new(io)
|
45
|
-
logger.info 'Such and such'
|
46
|
-
|
47
|
-
io.rewind; message = io.read
|
48
|
-
message.should =~ /INFO/
|
49
|
-
message.should =~ /Such and such$/
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
describe '#debug' do
|
54
|
-
it 'should emit a debug message' do
|
55
|
-
io = StringIO.new
|
56
|
-
logger = Useless::Logger.new(io)
|
57
|
-
logger.debug '101011'
|
58
|
-
|
59
|
-
io.rewind; message = io.read
|
60
|
-
message.should =~ /DEBUG/
|
61
|
-
message.should =~ /101011$/
|
62
|
-
end
|
63
|
-
|
64
|
-
it 'should emit nothing if the level is too high' do
|
65
|
-
io = StringIO.new
|
66
|
-
logger = Useless::Logger.new(io)
|
67
|
-
logger.level = ::Logger::INFO
|
68
|
-
logger.debug '101011'
|
69
|
-
|
70
|
-
io.rewind; message = io.read
|
71
|
-
message.should == ''
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
describe '#request_id' do
|
76
|
-
it 'should append the specified ID to all messages' do
|
77
|
-
io = StringIO.new
|
78
|
-
logger = Useless::Logger.new(io)
|
79
|
-
logger.request_id = 'abc123'
|
80
|
-
logger.info 'Yadda'
|
81
|
-
|
82
|
-
io.rewind; message = io.read
|
83
|
-
message.should =~ /abc123/
|
84
|
-
message.should =~ /Yadda$/
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/../../../spec_helper'
|
2
|
-
require 'useless/rack/base/files'
|
3
|
-
|
4
|
-
describe 'useless.io/files' do
|
5
|
-
include Rack::Test::Methods
|
6
|
-
|
7
|
-
def app
|
8
|
-
Useless::Rack.new
|
9
|
-
end
|
10
|
-
|
11
|
-
describe 'GET /files/:id' do
|
12
|
-
it 'should return a file if one exists for the specified ID' do
|
13
|
-
file = File.open(asset_path('muffin-milk.jpg'))
|
14
|
-
id = Useless::Rack.fs.put(file)
|
15
|
-
|
16
|
-
get Useless::Rack::Base::Files.url_for(id)
|
17
|
-
last_response.should be_ok
|
18
|
-
|
19
|
-
# See spec/useless/fs_spec.rb
|
20
|
-
file.rewind
|
21
|
-
blob = file.read
|
22
|
-
blob.force_encoding('BINARY')
|
23
|
-
blob.should == last_response.body
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'should return a 404 if the file does not exits' do
|
27
|
-
get Useless::Rack::Base::Files.url_for(BSON::ObjectId.new)
|
28
|
-
last_response.should be_not_found
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'should return a 404 if the specified ID is invalid' do
|
32
|
-
get Useless::Rack::Base::Files.url_for('invalid-id')
|
33
|
-
last_response.should be_not_found
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
describe '.url_for' do
|
38
|
-
it 'should return the appropriate URL for the specified file ID' do
|
39
|
-
Useless::Rack::Base::Files.url_for('abc123').should == 'http://useless.io/files/abc123'
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|