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