useless 0.1.2 → 0.2.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/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
|