Class: Otto
- Inherits:
-
Object
- Object
- Otto
- Defined in:
- lib/otto.rb,
lib/otto.rb
Defined Under Namespace
Modules: RequestHelpers, ResponseHelpers, Static, VERSION Classes: Route
Constant Summary collapse
- LIB_HOME =
File. File.dirname(__FILE__)
Class Attribute Summary collapse
-
.debug ⇒ Object
Returns the value of attribute debug.
Instance Attribute Summary collapse
-
#not_found ⇒ Object
Returns the value of attribute not_found.
-
#option ⇒ Object
(also: #options)
readonly
Returns the value of attribute option.
-
#route_definitions ⇒ Object
readonly
Returns the value of attribute route_definitions.
-
#routes ⇒ Object
readonly
Returns the value of attribute routes.
-
#routes_literal ⇒ Object
readonly
Returns the value of attribute routes_literal.
-
#routes_static ⇒ Object
readonly
Returns the value of attribute routes_static.
-
#server_error ⇒ Object
Returns the value of attribute server_error.
-
#static_route ⇒ Object
readonly
Returns the value of attribute static_route.
Class Method Summary collapse
- .default ⇒ Object
- .env?(*guesses) ⇒ Boolean
- .load(path) ⇒ Object
- .path(definition, params = {}) ⇒ Object
- .routes ⇒ Object
Instance Method Summary collapse
- #add_static_path(path) ⇒ Object
- #call(env) ⇒ Object
- #determine_locale(env) ⇒ Object
-
#initialize(path = nil, opts = {}) ⇒ Otto
constructor
A new instance of Otto.
- #load(path) ⇒ Object
- #safe_dir?(path) ⇒ Boolean
- #safe_file?(path) ⇒ Boolean
-
#uri(route_definition, params = {}) ⇒ Object
Return the URI path for the given
route_definition
e.g.
Constructor Details
#initialize(path = nil, opts = {}) ⇒ Otto
Returns a new instance of Otto.
30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/otto.rb', line 30 def initialize path=nil, opts={} @routes_static = { :GET => {} } @routes = { :GET => [] } @routes_literal = { :GET => {} } @route_definitions = {} @option = opts.merge({ :public => nil, :locale => 'en' }) load(path) unless path.nil? super() end |
Class Attribute Details
.debug ⇒ Object
Returns the value of attribute debug.
325 326 327 |
# File 'lib/otto.rb', line 325 def debug @debug end |
Instance Attribute Details
#not_found ⇒ Object
Returns the value of attribute not_found.
29 30 31 |
# File 'lib/otto.rb', line 29 def not_found @not_found end |
#option ⇒ Object (readonly) Also known as: options
Returns the value of attribute option.
28 29 30 |
# File 'lib/otto.rb', line 28 def option @option end |
#route_definitions ⇒ Object (readonly)
Returns the value of attribute route_definitions.
27 28 29 |
# File 'lib/otto.rb', line 27 def route_definitions @route_definitions end |
#routes ⇒ Object (readonly)
Returns the value of attribute routes.
27 28 29 |
# File 'lib/otto.rb', line 27 def routes @routes end |
#routes_literal ⇒ Object (readonly)
Returns the value of attribute routes_literal.
27 28 29 |
# File 'lib/otto.rb', line 27 def routes_literal @routes_literal end |
#routes_static ⇒ Object (readonly)
Returns the value of attribute routes_static.
27 28 29 |
# File 'lib/otto.rb', line 27 def routes_static @routes_static end |
#server_error ⇒ Object
Returns the value of attribute server_error.
29 30 31 |
# File 'lib/otto.rb', line 29 def server_error @server_error end |
#static_route ⇒ Object (readonly)
Returns the value of attribute static_route.
28 29 30 |
# File 'lib/otto.rb', line 28 def static_route @static_route end |
Class Method Details
.default ⇒ Object
326 327 328 329 |
# File 'lib/otto.rb', line 326 def default @default ||= Otto.new @default end |
.env?(*guesses) ⇒ Boolean
339 340 341 |
# File 'lib/otto.rb', line 339 def env? *guesses !guesses.flatten.select { |n| ENV['RACK_ENV'].to_s == n.to_s }.empty? end |
.load(path) ⇒ Object
330 331 332 |
# File 'lib/otto.rb', line 330 def load path default.load path end |
.path(definition, params = {}) ⇒ Object
333 334 335 |
# File 'lib/otto.rb', line 333 def path definition, params={} default.path definition, params end |
.routes ⇒ Object
336 337 338 |
# File 'lib/otto.rb', line 336 def routes default.routes end |
Instance Method Details
#add_static_path(path) ⇒ Object
76 77 78 79 80 81 82 83 84 85 |
# File 'lib/otto.rb', line 76 def add_static_path path if safe_file?(path) base_path = File.split(path).first # Files in the root directory can refer to themselves base_path = path if base_path == '/' static_path = File.join(option[:public], base_path) STDERR.puts "new static route: #{base_path} (#{path})" if Otto.debug routes_static[:GET][base_path] = base_path end end |
#call(env) ⇒ Object
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/otto.rb', line 87 def call env locale = determine_locale env env['rack.locale'] = locale if option[:public] && safe_dir?(option[:public]) @static_route ||= Rack::File.new(option[:public]) end path_info = Rack::Utils.unescape(env['PATH_INFO']) path_info = '/' if path_info.to_s.empty? path_info_clean = path_info.gsub /\/$/, '' base_path = File.split(path_info).first # Files in the root directory can refer to themselves base_path = path_info if base_path == '/' http_verb = env['REQUEST_METHOD'].upcase.to_sym literal_routes = routes_literal[http_verb] || {} literal_routes.merge! routes_literal[:GET] if http_verb == :HEAD if static_route && http_verb == :GET && routes_static[:GET].member?(base_path) #STDERR.puts " request: #{path_info} (static)" static_route.call(env) elsif literal_routes.has_key?(path_info_clean) route = literal_routes[path_info_clean] #STDERR.puts " request: #{http_verb} #{path_info} (literal route: #{route.verb} #{route.path})" route.call(env) elsif static_route && http_verb == :GET && safe_file?(path_info) static_path = File.join(option[:public], base_path) STDERR.puts " new static route: #{base_path} (#{path_info})" routes_static[:GET][base_path] = base_path static_route.call(env) else extra_params = {} found_route = nil valid_routes = routes[http_verb] || [] valid_routes.push *routes[:GET] if http_verb == :HEAD valid_routes.each { |route| #STDERR.puts " request: #{http_verb} #{path_info} (trying route: #{route.verb} #{route.pattern})" if (match = route.pattern.match(path_info)) values = match.captures.to_a # The first capture returned is the entire matched string b/c # we wrapped the entire regex in parens. We don't need it to # the full match. full_match = values.shift extra_params = if route.keys.any? route.keys.zip(values).inject({}) do |hash,(k,v)| if k == 'splat' (hash[k] ||= []) << v else hash[k] = v end hash end elsif values.any? {'captures' => values} else {} end found_route = route break end } found_route ||= literal_routes['/404'] if found_route found_route.call env, extra_params else @not_found || Otto::Static.not_found end end rescue => ex STDERR.puts ex., ex.backtrace if found_route = literal_routes['/500'] found_route.call env else @server_error || Otto::Static.server_error end end |
#determine_locale(env) ⇒ Object
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/otto.rb', line 188 def determine_locale env accept_langs = env['HTTP_ACCEPT_LANGUAGE'] accept_langs = self.option[:locale] if accept_langs.to_s.empty? locales = [] unless accept_langs.empty? locales = accept_langs.split(',').map { |l| l += ';q=1.0' unless l =~ /;q=\d+(?:\.\d+)?$/ l.split(';q=') }.sort_by { |locale, qvalue| qvalue.to_f }.collect { |locale, qvalue| locale }.reverse end STDERR.puts "locale: #{locales} (#{accept_langs})" if Otto.debug locales.empty? ? nil : locales end |
#load(path) ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/otto.rb', line 43 def load path path = File.(path) raise ArgumentError, "Bad path: #{path}" unless File.exist?(path) raw = File.readlines(path).select { |line| line =~ /^\w/ }.collect { |line| line.strip.split(/\s+/) } raw.each { |entry| begin verb, path, definition = *entry route = Otto::Route.new verb, path, definition route.otto = self path_clean = path.gsub /\/$/, '' @route_definitions[route.definition] = route STDERR.puts "route: #{route.pattern}" if Otto.debug @routes[route.verb] ||= [] @routes[route.verb] << route @routes_literal[route.verb] ||= {} @routes_literal[route.verb][path_clean] = route rescue => ex STDERR.puts "Bad route in #{path}: #{entry}" end } self end |
#safe_dir?(path) ⇒ Boolean
72 73 74 |
# File 'lib/otto.rb', line 72 def safe_dir? path (File.owned?(path) || File.grpowned?(path)) && File.directory?(path) end |
#safe_file?(path) ⇒ Boolean
66 67 68 69 70 |
# File 'lib/otto.rb', line 66 def safe_file? path globstr = File.join(option[:public], '*') pathstr = File.join(option[:public], path) File.fnmatch?(globstr, pathstr) && (File.owned?(pathstr) || File.grpowned?(pathstr)) && File.readable?(pathstr) && !File.directory?(pathstr) end |
#uri(route_definition, params = {}) ⇒ Object
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/otto.rb', line 168 def uri route_definition, params={} #raise RuntimeError, "Not working" route = @route_definitions[route_definition] unless route.nil? local_params = params.clone local_path = route.path.clone if objid = local_params.delete(:id) || local_params.delete('id') local_path.gsub! /\*/, objid end local_params.each_pair { |k,v| next unless local_path.match(":#{k}") local_path.gsub!(":#{k}", local_params.delete(k)) } uri = Addressable::URI.new uri.path = local_path uri.query_values = local_params uri.to_s end end |