utopia 1.8.3 → 1.9.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/Gemfile +2 -0
- data/documentation/.bowerrc +4 -0
- data/documentation/.rspec +4 -0
- data/documentation/Gemfile +27 -0
- data/documentation/Rakefile +5 -0
- data/documentation/config.ru +50 -0
- data/documentation/config/README.md +7 -0
- data/documentation/config/environment.rb +10 -0
- data/documentation/lib/readme.txt +1 -0
- data/documentation/pages/_heading.xnode +2 -0
- data/documentation/pages/_page.xnode +28 -0
- data/documentation/pages/errors/exception.xnode +5 -0
- data/documentation/pages/errors/file-not-found.xnode +5 -0
- data/documentation/pages/links.yaml +2 -0
- data/documentation/pages/welcome/index.xnode +41 -0
- data/documentation/pages/wiki/content.md +7 -0
- data/{setup/examples → documentation/pages}/wiki/controller.rb +8 -6
- data/documentation/pages/wiki/development-environment-setup/content.md +14 -0
- data/{setup/examples → documentation/pages}/wiki/edit.xnode +1 -1
- data/documentation/pages/wiki/server-setup/content.md +40 -0
- data/documentation/pages/wiki/show.xnode +12 -0
- data/documentation/pages/wiki/your-first-page/content.md +31 -0
- data/documentation/public +1 -0
- data/documentation/spec/website_context.rb +11 -0
- data/documentation/spec/website_spec.rb +15 -0
- data/documentation/tasks/test.rake +10 -0
- data/documentation/tasks/utopia.rake +38 -0
- data/lib/utopia/content.rb +2 -2
- data/lib/utopia/content/link.rb +5 -1
- data/lib/utopia/content/markup.rb +86 -66
- data/lib/utopia/content/tag.rb +28 -45
- data/lib/utopia/content/transaction.rb +31 -25
- data/lib/utopia/controller/actions.rb +1 -0
- data/lib/utopia/redirection.rb +4 -4
- data/lib/utopia/version.rb +1 -1
- data/setup/site/public/_static/site.css +20 -8
- data/spec/utopia/content/markup_spec.rb +10 -10
- data/spec/utopia/content/tag_spec.rb +36 -0
- data/spec/utopia/controller/middleware_spec.ru +4 -1
- data/spec/utopia/controller/middleware_spec/controller/controller.rb +2 -0
- data/spec/utopia/controller/middleware_spec/controller/nested/controller.rb +2 -0
- data/spec/utopia/controller/middleware_spec/redirect/controller.rb +2 -0
- data/spec/utopia/controller/middleware_spec/redirect/test/controller.rb +2 -0
- data/spec/utopia/controller/respond_spec.ru +5 -2
- data/spec/utopia/exceptions/handler_spec.ru +3 -1
- data/spec/utopia/exceptions/handler_spec/controller.rb +2 -0
- data/spec/utopia/exceptions/mailer_spec.ru +6 -2
- data/spec/utopia/localization_spec.rb +2 -2
- data/spec/utopia/localization_spec.ru +2 -1
- data/spec/utopia/localization_spec/controller.rb +2 -0
- data/spec/utopia/performance_spec/config.ru +1 -0
- data/spec/utopia/performance_spec/pages/api/controller.rb +1 -1
- data/utopia.gemspec +3 -3
- metadata +36 -14
- data/ext/utopia/xnode/fast_scanner/extconf.rb +0 -6
- data/ext/utopia/xnode/fast_scanner/parser.c +0 -289
- data/setup/examples/wiki/index.xnode +0 -10
- data/setup/examples/wiki/welcome/content.md +0 -3
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
desc 'Run by git post-update hook when deployed to a web server'
|
3
|
+
task :deploy do
|
4
|
+
# This task is typiclly run after the site is updated but before the server is restarted.
|
5
|
+
end
|
6
|
+
|
7
|
+
desc 'Restart the application server'
|
8
|
+
task :restart do
|
9
|
+
# This task is run after the deployment task above.
|
10
|
+
if passenger_config = `which passenger-config`.chomp!
|
11
|
+
sh(passenger_config, 'restart-app', '--ignore-passenger-not-running', File.dirname(__dir__))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Set up the environment for running your web application'
|
16
|
+
task :environment do
|
17
|
+
require_relative '../config/environment'
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'Run a server for testing your web application'
|
21
|
+
task :server => :environment do
|
22
|
+
port = ENV.fetch('SERVER_PORT', 9292).to_s
|
23
|
+
exec('puma', '-v', '-p', port)
|
24
|
+
end
|
25
|
+
|
26
|
+
desc 'Start an interactive console for your web application'
|
27
|
+
task :console => :environment do
|
28
|
+
require 'pry'
|
29
|
+
require 'rack/test'
|
30
|
+
|
31
|
+
include Rack::Test::Methods
|
32
|
+
|
33
|
+
def app
|
34
|
+
@app ||= Rack::Builder.parse_file(File.expand_path("../config.ru", __dir__)).first
|
35
|
+
end
|
36
|
+
|
37
|
+
Pry.start
|
38
|
+
end
|
data/lib/utopia/content.rb
CHANGED
@@ -60,10 +60,10 @@ module Utopia
|
|
60
60
|
def fetch_template(path)
|
61
61
|
if @template_cache
|
62
62
|
@template_cache.fetch_or_store(path.to_s) do
|
63
|
-
Trenni::
|
63
|
+
Trenni::MarkupTemplate.load_file(path)
|
64
64
|
end
|
65
65
|
else
|
66
|
-
Trenni::
|
66
|
+
Trenni::MarkupTemplate.load_file(path)
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
data/lib/utopia/content/link.rb
CHANGED
@@ -21,6 +21,8 @@
|
|
21
21
|
require 'yaml'
|
22
22
|
require 'trenni/builder'
|
23
23
|
|
24
|
+
require 'trenni/strings'
|
25
|
+
|
24
26
|
require_relative '../path'
|
25
27
|
require_relative '../locale'
|
26
28
|
|
@@ -86,7 +88,7 @@ module Utopia
|
|
86
88
|
@info.fetch(:title, @title)
|
87
89
|
end
|
88
90
|
|
89
|
-
def
|
91
|
+
def to_anchor(**options)
|
90
92
|
Trenni::Builder.fragment(options[:builder]) do |builder|
|
91
93
|
if href?
|
92
94
|
relative_href(options[:base])
|
@@ -102,6 +104,8 @@ module Utopia
|
|
102
104
|
end
|
103
105
|
end
|
104
106
|
|
107
|
+
alias to_href to_anchor
|
108
|
+
|
105
109
|
def to_s
|
106
110
|
"\#<#{self.class}(#{self.kind}) title=#{title.inspect} href=#{href.inspect}>"
|
107
111
|
end
|
@@ -18,7 +18,8 @@
|
|
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 'trenni/
|
21
|
+
require 'trenni/parsers'
|
22
|
+
require 'trenni/entities'
|
22
23
|
require 'trenni/strings'
|
23
24
|
|
24
25
|
require_relative 'tag'
|
@@ -48,106 +49,125 @@ module Utopia
|
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
51
|
-
class
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
class MarkupParser
|
53
|
+
class ParsedTag
|
54
|
+
def initialize(name, offset)
|
55
|
+
@offset = offset
|
56
|
+
@tag = Tag.new(name, false, SymbolicHash.new)
|
57
|
+
end
|
58
|
+
|
59
|
+
attr :tag
|
60
|
+
attr :offset
|
55
61
|
|
56
|
-
|
62
|
+
def to_s
|
63
|
+
@tag.to_s
|
64
|
+
end
|
57
65
|
end
|
58
|
-
|
66
|
+
|
59
67
|
class UnbalancedTagError < StandardError
|
60
|
-
def initialize(
|
61
|
-
@
|
62
|
-
@
|
63
|
-
@current_tag = current_tag
|
68
|
+
def initialize(buffer, opening_tag, closing_tag = nil)
|
69
|
+
@buffer = buffer
|
70
|
+
@opening_tag = current_tag
|
64
71
|
@closing_tag = closing_tag
|
65
|
-
|
66
|
-
@starting_line = Trenni::Location.new(@scanner.string, @start_position)
|
67
|
-
@ending_line = Trenni::Location.new(@scanner.string, @scanner.pos)
|
68
72
|
end
|
69
73
|
|
70
|
-
attr :
|
71
|
-
attr :start_position
|
74
|
+
attr :buffer
|
72
75
|
attr :current_tag
|
73
76
|
attr :closing_tag
|
74
|
-
|
77
|
+
|
78
|
+
def start_location
|
79
|
+
Trenni::Location.new(@buffer.read, current_tag.offset)
|
80
|
+
end
|
81
|
+
|
82
|
+
def end_location
|
83
|
+
if closing_tag and closing_tag.respond_to? :offset
|
84
|
+
Trenni::Location.new(@buffer.read, closing_tag.offset)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
75
88
|
def to_s
|
76
|
-
|
89
|
+
if @closing_tag
|
90
|
+
"#{start_location}: #{@opening_tag} was not closed!"
|
91
|
+
else
|
92
|
+
"#{start_location}: #{@opening_tag} was closed by #{@closing_tag}!"
|
93
|
+
end
|
77
94
|
end
|
78
95
|
end
|
79
|
-
|
80
|
-
def
|
96
|
+
|
97
|
+
def self.parse(buffer, delegate, entities = Trenni::Entities::HTML5)
|
98
|
+
# This is for compatibility with the existing API which passes in a string:
|
99
|
+
buffer = Trenni::Buffer(buffer)
|
100
|
+
|
101
|
+
self.new(buffer, delegate).parse!
|
102
|
+
end
|
103
|
+
|
104
|
+
def initialize(buffer, delegate, entities = Trenni::Entities::HTML5)
|
81
105
|
@buffer = buffer
|
106
|
+
|
82
107
|
@delegate = delegate
|
108
|
+
@entities = entities
|
109
|
+
|
110
|
+
@current = nil
|
83
111
|
@stack = []
|
84
112
|
end
|
85
|
-
|
113
|
+
|
86
114
|
def parse!
|
87
|
-
Trenni::
|
115
|
+
Trenni::Parsers.parse_markup(@buffer, self, @entities)
|
88
116
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
raise UnbalancedTagError.new(@scanner, current_position, current_tag.name, 'EOF')
|
117
|
+
if tag = @stack.pop
|
118
|
+
raise UnbalancedTagError.new(@buffer, tag)
|
93
119
|
end
|
94
120
|
end
|
95
121
|
|
96
|
-
def
|
97
|
-
@
|
98
|
-
end
|
99
|
-
|
100
|
-
def doctype(attributes)
|
101
|
-
@delegate.cdata("<!DOCTYPE #{attributes}>")
|
122
|
+
def open_tag_begin(name, offset)
|
123
|
+
@current = ParsedTag.new(name, offset)
|
102
124
|
end
|
103
125
|
|
104
|
-
def
|
105
|
-
@
|
126
|
+
def attribute(key, value)
|
127
|
+
@current.tag.attributes[key] = value
|
106
128
|
end
|
107
129
|
|
108
|
-
def
|
109
|
-
|
130
|
+
def open_tag_end(self_closing)
|
131
|
+
if self_closing
|
132
|
+
@current.tag.closed = true
|
133
|
+
@delegate.tag_complete(@current.tag)
|
134
|
+
else
|
135
|
+
@stack << @current
|
136
|
+
@delegate.tag_begin(@current.tag)
|
137
|
+
end
|
138
|
+
|
139
|
+
@current = nil
|
110
140
|
end
|
111
141
|
|
112
|
-
def
|
113
|
-
@
|
142
|
+
def close_tag(name, offset)
|
143
|
+
@current = @stack.pop
|
144
|
+
tag = @current.tag
|
145
|
+
|
146
|
+
if tag.name != name
|
147
|
+
raise UnbalancedTagError.new(@buffer, tag, ParsedTag.new(name, offset))
|
148
|
+
end
|
149
|
+
|
150
|
+
@delegate.tag_end(tag)
|
114
151
|
end
|
115
|
-
|
116
|
-
def begin_tag(tag_name, begin_tag_type)
|
117
|
-
if begin_tag_type == :opened
|
118
|
-
@stack << [Tag.new(tag_name, SymbolicHash.new), @scanner.pos]
|
119
|
-
else
|
120
|
-
current_tag, current_position = @stack.pop
|
121
|
-
|
122
|
-
if tag_name != current_tag.name
|
123
|
-
raise UnbalancedTagError.new(@scanner, current_position, current_tag.name, tag_name)
|
124
|
-
end
|
125
152
|
|
126
|
-
|
127
|
-
|
153
|
+
def doctype(string)
|
154
|
+
@delegate.write(string)
|
128
155
|
end
|
129
156
|
|
130
|
-
def
|
131
|
-
|
132
|
-
|
133
|
-
cur, pos = @stack.pop
|
134
|
-
cur.closed = true
|
135
|
-
|
136
|
-
@delegate.tag_complete(cur)
|
137
|
-
elsif end_tag_type == :opened # <...>
|
138
|
-
cur, pos = @stack.last
|
157
|
+
def comment(string)
|
158
|
+
@delegate.write(string)
|
159
|
+
end
|
139
160
|
|
140
|
-
|
141
|
-
|
142
|
-
end
|
161
|
+
def instruction(string)
|
162
|
+
@delegate.write(string)
|
143
163
|
end
|
144
164
|
|
145
|
-
def
|
146
|
-
@
|
165
|
+
def cdata(string)
|
166
|
+
@delegate.write(string)
|
147
167
|
end
|
148
168
|
|
149
|
-
def
|
150
|
-
|
169
|
+
def text(string)
|
170
|
+
@delegate.text(string)
|
151
171
|
end
|
152
172
|
end
|
153
173
|
end
|
data/lib/utopia/content/tag.rb
CHANGED
@@ -18,63 +18,46 @@
|
|
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 'trenni/markup'
|
22
|
+
|
21
23
|
module Utopia
|
22
24
|
class Content
|
23
25
|
# This represents an individual SGML tag, e.g. <a>, </a> or <a />, with attributes. Attribute values must be escaped.
|
24
|
-
|
25
|
-
|
26
|
-
if Tag === other
|
27
|
-
[@name, @attributes, @closed] == [other.name, other.attributes, other.closed]
|
28
|
-
end
|
29
|
-
end
|
26
|
+
Tag = Struct.new(:name, :closed, :attributes) do
|
27
|
+
include Trenni::Markup
|
30
28
|
|
31
|
-
def self.closed(name, attributes
|
32
|
-
|
33
|
-
tag.closed = true
|
34
|
-
|
35
|
-
return tag
|
29
|
+
def self.closed(name, **attributes)
|
30
|
+
self.new(name, true, attributes)
|
36
31
|
end
|
37
32
|
|
38
|
-
def
|
39
|
-
|
40
|
-
@attributes = attributes
|
41
|
-
@closed = false
|
42
|
-
end
|
43
|
-
|
44
|
-
def freeze
|
45
|
-
@name.freeze
|
46
|
-
@attributes.freeze
|
47
|
-
@closed.freeze
|
48
|
-
|
49
|
-
super
|
50
|
-
end
|
51
|
-
|
52
|
-
attr_accessor :name
|
53
|
-
attr_accessor :attributes
|
54
|
-
attr_accessor :closed
|
55
|
-
|
56
|
-
def [](key)
|
57
|
-
@attributes[key]
|
33
|
+
def self.opened(name, **attributes)
|
34
|
+
self.new(name, false, attributes)
|
58
35
|
end
|
59
36
|
|
60
|
-
def
|
61
|
-
|
37
|
+
def [] key
|
38
|
+
attributes[key]
|
62
39
|
end
|
63
40
|
|
41
|
+
alias to_hash attributes
|
42
|
+
|
64
43
|
def to_s(content = nil)
|
65
|
-
buffer = String.new
|
44
|
+
buffer = String.new.force_encoding(name.encoding)
|
66
45
|
|
67
|
-
|
46
|
+
write(buffer, content)
|
68
47
|
|
69
48
|
return buffer
|
70
49
|
end
|
71
50
|
|
72
|
-
alias
|
51
|
+
alias to_str to_s
|
52
|
+
|
53
|
+
def self_closed?
|
54
|
+
closed
|
55
|
+
end
|
73
56
|
|
74
|
-
def
|
57
|
+
def write_opening_tag(buffer, self_closing = false)
|
75
58
|
buffer << "<#{name}"
|
76
59
|
|
77
|
-
|
60
|
+
attributes.each do |key, value|
|
78
61
|
if value
|
79
62
|
buffer << " #{key}=\"#{value}\""
|
80
63
|
else
|
@@ -82,24 +65,24 @@ module Utopia
|
|
82
65
|
end
|
83
66
|
end
|
84
67
|
|
85
|
-
if
|
68
|
+
if self_closing
|
86
69
|
buffer << "/>"
|
87
70
|
else
|
88
71
|
buffer << ">"
|
89
72
|
end
|
90
73
|
end
|
91
74
|
|
92
|
-
def
|
75
|
+
def write_closing_tag(buffer)
|
93
76
|
buffer << "</#{name}>"
|
94
77
|
end
|
95
78
|
|
96
|
-
def
|
97
|
-
if
|
98
|
-
|
79
|
+
def write(buffer, content = nil)
|
80
|
+
if closed and content.nil?
|
81
|
+
write_opening_tag(buffer, true)
|
99
82
|
else
|
100
|
-
|
83
|
+
write_opening_tag(buffer)
|
101
84
|
buffer << content if content
|
102
|
-
|
85
|
+
write_closing_tag(buffer)
|
103
86
|
end
|
104
87
|
end
|
105
88
|
end
|
@@ -29,7 +29,7 @@ module Utopia
|
|
29
29
|
def initialize(tag)
|
30
30
|
@tag = tag
|
31
31
|
|
32
|
-
super
|
32
|
+
super "Unbalanced tag #{tag.name}"
|
33
33
|
end
|
34
34
|
|
35
35
|
attr :tag
|
@@ -48,12 +48,13 @@ module Utopia
|
|
48
48
|
@begin_tags = []
|
49
49
|
@end_tags = []
|
50
50
|
|
51
|
+
# TODO: Provide way to set encoding and content type.
|
52
|
+
#@encoding = Encoding::UTF_8
|
53
|
+
#self.content_type = "text/html; charset=#{@encoding.name}"
|
54
|
+
|
51
55
|
super()
|
52
56
|
end
|
53
57
|
|
54
|
-
attr :status
|
55
|
-
attr :headers
|
56
|
-
|
57
58
|
# A helper method for accessing controller variables from view:
|
58
59
|
def controller
|
59
60
|
@controller ||= Utopia::Controller[request]
|
@@ -64,7 +65,7 @@ module Utopia
|
|
64
65
|
end
|
65
66
|
|
66
67
|
def parse_markup(markup)
|
67
|
-
|
68
|
+
MarkupParser.parse(markup, self)
|
68
69
|
end
|
69
70
|
|
70
71
|
# The Rack::Request for this transaction.
|
@@ -83,7 +84,8 @@ module Utopia
|
|
83
84
|
attr :end_tags
|
84
85
|
|
85
86
|
def tag(name, attributes = {}, &block)
|
86
|
-
|
87
|
+
# If we provide a block which can give inner data, we are not self-closing.
|
88
|
+
tag = Tag.new(name, !block_given?, attributes)
|
87
89
|
|
88
90
|
node = tag_begin(tag)
|
89
91
|
|
@@ -94,7 +96,7 @@ module Utopia
|
|
94
96
|
|
95
97
|
def tag_complete(tag, node = nil)
|
96
98
|
if tag.name == CONTENT_TAG_NAME
|
97
|
-
current.
|
99
|
+
current.write(content)
|
98
100
|
else
|
99
101
|
node ||= lookup(tag)
|
100
102
|
|
@@ -125,9 +127,15 @@ module Utopia
|
|
125
127
|
|
126
128
|
return nil
|
127
129
|
end
|
130
|
+
|
131
|
+
def write(string)
|
132
|
+
current.write(string)
|
133
|
+
end
|
134
|
+
|
135
|
+
alias cdata write
|
128
136
|
|
129
|
-
def
|
130
|
-
current.
|
137
|
+
def text(string)
|
138
|
+
current.text(string)
|
131
139
|
end
|
132
140
|
|
133
141
|
def tag_end(tag = nil)
|
@@ -146,7 +154,7 @@ module Utopia
|
|
146
154
|
self.end_tags.pop
|
147
155
|
|
148
156
|
if current
|
149
|
-
current.
|
157
|
+
current.write(buffer)
|
150
158
|
end
|
151
159
|
|
152
160
|
return buffer
|
@@ -188,7 +196,7 @@ module Utopia
|
|
188
196
|
@begin_tags.last
|
189
197
|
end
|
190
198
|
|
191
|
-
# The content of the node
|
199
|
+
# The content of the node
|
192
200
|
def content
|
193
201
|
@end_tags.last.content
|
194
202
|
end
|
@@ -207,14 +215,13 @@ module Utopia
|
|
207
215
|
def initialize(tag, node, attributes = tag.to_hash)
|
208
216
|
@node = node
|
209
217
|
|
210
|
-
|
211
|
-
@buffer = String.new
|
218
|
+
@buffer = Trenni::MarkupString.new.force_encoding(Encoding::UTF_8)
|
212
219
|
|
213
220
|
@overrides = {}
|
214
|
-
|
221
|
+
|
215
222
|
@tags = []
|
216
223
|
@attributes = attributes
|
217
|
-
|
224
|
+
|
218
225
|
@content = nil
|
219
226
|
@deferred = []
|
220
227
|
end
|
@@ -230,7 +237,7 @@ module Utopia
|
|
230
237
|
def defer(value = nil, &block)
|
231
238
|
@deferred << block
|
232
239
|
|
233
|
-
Tag.closed(DEFERRED_TAG_NAME, :id => @deferred.size - 1)
|
240
|
+
Tag.closed(DEFERRED_TAG_NAME, :id => @deferred.size - 1)
|
234
241
|
end
|
235
242
|
|
236
243
|
def [](key)
|
@@ -253,7 +260,7 @@ module Utopia
|
|
253
260
|
|
254
261
|
def call(transaction)
|
255
262
|
@content = @buffer
|
256
|
-
@buffer =
|
263
|
+
@buffer = Trenni::MarkupString.new.force_encoding(Encoding::UTF_8)
|
257
264
|
|
258
265
|
if node.respond_to? :call
|
259
266
|
node.call(transaction, self)
|
@@ -264,27 +271,26 @@ module Utopia
|
|
264
271
|
return @buffer
|
265
272
|
end
|
266
273
|
|
267
|
-
def
|
268
|
-
@buffer <<
|
274
|
+
def write(string)
|
275
|
+
@buffer << string
|
269
276
|
end
|
270
277
|
|
271
|
-
def
|
272
|
-
|
278
|
+
def text(string)
|
279
|
+
@buffer << Trenni::MarkupString(string)
|
273
280
|
end
|
274
281
|
|
275
282
|
def tag_complete(tag)
|
276
|
-
tag.
|
283
|
+
tag.write(@buffer)
|
277
284
|
end
|
278
285
|
|
279
286
|
def tag_begin(tag)
|
280
287
|
@tags << tag
|
281
|
-
tag.
|
288
|
+
tag.write_opening_tag(@buffer)
|
282
289
|
end
|
283
290
|
|
284
291
|
def tag_end(tag)
|
285
292
|
raise UnbalancedTagError(tag) unless @tags.pop.name == tag.name
|
286
|
-
|
287
|
-
tag.write_close_html(@buffer)
|
293
|
+
tag.write_closing_tag(@buffer)
|
288
294
|
end
|
289
295
|
end
|
290
296
|
end
|