utopia 1.0.11 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|