utopia 1.0.11 → 1.1.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.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/.travis.yml +1 -0
- data/bin/utopia +1 -1
- data/lib/utopia/content.rb +10 -12
- data/lib/utopia/content/link.rb +8 -11
- data/lib/utopia/content/links.rb +1 -1
- data/lib/utopia/content/node.rb +8 -7
- data/lib/utopia/controller.rb +40 -39
- data/lib/utopia/controller/action.rb +14 -12
- data/lib/utopia/controller/base.rb +31 -32
- data/lib/utopia/controller/rewrite.rb +104 -0
- data/lib/utopia/exception_handler.rb +1 -1
- data/lib/utopia/extensions/rack.rb +21 -21
- data/lib/utopia/http.rb +14 -9
- data/lib/utopia/localization.rb +1 -1
- data/lib/utopia/middleware.rb +3 -0
- data/lib/utopia/path.rb +81 -25
- data/lib/utopia/path/matcher.rb +94 -0
- data/lib/utopia/redirector.rb +4 -4
- data/lib/utopia/session/encrypted_cookie.rb +1 -1
- data/lib/utopia/static.rb +1 -1
- data/lib/utopia/tags/node.rb +1 -1
- data/lib/utopia/version.rb +1 -1
- data/setup/site/config.ru +1 -1
- data/spec/utopia/content/link_spec.rb +8 -8
- data/spec/utopia/content/node_spec.rb +1 -1
- data/spec/utopia/content_spec.rb +3 -3
- data/spec/utopia/content_spec.ru +1 -1
- data/spec/utopia/controller/action_spec.rb +61 -0
- data/spec/utopia/controller/middleware_spec.rb +71 -0
- data/spec/utopia/controller/middleware_spec.ru +4 -0
- data/spec/utopia/controller/middleware_spec/controller/controller.rb +24 -0
- data/spec/utopia/{pages → controller/middleware_spec}/controller/index.xnode +0 -0
- data/spec/utopia/{pages → controller/middleware_spec}/controller/nested/controller.rb +0 -0
- data/spec/utopia/controller/rewrite_spec.rb +66 -0
- data/spec/utopia/{controller_spec.rb → controller/sequence_spec.rb} +11 -84
- data/spec/utopia/exception_handler_spec.rb +3 -3
- data/spec/utopia/exception_handler_spec.ru +2 -2
- data/spec/utopia/exception_handler_spec/controller.rb +15 -0
- data/spec/utopia/path/matcher_spec.rb +65 -0
- data/spec/utopia/rack_spec.rb +0 -12
- data/utopia.gemspec +1 -1
- metadata +27 -14
- data/spec/utopia/controller_spec.ru +0 -4
- data/spec/utopia/pages/controller/controller.rb +0 -43
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 965466abaa777fe6b33b71c24253718da3544bd1
|
4
|
+
data.tar.gz: 1a53312d2f58949a2a6027292dd5d6a66e0c56ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a24b35e08ccdbb23e1a85236e25b5166235a39d04d08fa2b5d8b5f00f9feb7ae07a43956bf6629ef4e2e98eaf00ac32b95638efa5b5e8df5cb7eb9d33a1fc002
|
7
|
+
data.tar.gz: 56ec05c1856d72c0bf9305f6c0ce821348c83f9d8c2d90a4cc3c53586755533b67c2b89fb1d50c7a4b0fa865e747b25d0d8b94d64983e23210717766325ddb81
|
data/.rspec
ADDED
data/.travis.yml
CHANGED
data/bin/utopia
CHANGED
@@ -37,7 +37,7 @@ Rake::TaskManager.record_task_metadata = true
|
|
37
37
|
verbose(false)
|
38
38
|
|
39
39
|
module Setup
|
40
|
-
BASE = File.expand_path("
|
40
|
+
BASE = File.expand_path("../setup", __dir__)
|
41
41
|
|
42
42
|
module Site
|
43
43
|
CONFIGURATION_FILES = ['config.ru', 'Gemfile', 'Rakefile']
|
data/lib/utopia/content.rb
CHANGED
@@ -28,7 +28,9 @@ require 'trenni/template'
|
|
28
28
|
|
29
29
|
module Utopia
|
30
30
|
class Content
|
31
|
-
|
31
|
+
INDEX = 'index'.freeze
|
32
|
+
|
33
|
+
def initialize(app, **options)
|
32
34
|
@app = app
|
33
35
|
|
34
36
|
@root = File.expand_path(options[:root] || Utopia::default_root)
|
@@ -57,7 +59,7 @@ module Utopia
|
|
57
59
|
return @tags[name]
|
58
60
|
end
|
59
61
|
|
60
|
-
if String === name && name.index(
|
62
|
+
if String === name && name.index('/')
|
61
63
|
name = Path.create(name)
|
62
64
|
end
|
63
65
|
|
@@ -77,7 +79,7 @@ module Utopia
|
|
77
79
|
end
|
78
80
|
|
79
81
|
if String === name_path
|
80
|
-
tag_path = File.join(root, dir.components,
|
82
|
+
tag_path = File.join(root, dir.components, '_' + name_path)
|
81
83
|
|
82
84
|
if File.exist? tag_path
|
83
85
|
return Node.new(self, dir + name, parent_path + name, tag_path)
|
@@ -112,9 +114,9 @@ module Utopia
|
|
112
114
|
|
113
115
|
# If the request for /foo/bar{extensions} is actually a directory, rewrite it to /foo/bar/index{extensions}:
|
114
116
|
if File.directory? directory_path
|
115
|
-
index_path = [basename.name, basename.rename(
|
117
|
+
index_path = [basename.name, basename.rename(INDEX)]
|
116
118
|
|
117
|
-
return [307, {
|
119
|
+
return [307, {HTTP::LOCATION => path.dirname.join(index_path).to_s}, []]
|
118
120
|
end
|
119
121
|
|
120
122
|
locale = env[Localization::CURRENT_LOCALE_KEY]
|
@@ -122,17 +124,13 @@ module Utopia
|
|
122
124
|
if link.path and node = lookup_node(link.path)
|
123
125
|
response = Rack::Response.new
|
124
126
|
|
125
|
-
attributes =
|
126
|
-
|
127
|
-
if request.respond_to?(:controller)
|
128
|
-
attributes = request.controller
|
129
|
-
end
|
127
|
+
attributes = request.env.fetch(VARIABLES_KEY, {}).to_hash
|
130
128
|
|
131
|
-
node.process!(request, response,
|
129
|
+
node.process!(request, response, attributes)
|
132
130
|
|
133
131
|
return response.finish
|
134
132
|
elsif redirect_uri = link[:uri]
|
135
|
-
return [307, {
|
133
|
+
return [307, {HTTP::LOCATION => redirect_uri}, []]
|
136
134
|
end
|
137
135
|
end
|
138
136
|
|
data/lib/utopia/content/link.rb
CHANGED
@@ -38,7 +38,7 @@ module Utopia
|
|
38
38
|
@name, @variant = path.last.split('.', 2)
|
39
39
|
@path = path
|
40
40
|
when :directory
|
41
|
-
# raise ArgumentError unless path.last.start_with?
|
41
|
+
# raise ArgumentError unless path.last.start_with? INDEX
|
42
42
|
|
43
43
|
@name = path.dirname.last
|
44
44
|
@variant = path.last.split('.', 2)[1]
|
@@ -85,7 +85,7 @@ module Utopia
|
|
85
85
|
@info.fetch(:title, @title)
|
86
86
|
end
|
87
87
|
|
88
|
-
def to_href(options
|
88
|
+
def to_href(**options)
|
89
89
|
Trenni::Builder.fragment(options[:builder]) do |builder|
|
90
90
|
if href?
|
91
91
|
relative_href(options[:base])
|
@@ -101,19 +101,16 @@ module Utopia
|
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
104
|
+
def to_s
|
105
|
+
"\#<#{self.class}(#{self.kind}) title=#{title.inspect} href=#{href.inspect}>"
|
106
|
+
end
|
107
|
+
|
104
108
|
def eql? other
|
105
|
-
|
106
|
-
return kind.eql?(other.kind) &&
|
107
|
-
name.eql?(other.name) &&
|
108
|
-
path.eql?(other.path) &&
|
109
|
-
info.eql?(other.info)
|
110
|
-
else
|
111
|
-
return false
|
112
|
-
end
|
109
|
+
self.class.eql?(other.class) and kind.eql?(other.kind) and name.eql?(other.name) and path.eql?(other.path) and info.eql?(other.info)
|
113
110
|
end
|
114
111
|
|
115
112
|
def == other
|
116
|
-
|
113
|
+
other and kind == other.kind and name == other.name and path == other.path
|
117
114
|
end
|
118
115
|
|
119
116
|
def default_locale?
|
data/lib/utopia/content/links.rb
CHANGED
data/lib/utopia/content/node.rb
CHANGED
@@ -134,7 +134,7 @@ module Utopia
|
|
134
134
|
|
135
135
|
# A helper method for accessing controller variables from view:
|
136
136
|
def controller
|
137
|
-
@request.
|
137
|
+
@request.env[VARIABLES_KEY]
|
138
138
|
end
|
139
139
|
|
140
140
|
def parse_xml(xml_data)
|
@@ -315,8 +315,9 @@ module Utopia
|
|
315
315
|
@controller.lookup_node(path)
|
316
316
|
end
|
317
317
|
|
318
|
-
def local_path(path =
|
319
|
-
path = Path
|
318
|
+
def local_path(path = '.', base = nil)
|
319
|
+
path = Path[path]
|
320
|
+
|
320
321
|
root = Pathname.new(@controller.root)
|
321
322
|
|
322
323
|
if path.absolute?
|
@@ -342,8 +343,8 @@ module Utopia
|
|
342
343
|
uri_path.dirname
|
343
344
|
end
|
344
345
|
|
345
|
-
def links(path =
|
346
|
-
path = uri_path.dirname + Path
|
346
|
+
def links(path = '.', **options, &block)
|
347
|
+
path = uri_path.dirname + Path[path]
|
347
348
|
links = Links.index(@controller.root, path, options)
|
348
349
|
|
349
350
|
if block_given?
|
@@ -361,14 +362,14 @@ module Utopia
|
|
361
362
|
def siblings_path
|
362
363
|
name = @uri_path.last.split('.', 2).first
|
363
364
|
|
364
|
-
if name ==
|
365
|
+
if name == INDEX
|
365
366
|
@uri_path.dirname(2)
|
366
367
|
else
|
367
368
|
@uri_path.dirname
|
368
369
|
end
|
369
370
|
end
|
370
371
|
|
371
|
-
def sibling_links(options
|
372
|
+
def sibling_links(**options)
|
372
373
|
return Links.index(@controller.root, siblings_path, options)
|
373
374
|
end
|
374
375
|
|
data/lib/utopia/controller.rb
CHANGED
@@ -20,25 +20,32 @@
|
|
20
20
|
|
21
21
|
require_relative 'path'
|
22
22
|
|
23
|
+
require_relative 'middleware'
|
23
24
|
require_relative 'controller/variables'
|
24
25
|
require_relative 'controller/action'
|
25
26
|
require_relative 'controller/base'
|
26
27
|
|
27
|
-
|
28
|
-
def controller(&block)
|
29
|
-
if block_given?
|
30
|
-
env["utopia.controller"].instance_eval(&block)
|
31
|
-
else
|
32
|
-
env["utopia.controller"]
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
28
|
+
require_relative 'controller/rewrite'
|
36
29
|
|
37
30
|
module Utopia
|
31
|
+
module Controllers
|
32
|
+
def self.class_name_for_controller(controller)
|
33
|
+
controller.uri_path.to_a.collect{|_| _.capitalize}.join + "_#{controller.object_id}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.define(klass)
|
37
|
+
self.const_set(
|
38
|
+
class_name_for_controller(klass),
|
39
|
+
klass,
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
38
44
|
class Controller
|
39
|
-
CONTROLLER_RB =
|
40
|
-
|
41
|
-
|
45
|
+
CONTROLLER_RB = 'controller.rb'.freeze
|
46
|
+
PATH_INFO_KEY = 'PATH_INFO'.freeze
|
47
|
+
|
48
|
+
def initialize(app, **options)
|
42
49
|
@app = app
|
43
50
|
@root = options[:root] || Utopia::default_root
|
44
51
|
|
@@ -77,11 +84,10 @@ module Utopia
|
|
77
84
|
|
78
85
|
klass.const_set(:CONTROLLER, self)
|
79
86
|
|
80
|
-
$LOAD_PATH.unshift(base_path)
|
81
|
-
|
82
87
|
klass.class_eval(File.read(controller_path), controller_path)
|
83
88
|
|
84
|
-
|
89
|
+
# Give the controller a useful name:
|
90
|
+
# Controllers.define(klass)
|
85
91
|
|
86
92
|
return klass.new
|
87
93
|
else
|
@@ -89,45 +95,40 @@ module Utopia
|
|
89
95
|
end
|
90
96
|
end
|
91
97
|
|
92
|
-
def invoke_controllers(
|
93
|
-
|
98
|
+
def invoke_controllers(request)
|
99
|
+
relative_path = Path[request.path_info]
|
94
100
|
controller_path = Path.new
|
101
|
+
variables = request.env[VARIABLES_KEY]
|
95
102
|
|
96
|
-
|
97
|
-
|
103
|
+
while relative_path.components.any?
|
104
|
+
controller_path.components << relative_path.components.shift
|
105
|
+
|
98
106
|
if controller = lookup_controller(controller_path)
|
99
|
-
#
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
end
|
108
|
-
|
109
|
-
if location
|
110
|
-
# Rewrite relative paths based on the controller's URI:
|
111
|
-
request.env['PATH_INFO'] = Path[location].expand(controller.class.uri_path).to_s
|
112
|
-
|
113
|
-
return invoke_controllers(variables, request, done)
|
114
|
-
end
|
115
|
-
|
116
|
-
done << controller
|
107
|
+
# Don't modify the original controller:
|
108
|
+
controller = controller.clone
|
109
|
+
|
110
|
+
# Append the controller to the set of controller variables, updates the controller with all current instance variables.
|
111
|
+
variables << controller
|
112
|
+
|
113
|
+
if result = controller.process!(request, relative_path)
|
114
|
+
return result
|
117
115
|
end
|
118
116
|
end
|
119
117
|
end
|
120
118
|
|
119
|
+
# The controllers may have rewriten the path so we update the path info:
|
120
|
+
request.env[PATH_INFO_KEY] = controller_path.to_s
|
121
|
+
|
121
122
|
# No controller gave a useful result:
|
122
123
|
return nil
|
123
124
|
end
|
124
125
|
|
125
126
|
def call(env)
|
126
|
-
variables = (env[
|
127
|
+
variables = (env[VARIABLES_KEY] ||= Variables.new)
|
127
128
|
|
128
129
|
request = Rack::Request.new(env)
|
129
130
|
|
130
|
-
if result = invoke_controllers(
|
131
|
+
if result = invoke_controllers(request)
|
131
132
|
return result
|
132
133
|
end
|
133
134
|
|
@@ -21,8 +21,7 @@
|
|
21
21
|
module Utopia
|
22
22
|
class Controller
|
23
23
|
class Action < Hash
|
24
|
-
attr_accessor :callback
|
25
|
-
attr_accessor :options
|
24
|
+
attr_accessor :path, :callback, :options
|
26
25
|
|
27
26
|
def callback?
|
28
27
|
@callback != nil
|
@@ -33,15 +32,15 @@ module Utopia
|
|
33
32
|
end
|
34
33
|
|
35
34
|
def eql? other
|
36
|
-
super and
|
35
|
+
super and @callback.eql? other.callback and @options.eql? other.options and @path.eql? other.path
|
37
36
|
end
|
38
37
|
|
39
38
|
def hash
|
40
|
-
[super, callback, options].hash
|
39
|
+
[super, callback, options, path].hash
|
41
40
|
end
|
42
41
|
|
43
42
|
def == other
|
44
|
-
super and
|
43
|
+
super and @callback == other.callback and @options == other.options and @path == other.path
|
45
44
|
end
|
46
45
|
|
47
46
|
protected
|
@@ -76,28 +75,31 @@ module Utopia
|
|
76
75
|
# relative_path = 2014/mr-potato
|
77
76
|
# actions => {:** => A}
|
78
77
|
def select(relative_path)
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
return actions
|
78
|
+
selection = [].tap do |actions|
|
79
|
+
append(relative_path.reverse, 0, actions)
|
80
|
+
end
|
84
81
|
end
|
85
82
|
|
86
|
-
def define(path, options
|
83
|
+
def define(path, **options, &callback)
|
87
84
|
current = self
|
88
85
|
|
89
86
|
path.reverse.each do |name|
|
90
87
|
current = (current[name.to_sym] ||= Action.new)
|
91
88
|
end
|
92
89
|
|
90
|
+
current.path = path
|
93
91
|
current.options = options
|
94
92
|
current.callback = callback
|
95
93
|
|
96
94
|
return current
|
97
95
|
end
|
98
96
|
|
97
|
+
def arity
|
98
|
+
@callback ? @callback.arity : 0
|
99
|
+
end
|
100
|
+
|
99
101
|
def invoke!(controller, *arguments)
|
100
|
-
controller.instance_exec(*arguments, &@callback)
|
102
|
+
controller.instance_exec(*arguments, self, &@callback)
|
101
103
|
end
|
102
104
|
|
103
105
|
def inspect
|
@@ -22,6 +22,8 @@ require_relative '../http'
|
|
22
22
|
|
23
23
|
module Utopia
|
24
24
|
class Controller
|
25
|
+
EMPTY_BODY = [].freeze
|
26
|
+
|
25
27
|
class Base
|
26
28
|
def self.base_path
|
27
29
|
self.const_get(:BASE_PATH)
|
@@ -36,55 +38,56 @@ module Utopia
|
|
36
38
|
end
|
37
39
|
|
38
40
|
class << self
|
39
|
-
def require_local(path)
|
40
|
-
require File.join(base_path, path)
|
41
|
-
end
|
42
|
-
|
43
41
|
def direct?(path)
|
44
42
|
path.dirname == uri_path
|
45
43
|
end
|
46
44
|
|
45
|
+
def patterns
|
46
|
+
@patterns ||= []
|
47
|
+
end
|
48
|
+
|
49
|
+
def match(pattern, &block)
|
50
|
+
patterns << [pattern, block]
|
51
|
+
end
|
52
|
+
|
47
53
|
def actions
|
48
54
|
@actions ||= Action.new
|
49
55
|
end
|
50
56
|
|
51
|
-
def on(path, options
|
52
|
-
if Symbol
|
53
|
-
|
57
|
+
def on(first, *path, **options, &block)
|
58
|
+
if first.is_a? Symbol
|
59
|
+
first = ['**', first]
|
54
60
|
end
|
55
61
|
|
56
|
-
actions.define(Path.
|
62
|
+
actions.define(Path.split(first) + path, options, &block)
|
57
63
|
end
|
58
64
|
|
59
65
|
def lookup(path)
|
60
|
-
|
66
|
+
relative_path = (path - uri_path).to_a
|
67
|
+
|
68
|
+
possible_actions = actions.select(relative_path)
|
61
69
|
end
|
62
70
|
end
|
63
71
|
|
64
72
|
# Given a path, look up all matched actions.
|
65
|
-
def
|
73
|
+
def actions_for_request(request, path)
|
66
74
|
self.class.lookup(path)
|
67
75
|
end
|
68
76
|
|
69
77
|
# Given a request, call associated actions if at least one exists.
|
70
78
|
def passthrough(request, path)
|
71
|
-
actions =
|
79
|
+
actions = actions_for_request(request, path)
|
72
80
|
|
73
|
-
|
74
|
-
variables = request.controller
|
75
|
-
controller_clone = self.clone
|
76
|
-
|
77
|
-
variables << controller_clone
|
78
|
-
|
81
|
+
unless actions.empty?
|
79
82
|
response = catch(:response) do
|
80
83
|
# By default give nothing - i.e. keep on processing:
|
81
84
|
actions.each do |action|
|
82
|
-
action.invoke!(
|
85
|
+
action.invoke!(self, request, path)
|
83
86
|
end and nil
|
84
87
|
end
|
85
88
|
|
86
89
|
if response
|
87
|
-
return
|
90
|
+
return self.respond_with(*response)
|
88
91
|
end
|
89
92
|
end
|
90
93
|
|
@@ -111,11 +114,7 @@ module Utopia
|
|
111
114
|
end
|
112
115
|
|
113
116
|
def redirect! (target, status = 302)
|
114
|
-
respond! :redirect => target
|
115
|
-
end
|
116
|
-
|
117
|
-
def rewrite! location
|
118
|
-
throw :rewrite, location.to_str
|
117
|
+
respond! :redirect => target, :status => status
|
119
118
|
end
|
120
119
|
|
121
120
|
def fail!(error = :bad_request)
|
@@ -140,25 +139,26 @@ module Utopia
|
|
140
139
|
status = options[:status] || status
|
141
140
|
end
|
142
141
|
|
143
|
-
status =
|
142
|
+
status = HTTP::STATUS_CODES[status] || status
|
144
143
|
headers = options[:headers] || {}
|
145
144
|
|
146
|
-
if options[:type]
|
147
|
-
headers[
|
145
|
+
if type = options[:type]
|
146
|
+
headers[HTTP::CONTENT_TYPE] ||= type
|
148
147
|
end
|
149
148
|
|
150
|
-
if options[:redirect]
|
151
|
-
headers[
|
149
|
+
if redirect = options[:redirect]
|
150
|
+
headers[HTTP::LOCATION] = redirect.to_s
|
152
151
|
status = 302 if status < 300 || status >= 400
|
153
152
|
end
|
154
153
|
|
155
|
-
body = []
|
156
154
|
if options[:body]
|
157
155
|
body = options[:body]
|
158
156
|
elsif options[:content]
|
159
157
|
body = [options[:content]]
|
160
158
|
elsif status >= 300
|
161
|
-
body = [
|
159
|
+
body = [HTTP::STATUS_DESCRIPTIONS[status] || "Status #{status}"]
|
160
|
+
else
|
161
|
+
body = EMPTY_BODY
|
162
162
|
end
|
163
163
|
|
164
164
|
return [status, headers, body]
|
@@ -166,7 +166,6 @@ module Utopia
|
|
166
166
|
|
167
167
|
# Return nil if this controller didn't do anything. Request will keep on processing. Return a valid rack response if the controller can do so.
|
168
168
|
def process!(request, path)
|
169
|
-
# puts "process! #{request} #{path}"
|
170
169
|
passthrough(request, path)
|
171
170
|
end
|
172
171
|
end
|