otto 0.2.1.003 → 0.3.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/CHANGES.txt +5 -0
- data/LICENSE.txt +1 -1
- data/README.md +114 -5
- data/Rakefile +3 -7
- data/VERSION.yml +2 -3
- data/example/app.rb +57 -0
- data/example/config.ru +35 -0
- data/example/public/favicon.ico +0 -0
- data/example/public/img/otto.jpg +0 -0
- data/example/routes +17 -0
- data/lib/otto.rb +2 -17
- data/otto.gemspec +7 -2
- metadata +7 -2
data/CHANGES.txt
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,112 @@
|
|
1
1
|
# Otto - 0.2
|
2
2
|
|
3
|
-
**Auto-define your rack-apps in
|
3
|
+
**Auto-define your rack-apps in plain-text.**
|
4
|
+
|
5
|
+
## Overview ##
|
6
|
+
|
7
|
+
Apps build with Otto have three, basic parts: a rackup file, a ruby file, and a routes file. If you've built a [Rack app](http://rack.rubyforge.org/) before, then you've seen a rackup file before. The ruby file is your actual app and the routes file is what Otto uses to map URI paths to a Ruby method.
|
4
8
|
|
9
|
+
A barebones app directory looks something like this:
|
5
10
|
|
11
|
+
$ cd myapp
|
12
|
+
$ ls
|
13
|
+
config.ru app.rb routes
|
14
|
+
|
15
|
+
See the examples/ directory for a working app.
|
16
|
+
|
17
|
+
|
18
|
+
### Routes ###
|
19
|
+
|
20
|
+
The routes file is just a plain-text file which defines the end points of your application. Each route has three parts:
|
21
|
+
|
22
|
+
* HTTP verb (GET, POST, PUT, DELETE or HEAD)
|
23
|
+
* URI path
|
24
|
+
* Ruby class and method to call
|
25
|
+
|
26
|
+
Here is an example:
|
27
|
+
|
28
|
+
GET / App#index
|
29
|
+
POST / App#receive_feedback
|
30
|
+
GET /redirect App#redirect
|
31
|
+
GET /robots.txt App#robots_text
|
32
|
+
GET /product/:prodid App#display_product
|
33
|
+
|
34
|
+
# You can also define these handlers when no
|
35
|
+
# route can be found or there's a server error. (optional)
|
36
|
+
GET /404 App#not_found
|
37
|
+
GET /500 App#server_error
|
38
|
+
|
39
|
+
### App ###
|
40
|
+
|
41
|
+
There is nothing special about the Ruby class. The only requirement is that the first two arguments to initialize be a Rack::Request object and a Rack::Response object. Otherwise, you can do anything you want. You're free to use any templating engine, any database mapper, etc. There is no magic.
|
42
|
+
|
43
|
+
class App
|
44
|
+
attr_reader :req, :res
|
45
|
+
|
46
|
+
# Otto creates an instance of this class for every request
|
47
|
+
# and passess the Rack::Request and Rack::Response objects.
|
48
|
+
def initialize req, res
|
49
|
+
@req, @res = req, res
|
50
|
+
end
|
51
|
+
|
52
|
+
def index
|
53
|
+
res.header['Content-Type'] = "text/html; charset=utf-8"
|
54
|
+
lines = [
|
55
|
+
'<img src="/img/otto.jpg" /><br/><br/>',
|
56
|
+
'Send feedback:<br/>',
|
57
|
+
'<form method="post"><input name="msg" /><input type="submit" /></form>',
|
58
|
+
'<a href="/product/100">A product example</a>'
|
59
|
+
]
|
60
|
+
res.body = lines.join($/)
|
61
|
+
end
|
62
|
+
|
63
|
+
def receive_feedback
|
64
|
+
res.body = req.params.inspect
|
65
|
+
end
|
66
|
+
|
67
|
+
def redirect
|
68
|
+
res.redirect '/robots.txt'
|
69
|
+
end
|
70
|
+
|
71
|
+
def robots_text
|
72
|
+
res.header['Content-Type'] = "text/plain"
|
73
|
+
rules = 'User-agent: *', 'Disallow: /private'
|
74
|
+
res.body = rules.join($/)
|
75
|
+
end
|
76
|
+
|
77
|
+
def display_product
|
78
|
+
res.header['Content-Type'] = "application/json; charset=utf-8"
|
79
|
+
prodid = req.params[:prodid]
|
80
|
+
res.body = '{"product":%s,"msg":"Hint: try another value"}' % [prodid]
|
81
|
+
end
|
82
|
+
|
83
|
+
def not_found
|
84
|
+
res.status = 404
|
85
|
+
res.body = "Item not found!"
|
86
|
+
end
|
87
|
+
|
88
|
+
def server_error
|
89
|
+
res.status = 500
|
90
|
+
res.body = "There was a server error!"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
### Rackup ###
|
95
|
+
|
96
|
+
There is also nothing special about the rackup file. It just builds a Rack app using your routes file.
|
97
|
+
|
98
|
+
require 'otto'
|
99
|
+
require 'app'
|
100
|
+
|
101
|
+
app = Otto.new("./routes")
|
102
|
+
|
103
|
+
map('/') {
|
104
|
+
run app
|
105
|
+
}
|
106
|
+
|
107
|
+
See the examples/ directory for a working app.
|
108
|
+
|
109
|
+
|
6
110
|
## Installation
|
7
111
|
|
8
112
|
Get it in one of the following ways:
|
@@ -14,23 +118,28 @@ Get it in one of the following ways:
|
|
14
118
|
You can also download via [tarball](http://github.com/delano/otto/tarball/latest) or [zip](http://github.com/delano/otto/zipball/latest).
|
15
119
|
|
16
120
|
|
17
|
-
|
18
121
|
## More Information
|
19
122
|
|
20
123
|
* [Codes](http://github.com/delano/otto)
|
21
124
|
* [RDocs](http://solutious.com/otto)
|
22
125
|
|
23
126
|
|
24
|
-
##
|
127
|
+
## In the wild ##
|
25
128
|
|
26
|
-
|
129
|
+
Services that use Otto:
|
130
|
+
|
131
|
+
* [One-Time Secret](https://onetimesecret.com/) -- A safe way to share sensitive data.
|
132
|
+
* [BlameStella](https://www.blamestella.com/) -- Web monitoring for devs and designers.
|
27
133
|
|
28
134
|
|
29
|
-
##
|
135
|
+
## Credits
|
136
|
+
|
137
|
+
* [Delano Mandelbaum](http://solutious.com)
|
30
138
|
|
31
139
|
|
32
140
|
## Related Projects
|
33
141
|
|
142
|
+
* [Sinatra](http://www.sinatrarb.com/)
|
34
143
|
|
35
144
|
## License
|
36
145
|
|
data/Rakefile
CHANGED
@@ -3,11 +3,7 @@ require "rake"
|
|
3
3
|
require "rake/clean"
|
4
4
|
require 'yaml'
|
5
5
|
|
6
|
-
|
7
|
-
require 'hanna/rdoctask'
|
8
|
-
rescue LoadError
|
9
|
-
require 'rake/rdoctask'
|
10
|
-
end
|
6
|
+
require 'hanna/rdoctask'
|
11
7
|
|
12
8
|
config = YAML.load_file("VERSION.yml")
|
13
9
|
task :default => ["build"]
|
@@ -17,7 +13,7 @@ name = "otto"
|
|
17
13
|
begin
|
18
14
|
require "jeweler"
|
19
15
|
Jeweler::Tasks.new do |gem|
|
20
|
-
gem.version = "#{config[:MAJOR]}.#{config[:MINOR]}.#{config[:PATCH]}
|
16
|
+
gem.version = "#{config[:MAJOR]}.#{config[:MINOR]}.#{config[:PATCH]}"
|
21
17
|
gem.name = "otto"
|
22
18
|
gem.rubyforge_project = gem.name
|
23
19
|
gem.summary = "Auto-define your rack-apps in plaintext."
|
@@ -35,7 +31,7 @@ end
|
|
35
31
|
|
36
32
|
|
37
33
|
Rake::RDocTask.new do |rdoc|
|
38
|
-
version = "#{config[:MAJOR]}.#{config[:MINOR]}.#{config[:PATCH]}
|
34
|
+
version = "#{config[:MAJOR]}.#{config[:MINOR]}.#{config[:PATCH]}"
|
39
35
|
rdoc.rdoc_dir = "doc"
|
40
36
|
rdoc.title = "otto #{version}"
|
41
37
|
rdoc.rdoc_files.include("README*")
|
data/VERSION.yml
CHANGED
data/example/app.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class App
|
4
|
+
|
5
|
+
# An instance of Rack::Request
|
6
|
+
attr_reader :req
|
7
|
+
# An instance of Rack::Response
|
8
|
+
attr_reader :res
|
9
|
+
|
10
|
+
# Otto creates an instance of this class for every request
|
11
|
+
# and passess the Rack::Request and Rack::Response objects.
|
12
|
+
def initialize req, res
|
13
|
+
@req, @res = req, res
|
14
|
+
res.header['Content-Type'] = "text/html; charset=utf-8"
|
15
|
+
end
|
16
|
+
|
17
|
+
def index
|
18
|
+
lines = [
|
19
|
+
'<img src="/img/otto.jpg" /><br/><br/>',
|
20
|
+
'Send feedback:<br/>',
|
21
|
+
'<form method="post"><input name="msg" /><input type="submit" /></form>',
|
22
|
+
'<a href="/product/100">A product example</a>'
|
23
|
+
]
|
24
|
+
res.body = lines.join($/)
|
25
|
+
end
|
26
|
+
|
27
|
+
def receive_feedback
|
28
|
+
res.body = req.params.inspect
|
29
|
+
end
|
30
|
+
|
31
|
+
def redirect
|
32
|
+
res.redirect '/robots.txt'
|
33
|
+
end
|
34
|
+
|
35
|
+
def robots_text
|
36
|
+
res.header['Content-Type'] = "text/plain"
|
37
|
+
rules = 'User-agent: *', 'Disallow: /private'
|
38
|
+
res.body = rules.join($/)
|
39
|
+
end
|
40
|
+
|
41
|
+
def display_product
|
42
|
+
res.header['Content-Type'] = "application/json; charset=utf-8"
|
43
|
+
prodid = req.params[:prodid]
|
44
|
+
res.body = '{"product":%s,"msg":"Hint: try another value"}' % [prodid]
|
45
|
+
end
|
46
|
+
|
47
|
+
def not_found
|
48
|
+
res.status = 404
|
49
|
+
res.body = "Item not found!"
|
50
|
+
end
|
51
|
+
|
52
|
+
def server_error
|
53
|
+
res.status = 500
|
54
|
+
res.body = "There was a server error!"
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
data/example/config.ru
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# OTTO EXAMPLE APP CONFIG - 2011-12-17
|
2
|
+
#
|
3
|
+
# Usage:
|
4
|
+
#
|
5
|
+
# $ thin -e dev -R config.ru -p 10770 start
|
6
|
+
# $ tail -f /var/log/system.log
|
7
|
+
|
8
|
+
ENV['RACK_ENV'] ||= 'prod'
|
9
|
+
ENV['APP_ROOT'] = ::File.expand_path(::File.join(::File.dirname(__FILE__)))
|
10
|
+
$:.unshift(::File.join(ENV['APP_ROOT']))
|
11
|
+
$:.unshift(::File.join(ENV['APP_ROOT'], '..', 'lib'))
|
12
|
+
|
13
|
+
require 'otto'
|
14
|
+
require 'app'
|
15
|
+
|
16
|
+
PUBLIC_DIR = "#{ENV['APP_ROOT']}/public"
|
17
|
+
APP_DIR = "#{ENV['APP_ROOT']}"
|
18
|
+
|
19
|
+
app = Otto.new("#{APP_DIR}/routes")
|
20
|
+
|
21
|
+
if Otto.env?(:dev) # DEV: Run web apps with extra logging and reloading
|
22
|
+
map('/') {
|
23
|
+
use Rack::CommonLogger
|
24
|
+
use Rack::Reloader, 0
|
25
|
+
app.option[:public] = PUBLIC_DIR
|
26
|
+
app.add_static_path '/favicon.ico'
|
27
|
+
run app
|
28
|
+
}
|
29
|
+
# Specify static paths to serve in dev-mode only
|
30
|
+
map('/etc/') { run Rack::File.new("#{PUBLIC_DIR}/etc") }
|
31
|
+
map('/img/') { run Rack::File.new("#{PUBLIC_DIR}/img") }
|
32
|
+
|
33
|
+
else # PROD: run barebones webapp
|
34
|
+
map('/') { run app }
|
35
|
+
end
|
Binary file
|
Binary file
|
data/example/routes
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# OTTO - ROUTES EXAMPLE
|
2
|
+
|
3
|
+
# Each route has three parts:
|
4
|
+
# * HTTP verb (GET, POST, PUT, DELETE or HEAD)
|
5
|
+
# * URI path
|
6
|
+
# * Ruby class and method to call
|
7
|
+
|
8
|
+
GET / App#index
|
9
|
+
POST / App#receive_feedback
|
10
|
+
GET /redirect App#redirect
|
11
|
+
GET /robots.txt App#robots_text
|
12
|
+
GET /product/:prodid App#display_product
|
13
|
+
|
14
|
+
# You can also define these handlers when no
|
15
|
+
# route can be found or there's a server error. (optional)
|
16
|
+
GET /404 App#not_found
|
17
|
+
GET /500 App#server_error
|
data/lib/otto.rb
CHANGED
@@ -13,8 +13,7 @@ class Otto
|
|
13
13
|
[@version[:MAJOR], @version[:MINOR], @version[:PATCH]].join('.')
|
14
14
|
end
|
15
15
|
def self.inspect
|
16
|
-
|
17
|
-
[@version[:MAJOR], @version[:MINOR], @version[:PATCH], @version[:BUILD]].join('.')
|
16
|
+
to_s
|
18
17
|
end
|
19
18
|
def self.load_config
|
20
19
|
require 'yaml'
|
@@ -320,9 +319,8 @@ class Otto
|
|
320
319
|
end
|
321
320
|
module RequestHelpers
|
322
321
|
def user_agent
|
323
|
-
env['HTTP_USER_AGENT']
|
322
|
+
env['HTTP_USER_AGENT']
|
324
323
|
end
|
325
|
-
|
326
324
|
# HTTP_X_FORWARDED_FOR is from the ELB (non-https only)
|
327
325
|
# and it can take the form: 74.121.244.2, 10.252.130.147
|
328
326
|
# HTTP_X_REAL_IP is from nginx
|
@@ -332,62 +330,49 @@ class Otto
|
|
332
330
|
env['HTTP_X_FORWARDED_FOR'].to_s.split(/,\s*/).first ||
|
333
331
|
env['HTTP_X_REAL_IP'] || env['REMOTE_ADDR']
|
334
332
|
end
|
335
|
-
|
336
333
|
def request_method
|
337
334
|
env['REQUEST_METHOD']
|
338
335
|
end
|
339
|
-
|
340
336
|
def current_server
|
341
337
|
[current_server_name, env['SERVER_PORT']].join(':')
|
342
338
|
end
|
343
|
-
|
344
339
|
def current_server_name
|
345
340
|
env['SERVER_NAME']
|
346
341
|
end
|
347
|
-
|
348
342
|
def http_host
|
349
343
|
env['HTTP_HOST']
|
350
344
|
end
|
351
345
|
def request_path
|
352
346
|
env['REQUEST_PATH']
|
353
347
|
end
|
354
|
-
|
355
348
|
def request_uri
|
356
349
|
env['REQUEST_URI']
|
357
350
|
end
|
358
|
-
|
359
351
|
def root_path
|
360
352
|
env['SCRIPT_NAME']
|
361
353
|
end
|
362
|
-
|
363
354
|
def absolute_suri host=current_server_name
|
364
355
|
prefix = local? ? 'http://' : 'https://'
|
365
356
|
[prefix, host, request_path].join
|
366
357
|
end
|
367
|
-
|
368
358
|
def local?
|
369
359
|
Otto.env?(:dev, :development) && client_ipaddress == '127.0.0.1'
|
370
360
|
end
|
371
|
-
|
372
361
|
def secure?
|
373
362
|
# X-Scheme is set by nginx
|
374
363
|
# X-FORWARDED-PROTO is set by elastic load balancer
|
375
364
|
(env['HTTP_X_FORWARDED_PROTO'] == 'https' || env['HTTP_X_SCHEME'] == "https")
|
376
365
|
end
|
377
|
-
|
378
366
|
def cookie name
|
379
367
|
cookies[name.to_s]
|
380
368
|
end
|
381
|
-
|
382
369
|
def cookie? name
|
383
370
|
!cookie(name).to_s.empty?
|
384
371
|
end
|
385
|
-
|
386
372
|
def current_absolute_uri
|
387
373
|
prefix = secure? && !local? ? 'https://' : 'http://'
|
388
374
|
[prefix, http_host, request_path].join
|
389
375
|
end
|
390
|
-
|
391
376
|
end
|
392
377
|
module ResponseHelpers
|
393
378
|
attr_accessor :request
|
data/otto.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "otto"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Delano Mandelbaum"]
|
12
|
-
s.date = "2011-
|
12
|
+
s.date = "2011-12-20"
|
13
13
|
s.description = "Auto-define your rack-apps in plaintext."
|
14
14
|
s.email = "delano@solutious.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -22,6 +22,11 @@ Gem::Specification.new do |s|
|
|
22
22
|
"README.md",
|
23
23
|
"Rakefile",
|
24
24
|
"VERSION.yml",
|
25
|
+
"example/app.rb",
|
26
|
+
"example/config.ru",
|
27
|
+
"example/public/favicon.ico",
|
28
|
+
"example/public/img/otto.jpg",
|
29
|
+
"example/routes",
|
25
30
|
"lib/otto.rb",
|
26
31
|
"otto.gemspec"
|
27
32
|
]
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: otto
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.3.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Delano Mandelbaum
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-12-20 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rack
|
@@ -49,6 +49,11 @@ files:
|
|
49
49
|
- README.md
|
50
50
|
- Rakefile
|
51
51
|
- VERSION.yml
|
52
|
+
- example/app.rb
|
53
|
+
- example/config.ru
|
54
|
+
- example/public/favicon.ico
|
55
|
+
- example/public/img/otto.jpg
|
56
|
+
- example/routes
|
52
57
|
- lib/otto.rb
|
53
58
|
- otto.gemspec
|
54
59
|
homepage: http://github.com/delano/otto
|