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.
@@ -1,5 +1,10 @@
1
1
  OTTO, CHANGES
2
2
 
3
+ #### 0.3.0 (2011-12-17) ###############################
4
+
5
+ * ADDED: Example app, better docs in readme
6
+ * CHANGE: No default value for user agent
7
+
3
8
  #### 0.2.1 (2011-07-07) ###############################
4
9
 
5
10
  * ADDED: Otto#add_static_path
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 Delano Mandelbaum
1
+ Copyright (c) 2011,2012 Delano Mandelbaum
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,8 +1,112 @@
1
1
  # Otto - 0.2
2
2
 
3
- **Auto-define your rack-apps in plaintext.**
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
- ## Credits
127
+ ## In the wild ##
25
128
 
26
- * [Delano Mandelbaum](http://solutious.com)
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
- ## Thanks
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
- begin
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]}.#{config[:BUILD]}"
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]}.#{config[:BUILD]}"
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*")
@@ -1,4 +1,3 @@
1
1
  :MAJOR: 0
2
- :MINOR: 2
3
- :PATCH: 1
4
- :BUILD: '003'
2
+ :MINOR: 3
3
+ :PATCH: 0
@@ -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
@@ -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
@@ -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
@@ -13,8 +13,7 @@ class Otto
13
13
  [@version[:MAJOR], @version[:MINOR], @version[:PATCH]].join('.')
14
14
  end
15
15
  def self.inspect
16
- load_config
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'] || '[no-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
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "otto"
8
- s.version = "0.2.1.003"
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-10-19"
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.2.1.003
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-10-19 00:00:00 Z
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