otto 0.2.0.001
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 +7 -0
- data/LICENSE.txt +19 -0
- data/README.md +37 -0
- data/Rakefile +65 -0
- data/VERSION.yml +4 -0
- data/lib/otto.rb +396 -0
- data/otto.gemspec +50 -0
- metadata +84 -0
data/CHANGES.txt
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2011 Delano Mandelbaum
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
|
11
|
+
all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Otto - 0.2
|
|
2
|
+
|
|
3
|
+
**Auto-define your rack-apps in plaintext.**
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
Get it in one of the following ways:
|
|
9
|
+
|
|
10
|
+
$ gem install otto
|
|
11
|
+
$ sudo gem install otto
|
|
12
|
+
$ git clone git://github.com/delano/otto.git
|
|
13
|
+
|
|
14
|
+
You can also download via [tarball](http://github.com/delano/otto/tarball/latest) or [zip](http://github.com/delano/otto/zipball/latest).
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## More Information
|
|
19
|
+
|
|
20
|
+
* [Codes](http://github.com/delano/otto)
|
|
21
|
+
* [RDocs](http://solutious.com/otto)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Credits
|
|
25
|
+
|
|
26
|
+
* [Delano Mandelbaum](http://solutious.com)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## Thanks
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## Related Projects
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## License
|
|
36
|
+
|
|
37
|
+
See LICENSE.txt
|
data/Rakefile
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require "rubygems"
|
|
2
|
+
require "rake"
|
|
3
|
+
require "rake/clean"
|
|
4
|
+
require 'yaml'
|
|
5
|
+
|
|
6
|
+
begin
|
|
7
|
+
require 'hanna/rdoctask'
|
|
8
|
+
rescue LoadError
|
|
9
|
+
require 'rake/rdoctask'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
config = YAML.load_file("VERSION.yml")
|
|
13
|
+
task :default => ["build"]
|
|
14
|
+
CLEAN.include [ 'pkg', 'doc' ]
|
|
15
|
+
name = "otto"
|
|
16
|
+
|
|
17
|
+
begin
|
|
18
|
+
require "jeweler"
|
|
19
|
+
Jeweler::Tasks.new do |gem|
|
|
20
|
+
gem.version = "#{config[:MAJOR]}.#{config[:MINOR]}.#{config[:PATCH]}.#{config[:BUILD]}"
|
|
21
|
+
gem.name = "otto"
|
|
22
|
+
gem.rubyforge_project = gem.name
|
|
23
|
+
gem.summary = "Auto-define your rack-apps in plaintext."
|
|
24
|
+
gem.description = "Auto-define your rack-apps in plaintext."
|
|
25
|
+
gem.email = "delano@solutious.com"
|
|
26
|
+
gem.homepage = "http://github.com/delano/otto"
|
|
27
|
+
gem.authors = ["Delano Mandelbaum"]
|
|
28
|
+
gem.add_dependency('rack', '>= 1.2.1')
|
|
29
|
+
gem.add_dependency('addressable', '= 2.2.4')
|
|
30
|
+
end
|
|
31
|
+
Jeweler::GemcutterTasks.new
|
|
32
|
+
rescue LoadError
|
|
33
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
Rake::RDocTask.new do |rdoc|
|
|
38
|
+
version = "#{config[:MAJOR]}.#{config[:MINOR]}.#{config[:PATCH]}.#{config[:BUILD]}"
|
|
39
|
+
rdoc.rdoc_dir = "doc"
|
|
40
|
+
rdoc.title = "otto #{version}"
|
|
41
|
+
rdoc.rdoc_files.include("README*")
|
|
42
|
+
rdoc.rdoc_files.include("LICENSE.txt")
|
|
43
|
+
rdoc.rdoc_files.include("bin/*.rb")
|
|
44
|
+
rdoc.rdoc_files.include("lib/**/*.rb")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# Rubyforge Release / Publish Tasks ==================================
|
|
49
|
+
|
|
50
|
+
#about 'Publish website to rubyforge'
|
|
51
|
+
task 'publish:rdoc' => 'doc/index.html' do
|
|
52
|
+
sh "scp -rp doc/* rubyforge.org:/var/www/gforge-projects/#{name}/"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
#about 'Public release to rubyforge'
|
|
56
|
+
task 'publish:gem' => [:package] do |t|
|
|
57
|
+
sh <<-end
|
|
58
|
+
rubyforge add_release -o Any -a CHANGES.txt -f -n README.md #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.gem &&
|
|
59
|
+
rubyforge add_file -o Any -a CHANGES.txt -f -n README.md #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.tgz
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
data/VERSION.yml
ADDED
data/lib/otto.rb
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
|
|
2
|
+
require 'rack/request'
|
|
3
|
+
require 'rack/response'
|
|
4
|
+
require 'rack/utils'
|
|
5
|
+
require 'addressable/uri'
|
|
6
|
+
|
|
7
|
+
class Otto
|
|
8
|
+
LIB_HOME = File.expand_path File.dirname(__FILE__) unless defined?(Otto::LIB_HOME)
|
|
9
|
+
|
|
10
|
+
module VERSION
|
|
11
|
+
def self.to_s
|
|
12
|
+
load_config
|
|
13
|
+
[@version[:MAJOR], @version[:MINOR], @version[:PATCH]].join('.')
|
|
14
|
+
end
|
|
15
|
+
def self.inspect
|
|
16
|
+
load_config
|
|
17
|
+
[@version[:MAJOR], @version[:MINOR], @version[:PATCH], @version[:BUILD]].join('.')
|
|
18
|
+
end
|
|
19
|
+
def self.load_config
|
|
20
|
+
require 'yaml'
|
|
21
|
+
@version ||= YAML.load_file(File.join(LIB_HOME, '..', 'VERSION.yml'))
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class Otto
|
|
27
|
+
attr_reader :routes, :routes_literal, :routes_static, :route_definitions
|
|
28
|
+
attr_reader :option, :static_route
|
|
29
|
+
attr_accessor :not_found, :server_error
|
|
30
|
+
def initialize path=nil, opts={}
|
|
31
|
+
@routes_static = { :GET => {} }
|
|
32
|
+
@routes = { :GET => [] }
|
|
33
|
+
@routes_literal = { :GET => {} }
|
|
34
|
+
@route_definitions = {}
|
|
35
|
+
@option = opts.merge({
|
|
36
|
+
:public => nil
|
|
37
|
+
})
|
|
38
|
+
load(path) unless path.nil?
|
|
39
|
+
super()
|
|
40
|
+
end
|
|
41
|
+
alias_method :options, :option
|
|
42
|
+
def load path
|
|
43
|
+
path = File.expand_path(path)
|
|
44
|
+
raise ArgumentError, "Bad path: #{path}" unless File.exists?(path)
|
|
45
|
+
raw = File.readlines(path).select { |line| line =~ /^\w/ }.collect { |line| line.strip.split(/\s+/) }
|
|
46
|
+
raw.each { |entry|
|
|
47
|
+
begin
|
|
48
|
+
verb, path, definition = *entry
|
|
49
|
+
route = Otto::Route.new verb, path, definition
|
|
50
|
+
route.otto = self
|
|
51
|
+
path_clean = path.gsub /\/$/, ''
|
|
52
|
+
@route_definitions[route.definition] = route
|
|
53
|
+
STDERR.puts "route: #{route.pattern}"
|
|
54
|
+
@routes[route.verb] ||= []
|
|
55
|
+
@routes[route.verb] << route
|
|
56
|
+
@routes_literal[route.verb] ||= {}
|
|
57
|
+
@routes_literal[route.verb][path_clean] = route
|
|
58
|
+
rescue => ex
|
|
59
|
+
STDERR.puts "Bad route in #{path}: #{entry}"
|
|
60
|
+
end
|
|
61
|
+
}
|
|
62
|
+
self
|
|
63
|
+
end
|
|
64
|
+
def safe_file?(path)
|
|
65
|
+
globstr = File.join(option[:public], '*')
|
|
66
|
+
pathstr = File.join(option[:public], path)
|
|
67
|
+
File.fnmatch?(globstr, pathstr) && File.owned?(pathstr) && !File.directory?(pathstr)
|
|
68
|
+
end
|
|
69
|
+
def call env
|
|
70
|
+
if option[:public] && File.owned?(option[:public])
|
|
71
|
+
@static_route ||= Rack::File.new(option[:public])
|
|
72
|
+
end
|
|
73
|
+
path_info = Rack::Utils.unescape(env['PATH_INFO'])
|
|
74
|
+
path_info = '/' if path_info.to_s.empty?
|
|
75
|
+
path_info_clean = path_info.gsub /\/$/, ''
|
|
76
|
+
base_path = File.split(path_info).first
|
|
77
|
+
# Files in the root directory can refer to themselves
|
|
78
|
+
base_path = path_info if base_path == '/'
|
|
79
|
+
http_verb = env['REQUEST_METHOD'].upcase.to_sym
|
|
80
|
+
literal_routes = routes_literal[http_verb] || {}
|
|
81
|
+
literal_routes.merge! routes_literal[:GET] if http_verb == :HEAD
|
|
82
|
+
if static_route && http_verb == :GET && routes_static[:GET].member?(base_path)
|
|
83
|
+
#STDERR.puts " request: #{path_info} (static)"
|
|
84
|
+
static_route.call(env)
|
|
85
|
+
elsif literal_routes.has_key?(path_info_clean)
|
|
86
|
+
route = literal_routes[path_info_clean]
|
|
87
|
+
#STDERR.puts " request: #{http_verb} #{path_info} (literal route: #{route.verb} #{route.path})"
|
|
88
|
+
route.call(env)
|
|
89
|
+
elsif static_route && http_verb == :GET && safe_file?(path_info)
|
|
90
|
+
static_path = File.join(option[:public], base_path)
|
|
91
|
+
STDERR.puts " new static route: #{base_path} (#{path_info})"
|
|
92
|
+
routes_static[:GET][base_path] = base_path
|
|
93
|
+
static_route.call(env)
|
|
94
|
+
else
|
|
95
|
+
extra_params = {}
|
|
96
|
+
found_route = nil
|
|
97
|
+
valid_routes = routes[http_verb] || []
|
|
98
|
+
valid_routes.push *routes[:GET] if http_verb == :HEAD
|
|
99
|
+
valid_routes.each { |route|
|
|
100
|
+
#STDERR.puts " request: #{http_verb} #{path_info} (trying route: #{route.verb} #{route.pattern})"
|
|
101
|
+
if (match = route.pattern.match(path_info))
|
|
102
|
+
values = match.captures.to_a
|
|
103
|
+
# The first capture returned is the entire matched string b/c
|
|
104
|
+
# we wrapped the entire regex in parens. We don't need it to
|
|
105
|
+
# the full match.
|
|
106
|
+
full_match = values.shift
|
|
107
|
+
extra_params =
|
|
108
|
+
if route.keys.any?
|
|
109
|
+
route.keys.zip(values).inject({}) do |hash,(k,v)|
|
|
110
|
+
if k == 'splat'
|
|
111
|
+
(hash[k] ||= []) << v
|
|
112
|
+
else
|
|
113
|
+
hash[k] = v
|
|
114
|
+
end
|
|
115
|
+
hash
|
|
116
|
+
end
|
|
117
|
+
elsif values.any?
|
|
118
|
+
{'captures' => values}
|
|
119
|
+
else
|
|
120
|
+
{}
|
|
121
|
+
end
|
|
122
|
+
found_route = route
|
|
123
|
+
break
|
|
124
|
+
end
|
|
125
|
+
}
|
|
126
|
+
found_route ||= literal_routes['/404']
|
|
127
|
+
if found_route
|
|
128
|
+
found_route.call env, extra_params
|
|
129
|
+
else
|
|
130
|
+
@not_found || Otto::Static.not_found
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
rescue => ex
|
|
134
|
+
STDERR.puts ex.message, ex.backtrace
|
|
135
|
+
if found_route = literal_routes['/500']
|
|
136
|
+
found_route.call env
|
|
137
|
+
else
|
|
138
|
+
@server_error || Otto::Static.server_error
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# Return the URI path for the given +route_definition+
|
|
144
|
+
# e.g.
|
|
145
|
+
#
|
|
146
|
+
# Otto.default.path 'YourClass.somemethod' #=> /some/path
|
|
147
|
+
#
|
|
148
|
+
def uri route_definition, params={}
|
|
149
|
+
#raise RuntimeError, "Not working"
|
|
150
|
+
route = @route_definitions[route_definition]
|
|
151
|
+
unless route.nil?
|
|
152
|
+
local_params = params.clone
|
|
153
|
+
local_path = route.path.clone
|
|
154
|
+
if objid = local_params.delete(:id) || local_params.delete('id')
|
|
155
|
+
local_path.gsub! /\*/, objid
|
|
156
|
+
end
|
|
157
|
+
local_params.each_pair { |k,v|
|
|
158
|
+
next unless local_path.match(":#{k}")
|
|
159
|
+
local_path.gsub!(":#{k}", local_params.delete(k))
|
|
160
|
+
}
|
|
161
|
+
uri = Addressable::URI.new
|
|
162
|
+
uri.path = local_path
|
|
163
|
+
uri.query_values = local_params
|
|
164
|
+
uri.to_s
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
module Static
|
|
169
|
+
extend self
|
|
170
|
+
def server_error
|
|
171
|
+
[500, {'Content-Type'=>'text/plain'}, ["Server error"]]
|
|
172
|
+
end
|
|
173
|
+
def not_found
|
|
174
|
+
[404, {'Content-Type'=>'text/plain'}, ["Not Found"]]
|
|
175
|
+
end
|
|
176
|
+
# Enable string or symbol key access to the nested params hash.
|
|
177
|
+
def indifferent_params(params)
|
|
178
|
+
if params.is_a?(Hash)
|
|
179
|
+
params = indifferent_hash.merge(params)
|
|
180
|
+
params.each do |key, value|
|
|
181
|
+
next unless value.is_a?(Hash) || value.is_a?(Array)
|
|
182
|
+
params[key] = indifferent_params(value)
|
|
183
|
+
end
|
|
184
|
+
elsif params.is_a?(Array)
|
|
185
|
+
params.collect! do |value|
|
|
186
|
+
if value.is_a?(Hash) || value.is_a?(Array)
|
|
187
|
+
indifferent_params(value)
|
|
188
|
+
else
|
|
189
|
+
value
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
# Creates a Hash with indifferent access.
|
|
195
|
+
def indifferent_hash
|
|
196
|
+
Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
#
|
|
200
|
+
# e.g.
|
|
201
|
+
#
|
|
202
|
+
# GET /uri/path YourApp.method
|
|
203
|
+
# GET /uri/path2 YourApp#method
|
|
204
|
+
#
|
|
205
|
+
class Route
|
|
206
|
+
module ClassMethods
|
|
207
|
+
attr_accessor :otto
|
|
208
|
+
end
|
|
209
|
+
attr_reader :verb, :path, :pattern, :method, :klass, :name, :definition, :keys, :kind
|
|
210
|
+
attr_accessor :otto
|
|
211
|
+
def initialize verb, path, definition
|
|
212
|
+
@verb, @path, @definition = verb.to_s.upcase.to_sym, path, definition
|
|
213
|
+
@pattern, @keys = *compile(@path)
|
|
214
|
+
if !@definition.index('.').nil?
|
|
215
|
+
@klass, @name = @definition.split('.')
|
|
216
|
+
@kind = :class
|
|
217
|
+
elsif !@definition.index('#').nil?
|
|
218
|
+
@klass, @name = @definition.split('#')
|
|
219
|
+
@kind = :instance
|
|
220
|
+
else
|
|
221
|
+
raise ArgumentError, "Bad definition: #{@definition}"
|
|
222
|
+
end
|
|
223
|
+
@klass = eval(@klass)
|
|
224
|
+
#@method = eval(@klass).method(@name)
|
|
225
|
+
end
|
|
226
|
+
def pattern_regexp
|
|
227
|
+
Regexp.new(@path.gsub(/\/\*/, '/.+'))
|
|
228
|
+
end
|
|
229
|
+
def call(env, extra_params={})
|
|
230
|
+
extra_params ||= {}
|
|
231
|
+
req = Rack::Request.new(env)
|
|
232
|
+
res = Rack::Response.new
|
|
233
|
+
req.extend Otto::RequestHelpers
|
|
234
|
+
res.extend Otto::ResponseHelpers
|
|
235
|
+
res.request = req
|
|
236
|
+
req.params.merge! extra_params
|
|
237
|
+
req.params.replace Otto::Static.indifferent_params(req.params)
|
|
238
|
+
klass.extend Otto::Route::ClassMethods
|
|
239
|
+
klass.otto = self.otto
|
|
240
|
+
case kind
|
|
241
|
+
when :instance
|
|
242
|
+
inst = klass.new req, res
|
|
243
|
+
inst.send(name)
|
|
244
|
+
when :class
|
|
245
|
+
klass.send(name, req, res)
|
|
246
|
+
else
|
|
247
|
+
raise RuntimeError, "Unsupported kind for #{@definition}: #{kind}"
|
|
248
|
+
end
|
|
249
|
+
res.body = [res.body] unless res.body.respond_to?(:each)
|
|
250
|
+
res.finish
|
|
251
|
+
end
|
|
252
|
+
# Brazenly borrowed from Sinatra::Base:
|
|
253
|
+
# https://github.com/sinatra/sinatra/blob/v1.2.6/lib/sinatra/base.rb#L1156
|
|
254
|
+
def compile(path)
|
|
255
|
+
keys = []
|
|
256
|
+
if path.respond_to? :to_str
|
|
257
|
+
special_chars = %w{. + ( ) $}
|
|
258
|
+
pattern =
|
|
259
|
+
path.to_str.gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match|
|
|
260
|
+
case match
|
|
261
|
+
when "*"
|
|
262
|
+
keys << 'splat'
|
|
263
|
+
"(.*?)"
|
|
264
|
+
when *special_chars
|
|
265
|
+
Regexp.escape(match)
|
|
266
|
+
else
|
|
267
|
+
keys << $2[1..-1]
|
|
268
|
+
"([^/?#]+)"
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
# Wrap the regex in parens so the regex works properly.
|
|
272
|
+
# They can fail when there's an | for example (matching only the last one).
|
|
273
|
+
# Note: this means we also need to remove the first matched value.
|
|
274
|
+
[/\A(#{pattern})\z/, keys]
|
|
275
|
+
elsif path.respond_to?(:keys) && path.respond_to?(:match)
|
|
276
|
+
[path, path.keys]
|
|
277
|
+
elsif path.respond_to?(:names) && path.respond_to?(:match)
|
|
278
|
+
[path, path.names]
|
|
279
|
+
elsif path.respond_to? :match
|
|
280
|
+
[path, keys]
|
|
281
|
+
else
|
|
282
|
+
raise TypeError, path
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
class << self
|
|
287
|
+
def default
|
|
288
|
+
@default ||= Otto.new
|
|
289
|
+
@default
|
|
290
|
+
end
|
|
291
|
+
def load path
|
|
292
|
+
default.load path
|
|
293
|
+
end
|
|
294
|
+
def path definition, params={}
|
|
295
|
+
default.path definition, params
|
|
296
|
+
end
|
|
297
|
+
def routes
|
|
298
|
+
default.routes
|
|
299
|
+
end
|
|
300
|
+
def env? *guesses
|
|
301
|
+
!guesses.flatten.select { |n| ENV['RACK_ENV'].to_s == n.to_s }.empty?
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
module RequestHelpers
|
|
305
|
+
def user_agent
|
|
306
|
+
env['HTTP_USER_AGENT'] || '[no-user-agent]'
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# HTTP_X_FORWARDED_FOR is from the ELB (non-https only)
|
|
310
|
+
# and it can take the form: 74.121.244.2, 10.252.130.147
|
|
311
|
+
# HTTP_X_REAL_IP is from nginx
|
|
312
|
+
# REMOTE_ADDR is from thin
|
|
313
|
+
# There's no way to get the client IP address in HTTPS.
|
|
314
|
+
def client_ipaddress
|
|
315
|
+
env['HTTP_X_FORWARDED_FOR'].to_s.split(/,\s*/).first ||
|
|
316
|
+
env['HTTP_X_REAL_IP'] || env['REMOTE_ADDR']
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def request_method
|
|
320
|
+
env['REQUEST_METHOD']
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def current_server
|
|
324
|
+
[current_server_name, env['SERVER_PORT']].join(':')
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def current_server_name
|
|
328
|
+
env['SERVER_NAME']
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def http_host
|
|
332
|
+
env['HTTP_HOST']
|
|
333
|
+
end
|
|
334
|
+
def request_path
|
|
335
|
+
env['REQUEST_PATH']
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def request_uri
|
|
339
|
+
env['REQUEST_URI']
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def root_path
|
|
343
|
+
env['SCRIPT_NAME']
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def absolute_suri host=current_server_name
|
|
347
|
+
prefix = local? ? 'http://' : 'https://'
|
|
348
|
+
[prefix, host, request_path].join
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def local?
|
|
352
|
+
Otto.env?(:dev, :development) && client_ipaddress == '127.0.0.1'
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def secure?
|
|
356
|
+
# X-Scheme is set by nginx
|
|
357
|
+
# X-FORWARDED-PROTO is set by elastic load balancer
|
|
358
|
+
(env['HTTP_X_FORWARDED_PROTO'] == 'https' || env['HTTP_X_SCHEME'] == "https")
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def cookie name
|
|
362
|
+
cookies[name.to_s]
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def cookie? name
|
|
366
|
+
!cookie(name).to_s.empty?
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def current_absolute_uri
|
|
370
|
+
prefix = secure? && !local? ? 'https://' : 'http://'
|
|
371
|
+
[prefix, http_host, request_path].join
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
end
|
|
375
|
+
module ResponseHelpers
|
|
376
|
+
attr_accessor :request
|
|
377
|
+
def send_secure_cookie name, value, ttl
|
|
378
|
+
send_cookie name, value, ttl, true
|
|
379
|
+
end
|
|
380
|
+
def send_cookie name, value, ttl, secure=true
|
|
381
|
+
secure = false if request.local?
|
|
382
|
+
opts = {
|
|
383
|
+
:value => value,
|
|
384
|
+
:path => '/',
|
|
385
|
+
:expires => (Time.now + ttl + 10).utc,
|
|
386
|
+
:secure => secure
|
|
387
|
+
}
|
|
388
|
+
opts[:domain] = request.env['SERVER_NAME']
|
|
389
|
+
#pp [:cookie, name, opts]
|
|
390
|
+
set_cookie name, opts
|
|
391
|
+
end
|
|
392
|
+
def delete_cookie name
|
|
393
|
+
send_cookie name, nil, -1.day
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
end
|
data/otto.gemspec
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Generated by jeweler
|
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
|
4
|
+
# -*- encoding: utf-8 -*-
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |s|
|
|
7
|
+
s.name = %q{otto}
|
|
8
|
+
s.version = "0.2.0.001"
|
|
9
|
+
|
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
11
|
+
s.authors = ["Delano Mandelbaum"]
|
|
12
|
+
s.date = %q{2011-07-07}
|
|
13
|
+
s.description = %q{Auto-define your rack-apps in plaintext.}
|
|
14
|
+
s.email = %q{delano@solutious.com}
|
|
15
|
+
s.extra_rdoc_files = [
|
|
16
|
+
"LICENSE.txt",
|
|
17
|
+
"README.md"
|
|
18
|
+
]
|
|
19
|
+
s.files = [
|
|
20
|
+
"CHANGES.txt",
|
|
21
|
+
"LICENSE.txt",
|
|
22
|
+
"README.md",
|
|
23
|
+
"Rakefile",
|
|
24
|
+
"VERSION.yml",
|
|
25
|
+
"lib/otto.rb",
|
|
26
|
+
"otto.gemspec"
|
|
27
|
+
]
|
|
28
|
+
s.homepage = %q{http://github.com/delano/otto}
|
|
29
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
|
30
|
+
s.require_paths = ["lib"]
|
|
31
|
+
s.rubyforge_project = %q{otto}
|
|
32
|
+
s.rubygems_version = %q{1.5.2}
|
|
33
|
+
s.summary = %q{Auto-define your rack-apps in plaintext.}
|
|
34
|
+
|
|
35
|
+
if s.respond_to? :specification_version then
|
|
36
|
+
s.specification_version = 3
|
|
37
|
+
|
|
38
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
39
|
+
s.add_runtime_dependency(%q<rack>, [">= 1.2.1"])
|
|
40
|
+
s.add_runtime_dependency(%q<addressable>, ["= 2.2.4"])
|
|
41
|
+
else
|
|
42
|
+
s.add_dependency(%q<rack>, [">= 1.2.1"])
|
|
43
|
+
s.add_dependency(%q<addressable>, ["= 2.2.4"])
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
s.add_dependency(%q<rack>, [">= 1.2.1"])
|
|
47
|
+
s.add_dependency(%q<addressable>, ["= 2.2.4"])
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
metadata
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: otto
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
prerelease:
|
|
5
|
+
version: 0.2.0.001
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Delano Mandelbaum
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
|
|
13
|
+
date: 2011-07-07 00:00:00 -04:00
|
|
14
|
+
default_executable:
|
|
15
|
+
dependencies:
|
|
16
|
+
- !ruby/object:Gem::Dependency
|
|
17
|
+
name: rack
|
|
18
|
+
prerelease: false
|
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
20
|
+
none: false
|
|
21
|
+
requirements:
|
|
22
|
+
- - ">="
|
|
23
|
+
- !ruby/object:Gem::Version
|
|
24
|
+
version: 1.2.1
|
|
25
|
+
type: :runtime
|
|
26
|
+
version_requirements: *id001
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: addressable
|
|
29
|
+
prerelease: false
|
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
31
|
+
none: false
|
|
32
|
+
requirements:
|
|
33
|
+
- - "="
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: 2.2.4
|
|
36
|
+
type: :runtime
|
|
37
|
+
version_requirements: *id002
|
|
38
|
+
description: Auto-define your rack-apps in plaintext.
|
|
39
|
+
email: delano@solutious.com
|
|
40
|
+
executables: []
|
|
41
|
+
|
|
42
|
+
extensions: []
|
|
43
|
+
|
|
44
|
+
extra_rdoc_files:
|
|
45
|
+
- LICENSE.txt
|
|
46
|
+
- README.md
|
|
47
|
+
files:
|
|
48
|
+
- CHANGES.txt
|
|
49
|
+
- LICENSE.txt
|
|
50
|
+
- README.md
|
|
51
|
+
- Rakefile
|
|
52
|
+
- VERSION.yml
|
|
53
|
+
- lib/otto.rb
|
|
54
|
+
- otto.gemspec
|
|
55
|
+
has_rdoc: true
|
|
56
|
+
homepage: http://github.com/delano/otto
|
|
57
|
+
licenses: []
|
|
58
|
+
|
|
59
|
+
post_install_message:
|
|
60
|
+
rdoc_options:
|
|
61
|
+
- --charset=UTF-8
|
|
62
|
+
require_paths:
|
|
63
|
+
- lib
|
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
65
|
+
none: false
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: "0"
|
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
|
+
none: false
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: "0"
|
|
76
|
+
requirements: []
|
|
77
|
+
|
|
78
|
+
rubyforge_project: otto
|
|
79
|
+
rubygems_version: 1.5.2
|
|
80
|
+
signing_key:
|
|
81
|
+
specification_version: 3
|
|
82
|
+
summary: Auto-define your rack-apps in plaintext.
|
|
83
|
+
test_files: []
|
|
84
|
+
|