utopia 0.12.5 → 0.12.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,9 +18,9 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require 'utopia/middleware'
22
- require 'utopia/path'
23
- require 'utopia/http'
21
+ require_relative 'controller/variables'
22
+ require_relative 'controller/action'
23
+ require_relative 'controller/base'
24
24
 
25
25
  class Rack::Request
26
26
  def controller(&block)
@@ -35,210 +35,7 @@ end
35
35
  module Utopia
36
36
  module Middleware
37
37
  class Controller
38
- CONTROLLER_RB = "controller.rb"
39
-
40
- class Variables
41
- def initialize
42
- @controllers = [Object.new]
43
- end
44
-
45
- def << controller
46
- @controllers << controller
47
- end
48
-
49
- def fetch(key)
50
- @controllers.reverse_each do |controller|
51
- if controller.instance_variables.include?(key)
52
- return controller.instance_variable_get(key)
53
- end
54
- end
55
-
56
- if block_given?
57
- yield key
58
- else
59
- raise KeyError.new(key)
60
- end
61
- end
62
-
63
- def to_hash
64
- attributes = {}
65
-
66
- @controllers.each do |controller|
67
- controller.instance_variables.each do |name|
68
- key = name[1..-1]
69
-
70
- # Instance variables that start with an underscore are considered private and not exposed:
71
- next if key.start_with?('_')
72
-
73
- attributes[key] = controller.instance_variable_get(name)
74
- end
75
- end
76
-
77
- return attributes
78
- end
79
-
80
- def [] key
81
- fetch("@#{key}".to_sym) { nil }
82
- end
83
-
84
- # Deprecated - to support old code:
85
- def []= key, value
86
- @controllers.first.instance_variable_set("@#{key}".to_sym, value)
87
- end
88
- end
89
-
90
- class Base
91
- def initialize(controller)
92
- @_controller = controller
93
- @_actions = {}
94
-
95
- methods.each do |method_name|
96
- next unless method_name.match(/on_(.*)$/)
97
-
98
- action($1.split("_")) do |path, request|
99
- # LOG.debug("Controller: #{method_name}")
100
- self.send(method_name, path, request)
101
-
102
- # Don't pass the result back, instead use pass! or respond!
103
- nil
104
- end
105
- end
106
- end
107
-
108
- def action(path, options = {}, &block)
109
- cur = @_actions
110
-
111
- path.reverse.each do |name|
112
- cur = cur[name] ||= {}
113
- end
114
-
115
- cur[:action] = Proc.new(&block)
116
- end
117
-
118
- def lookup(path)
119
- cur = @_actions
120
-
121
- path.components.reverse.each do |name|
122
- cur = cur[name]
123
-
124
- return nil if cur == nil
125
-
126
- if action = cur[:action]
127
- return action
128
- end
129
- end
130
- end
131
-
132
- # Given a request, call an associated action if one exists.
133
- def passthrough(path, request)
134
- action = lookup(path)
135
-
136
- if action
137
- variables = request.controller
138
- clone = self.dup
139
-
140
- variables << clone
141
-
142
- response = catch(:response) do
143
- clone.instance_exec(path, request, &action)
144
-
145
- # By default give nothing - i.e. keep on processing:
146
- nil
147
- end
148
-
149
- if response
150
- return clone.respond_with(*response)
151
- end
152
- end
153
-
154
- return nil
155
- end
156
-
157
- def call(env)
158
- @_controller.app.call(env)
159
- end
160
-
161
- def respond!(*args)
162
- throw :response, args
163
- end
164
-
165
- def ignore!
166
- throw :response, nil
167
- end
168
-
169
- def redirect! (target, status = 302)
170
- respond! :redirect => target, :status => status
171
- end
172
-
173
- def fail!(error = :bad_request)
174
- respond! error
175
- end
176
-
177
- def success!(*args)
178
- respond! :success, *args
179
- end
180
-
181
- def respond_with(*args)
182
- return args[0] if args[0] == nil || Array === args[0]
183
-
184
- status = 200
185
- options = nil
186
-
187
- if Numeric === args[0] || Symbol === args[0]
188
- status = args[0]
189
- options = args[1] || {}
190
- else
191
- options = args[0]
192
- status = options[:status] || status
193
- end
194
-
195
- status = Utopia::HTTP::STATUS_CODES[status] || status
196
- headers = options[:headers] || {}
197
-
198
- if options[:type]
199
- headers['Content-Type'] ||= options[:type]
200
- end
201
-
202
- if options[:redirect]
203
- headers["Location"] = options[:redirect]
204
- status = 302 if status < 300 || status >= 400
205
- end
206
-
207
- body = []
208
- if options[:body]
209
- body = options[:body]
210
- elsif options[:content]
211
- body = [options[:content]]
212
- elsif status >= 300
213
- body = [Utopia::HTTP::STATUS_DESCRIPTIONS[status] || "Status #{status}"]
214
- end
215
-
216
- # Utopia::LOG.debug([status, headers, body].inspect)
217
- return [status, headers, body]
218
- end
219
-
220
- def direct?(path)
221
- path.dirname == self.class.uri_path
222
- end
223
-
224
- def process!(path, request)
225
- return nil unless direct?(path)
226
-
227
- passthrough(path, request)
228
- end
229
-
230
- def self.base_path
231
- self.const_get(:BASE_PATH)
232
- end
233
-
234
- def self.uri_path
235
- self.const_get(:URI_PATH)
236
- end
237
-
238
- def self.require_local(path)
239
- require File.join(base_path, path)
240
- end
241
- end
38
+ CONTROLLER_RB = "controller.rb".freeze
242
39
 
243
40
  def initialize(app, options = {})
244
41
  @app = app
@@ -246,41 +43,32 @@ module Utopia
246
43
 
247
44
  @controllers = {}
248
45
  @cache_controllers = (UTOPIA_ENV == :production)
249
-
250
- if options[:controller_file]
251
- @controller_file = options[:controller_file]
252
- else
253
- @controller_file = "controller.rb"
254
- end
255
46
  end
256
-
47
+
257
48
  attr :app
258
-
259
- def lookup(path)
49
+
50
+ def lookup_controller(path)
260
51
  if @cache_controllers
261
52
  return @controllers.fetch(path.to_s) do |key|
262
- @controllers[key] = load_file(path)
53
+ @controllers[key] = load_controller_file(path)
263
54
  end
264
55
  else
265
- return load_file(path)
56
+ return load_controller_file(path)
266
57
  end
267
58
  end
268
-
269
- def load_file(path)
270
- if path.directory?
271
- uri_path = path
272
- base_path = File.join(@root, uri_path.components)
273
- else
274
- uri_path = path.dirname
275
- base_path = File.join(@root, uri_path.components)
276
- end
277
-
59
+
60
+ def load_controller_file(path)
61
+ uri_path = path
62
+ base_path = File.join(@root, uri_path.components)
63
+
278
64
  controller_path = File.join(base_path, CONTROLLER_RB)
279
-
65
+ # puts "load_controller_file(#{path.inspect}) => #{controller_path}"
66
+
280
67
  if File.exist?(controller_path)
281
68
  klass = Class.new(Base)
282
69
  klass.const_set(:BASE_PATH, base_path)
283
70
  klass.const_set(:URI_PATH, uri_path)
71
+ klass.const_set(:CONTROLLER, self)
284
72
 
285
73
  $LOAD_PATH.unshift(base_path)
286
74
 
@@ -288,34 +76,53 @@ module Utopia
288
76
 
289
77
  $LOAD_PATH.delete(base_path)
290
78
 
291
- return klass.new(self)
79
+ return klass.new
292
80
  else
293
81
  return nil
294
82
  end
295
83
  end
296
-
297
- def fetch_controllers(path)
298
- controllers = []
299
-
300
- path.ascend do |parent_path|
301
- controllers << lookup(parent_path)
84
+
85
+ def invoke_controllers(variables, request, done = Set.new)
86
+ path = Path.create(request.path_info)
87
+ controller_path = Path.new
88
+
89
+ path.descend do |controller_path|
90
+ # puts "Invoke controller: #{controller_path}"
91
+ if controller = lookup_controller(controller_path)
92
+ # We only want to invoke controllers which have not already been invoked:
93
+ unless done.include? controller
94
+ # If we get throw :rewrite, location, the URL has been rewritten and we need to request again:
95
+ location = catch(:rewrite) do
96
+ # Invoke the controller and if it returns a result, send it back out:
97
+ if result = controller.process!(request, path)
98
+ return result
99
+ end
100
+ end
101
+
102
+ if location
103
+ request.env['PATH_INFO'] = location.to_s
104
+
105
+ return invoke_controllers(variables, request, done)
106
+ end
107
+
108
+ done << controller
109
+ end
110
+ end
302
111
  end
303
-
304
- return controllers.compact.reverse
112
+
113
+ # No controller gave a useful result:
114
+ return nil
305
115
  end
306
-
116
+
307
117
  def call(env)
308
118
  variables = (env["utopia.controller"] ||= Variables.new)
309
119
 
310
120
  request = Rack::Request.new(env)
311
-
312
- path = Path.create(request.path_info)
313
- fetch_controllers(path).each do |controller|
314
- if result = controller.process!(path, request)
315
- return result
316
- end
121
+
122
+ if result = invoke_controllers(variables, request)
123
+ return result
317
124
  end
318
-
125
+
319
126
  return @app.call(env)
320
127
  end
321
128
  end
@@ -23,7 +23,6 @@ require 'utopia/path'
23
23
 
24
24
  module Utopia
25
25
  module Middleware
26
-
27
26
  class DirectoryIndex
28
27
  def initialize(app, options = {})
29
28
  @app = app
@@ -52,6 +51,5 @@ module Utopia
52
51
  end
53
52
  end
54
53
  end
55
-
56
54
  end
57
55
  end
@@ -0,0 +1,80 @@
1
+ # Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
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.
20
+
21
+ require 'utopia/middleware'
22
+ require 'utopia/extensions/string'
23
+
24
+ module Utopia
25
+ module Middleware
26
+ class ExceptionHandler
27
+ def initialize(app, location)
28
+ @app = app
29
+
30
+ @location = location
31
+ end
32
+
33
+ def fatal_error(env, ex)
34
+ body = StringIO.new
35
+
36
+ body.puts "<!DOCTYPE html><html><head><title>Fatal Error</title></head><body>"
37
+ body.puts "<h1>Fatal Error</h1>"
38
+ body.puts "<p>While requesting resource #{env['PATH_INFO'].to_html}, a fatal error occurred.</p>"
39
+ body.puts "<blockquote><strong>#{ex.class.name.to_html}</strong>: #{ex.to_s.to_html}</blockquote>"
40
+ body.puts "<p>There is nothing more we can do to fix the problem at this point.</p>"
41
+ body.puts "<p>We apologize for the inconvenience.</p>"
42
+ body.puts "</body></html>"
43
+ body.rewind
44
+
45
+ return [400, {"Content-Type" => "text/html"}, body]
46
+ end
47
+
48
+ def redirect(env, ex)
49
+ return @app.call(env.merge('PATH_INFO' => @location, 'REQUEST_METHOD' => 'GET'))
50
+ end
51
+
52
+ def call(env)
53
+ begin
54
+ return @app.call(env)
55
+ rescue Exception => ex
56
+ log = ::Logger.new(env['rack.errors'])
57
+
58
+ log.error "Exception #{ex.to_s.dump}!"
59
+
60
+ ex.backtrace.each do |bt|
61
+ log.error bt
62
+ end
63
+
64
+ if env['PATH_INFO'] == @location
65
+ return fatal_error(env, ex)
66
+ else
67
+
68
+ # If redirection fails
69
+ begin
70
+ return redirect(env, ex)
71
+ rescue
72
+ return fatal_error(env, ex)
73
+ end
74
+
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -38,63 +38,6 @@ module Utopia
38
38
  "Requested resource #{@resource_path} resulted in a #{@resource_status} error. Requested error handler #{@error_path} resulted in a #{@error_status} error."
39
39
  end
40
40
  end
41
-
42
- class ExceptionHandler
43
- def initialize(app, location)
44
- @app = app
45
-
46
- @location = location
47
- end
48
-
49
- def fatal_error(env, ex)
50
- body = StringIO.new
51
-
52
- body.puts "<!DOCTYPE html><html><head><title>Fatal Error</title></head><body>"
53
- body.puts "<h1>Fatal Error</h1>"
54
- body.puts "<p>While requesting resource #{env['PATH_INFO'].to_html}, a fatal error occurred.</p>"
55
- body.puts "<blockquote><strong>#{ex.class.name.to_html}</strong>: #{ex.to_s.to_html}</blockquote>"
56
- body.puts "<p>There is nothing more we can do to fix the problem at this point.</p>"
57
- body.puts "<p>We apologize for the inconvenience.</p>"
58
- body.puts "</body></html>"
59
- body.rewind
60
-
61
- return [400, {"Content-Type" => "text/html"}, body]
62
- end
63
-
64
- def redirect(env, ex)
65
- return @app.call(env.merge('PATH_INFO' => @location, 'REQUEST_METHOD' => 'GET'))
66
- end
67
-
68
- def call(env)
69
- begin
70
- return @app.call(env)
71
- rescue Exception => ex
72
- log = ::Logger.new(env['rack.errors'])
73
-
74
- log.error "Exception #{ex.to_s.dump}!"
75
-
76
- ex.backtrace.each do |bt|
77
- log.error bt
78
- end
79
-
80
- if env['PATH_INFO'] == @location
81
- return fatal_error(env, ex)
82
- else
83
-
84
- # If redirection fails
85
- begin
86
- return redirect(env, ex)
87
- rescue
88
- return fatal_error(env, ex)
89
- end
90
-
91
- end
92
- end
93
- end
94
- end
95
-
96
- # Legacy Support
97
- ExceptionRedirector = ExceptionHandler
98
41
 
99
42
  class Redirector
100
43
  # Redirects a whole source tree to a destination tree, given by the roots.
data/lib/utopia/path.rb CHANGED
@@ -19,7 +19,6 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Utopia
22
-
23
22
  class Path
24
23
  SEPARATOR = "/"
25
24
 
@@ -55,6 +54,8 @@ module Utopia
55
54
  else
56
55
  return Path.new(path.split(SEPARATOR))
57
56
  end
57
+ when Symbol
58
+ return Path.new([path])
58
59
  end
59
60
  end
60
61
 
@@ -84,7 +85,7 @@ module Utopia
84
85
  end
85
86
  end
86
87
 
87
- def to_s
88
+ def to_str
88
89
  if @components == [""]
89
90
  SEPARATOR
90
91
  else
@@ -92,6 +93,14 @@ module Utopia
92
93
  end
93
94
  end
94
95
 
96
+ def to_s
97
+ to_str
98
+ end
99
+
100
+ def [] index
101
+ @components[index]
102
+ end
103
+
95
104
  def join(other)
96
105
  Path.new(@components + other).simplify
97
106
  end
@@ -168,21 +177,30 @@ module Utopia
168
177
  components.join(File::SEPARATOR)
169
178
  end
170
179
 
180
+ def descend(&block)
181
+ return to_enum(:descend) unless block_given?
182
+
183
+ parent_path = []
184
+
185
+ components.each do |component|
186
+ parent_path << component
187
+
188
+ yield self.class.new(parent_path)
189
+ end
190
+ end
191
+
171
192
  def ascend(&block)
172
- paths = []
193
+ return to_enum(:ascend) unless block_given?
173
194
 
174
195
  next_parent = self
175
-
196
+
176
197
  begin
177
198
  parent = next_parent
178
199
 
179
- yield parent if block_given?
180
- paths << parent
200
+ yield parent
181
201
 
182
202
  next_parent = parent.dirname
183
203
  end until next_parent.eql?(parent)
184
-
185
- return paths
186
204
  end
187
205
 
188
206
  def split(at)
@@ -247,5 +265,4 @@ module Utopia
247
265
  return Path.locale(last, extension)
248
266
  end
249
267
  end
250
-
251
- end
268
+ end
@@ -3,7 +3,7 @@
3
3
  UTOPIA_ENV = (ENV['UTOPIA_ENV'] || ENV['RACK_ENV'] || :development).to_sym
4
4
  $LOAD_PATH << File.join(File.dirname(__FILE__), "lib")
5
5
 
6
- require 'utopia/middleware/all'
6
+ require 'utopia'
7
7
 
8
8
  # Utopia relies heavily on a local cache:
9
9
  require 'rack/cache'
@@ -16,7 +16,6 @@ else
16
16
  end
17
17
 
18
18
  use Rack::ContentLength
19
- use Utopia::Middleware::Logger
20
19
 
21
20
  use Utopia::Middleware::Redirector, {
22
21
  :strings => {
@@ -20,6 +20,8 @@
20
20
 
21
21
  require 'pathname'
22
22
 
23
+ warn "require 'utopia/tags/all' is deprecated. Require specific tags instead."
24
+
23
25
  Pathname.new(__FILE__).dirname.entries.each do |path|
24
26
  next unless /\.rb$/ === path.to_s
25
27
 
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Utopia
22
- VERSION = "0.12.5"
22
+ VERSION = "0.12.6"
23
23
  end
data/lib/utopia.rb CHANGED
@@ -20,5 +20,12 @@
20
20
 
21
21
  require 'utopia/version'
22
22
 
23
- module Utopia
24
- end
23
+ require 'utopia/middleware/content'
24
+ require 'utopia/middleware/controller'
25
+ require 'utopia/middleware/directory_index'
26
+ require 'utopia/middleware/exception_handler'
27
+ require 'utopia/middleware/localization'
28
+ require 'utopia/middleware/mail_exceptions'
29
+ require 'utopia/middleware/redirector'
30
+ require 'utopia/middleware/requester'
31
+ require 'utopia/middleware/static'