utopia 0.12.5 → 0.12.6

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.
@@ -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'