utopia 0.9.17
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.
- data/ext/utopia/xnode/fast_scanner/extconf.rb +6 -0
- data/ext/utopia/xnode/fast_scanner/parser.c +289 -0
- data/lib/utopia.rb +2 -0
- data/lib/utopia/etanni.rb +96 -0
- data/lib/utopia/extensions.rb +87 -0
- data/lib/utopia/link.rb +243 -0
- data/lib/utopia/middleware.rb +33 -0
- data/lib/utopia/middleware/all.rb +24 -0
- data/lib/utopia/middleware/benchmark.rb +47 -0
- data/lib/utopia/middleware/content.rb +139 -0
- data/lib/utopia/middleware/content/node.rb +363 -0
- data/lib/utopia/middleware/controller.rb +198 -0
- data/lib/utopia/middleware/directory_index.rb +54 -0
- data/lib/utopia/middleware/localization.rb +94 -0
- data/lib/utopia/middleware/localization/name.rb +64 -0
- data/lib/utopia/middleware/logger.rb +68 -0
- data/lib/utopia/middleware/redirector.rb +171 -0
- data/lib/utopia/middleware/requester.rb +116 -0
- data/lib/utopia/middleware/static.rb +186 -0
- data/lib/utopia/path.rb +193 -0
- data/lib/utopia/response_helper.rb +22 -0
- data/lib/utopia/session/encrypted_cookie.rb +115 -0
- data/lib/utopia/tag.rb +84 -0
- data/lib/utopia/tags.rb +32 -0
- data/lib/utopia/tags/all.rb +25 -0
- data/lib/utopia/tags/env.rb +24 -0
- data/lib/utopia/tags/fortune.rb +20 -0
- data/lib/utopia/tags/gallery.rb +175 -0
- data/lib/utopia/tags/google_analytics.rb +37 -0
- data/lib/utopia/tags/node.rb +24 -0
- data/lib/utopia/tags/override.rb +28 -0
- data/lib/utopia/time_store.rb +102 -0
- data/lib/utopia/version.rb +24 -0
- data/lib/utopia/xnode.rb +17 -0
- data/lib/utopia/xnode/processor.rb +97 -0
- data/lib/utopia/xnode/scanner.rb +153 -0
- metadata +168 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
# Copyright (c) 2010 Samuel Williams. Released under the GNU GPLv3.
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'utopia/tags'
|
17
|
+
|
18
|
+
Utopia::Tags.create("google_analytics") do |transaction, state|
|
19
|
+
html = <<EOF
|
20
|
+
<!-- Google Analytics -->
|
21
|
+
|
22
|
+
<script type="text/javascript">
|
23
|
+
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
24
|
+
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
25
|
+
</script>
|
26
|
+
<script type="text/javascript">
|
27
|
+
try {
|
28
|
+
var pageTracker = _gat._getTracker(#{state[:id].dump});
|
29
|
+
pageTracker._trackPageview();
|
30
|
+
} catch(err) {}</script>
|
31
|
+
|
32
|
+
<!-- Google Analytics -->
|
33
|
+
EOF
|
34
|
+
|
35
|
+
|
36
|
+
transaction.cdata(html)
|
37
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Copyright (c) 2010 Samuel Williams. Released under the GNU GPLv3.
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'utopia/tags'
|
17
|
+
|
18
|
+
Utopia::Tags.create("node") do |transaction, state|
|
19
|
+
path = Utopia::Path.create(state[:path])
|
20
|
+
|
21
|
+
node = transaction.lookup_node(path)
|
22
|
+
|
23
|
+
transaction.render_node(node)
|
24
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Copyright (c) 2010 Samuel Williams. Released under the GNU GPLv3.
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'utopia/tags'
|
17
|
+
|
18
|
+
class Utopia::Tags::Override
|
19
|
+
def self.tag_begin(transaction, state)
|
20
|
+
state.overrides[state[:name]] = state[:with]
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.call(transaction, state)
|
24
|
+
transaction.parse_xml(state.content)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Utopia::Tags.register("override", Utopia::Tags::Override)
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# Copyright (c) 2010 Samuel Williams. Released under the GNU GPLv3.
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'set'
|
17
|
+
require 'csv'
|
18
|
+
|
19
|
+
module Utopia
|
20
|
+
|
21
|
+
# TimeStore is a very simple time oriented database. New entries
|
22
|
+
# are added in chronological order and it is not possible to change
|
23
|
+
# this behaviour, or remove old entries. It stores data in a CSV
|
24
|
+
# format into a directory where each file represents a week in the
|
25
|
+
# year.
|
26
|
+
#
|
27
|
+
# The design of this class is to enable efficient logging of data
|
28
|
+
# in a backup friendly file format (i.e. files older than one week
|
29
|
+
# are not touched).
|
30
|
+
#
|
31
|
+
# Due to the nature of CSV data, a header must be specified. This
|
32
|
+
# header can have columns added, but not removed. Columns not
|
33
|
+
# specified in the header will not be recorded.
|
34
|
+
#
|
35
|
+
class TimeStore
|
36
|
+
def initialize(path, header)
|
37
|
+
@path = path
|
38
|
+
|
39
|
+
header = header.collect{|name| name.to_s}
|
40
|
+
|
41
|
+
@header_path = File.join(@path, "header.csv")
|
42
|
+
|
43
|
+
if File.exist? @header_path
|
44
|
+
@header = File.read(@header_path).split(",")
|
45
|
+
else
|
46
|
+
@header = []
|
47
|
+
end
|
48
|
+
|
49
|
+
diff = (Set.new(header) + "time") - @header
|
50
|
+
|
51
|
+
if diff.size
|
52
|
+
@header += diff.to_a.sort
|
53
|
+
|
54
|
+
File.open(@header_path, "w") do |file|
|
55
|
+
file.write(@header.join(","))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
@last_path = nil
|
60
|
+
@last_file = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
attr :header
|
64
|
+
|
65
|
+
def path_for_time(time)
|
66
|
+
return File.join(@path, time.strftime("%Y-%W") + ".csv")
|
67
|
+
end
|
68
|
+
|
69
|
+
def open(time, &block)
|
70
|
+
path = path_for_time(time)
|
71
|
+
|
72
|
+
if @last_path != path
|
73
|
+
if @last_file
|
74
|
+
@last_file.close
|
75
|
+
@last_file = nil
|
76
|
+
end
|
77
|
+
|
78
|
+
@last_file = File.open(path, "a")
|
79
|
+
@last_file.sync = true
|
80
|
+
@last_path = path
|
81
|
+
end
|
82
|
+
|
83
|
+
yield @last_file
|
84
|
+
|
85
|
+
#File.open(path_for_time(time), "a", &block)
|
86
|
+
end
|
87
|
+
|
88
|
+
def dump(values)
|
89
|
+
row = @header.collect{|key| values[key.to_sym]}
|
90
|
+
return CSV.generate_line(row)
|
91
|
+
end
|
92
|
+
|
93
|
+
def <<(values)
|
94
|
+
time = values[:time] = Time.now
|
95
|
+
|
96
|
+
open(time) do |file|
|
97
|
+
file.puts(dump(values))
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Copyright (c) 2009 Samuel Williams. Released under the GNU GPLv3.
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
module Utopia
|
17
|
+
module VERSION #:nodoc:
|
18
|
+
MAJOR = 0
|
19
|
+
MINOR = 9
|
20
|
+
TINY = 17
|
21
|
+
|
22
|
+
STRING = [MAJOR, MINOR, TINY].join('.')
|
23
|
+
end
|
24
|
+
end
|
data/lib/utopia/xnode.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Copyright (c) 2010 Samuel Williams. Released under the GNU GPLv3.
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'utopia/xnode/scanner'
|
17
|
+
require 'utopia/xnode/processor'
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# Copyright (c) 2010 Samuel Williams. Released under the GNU GPLv3.
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'utopia/tag'
|
17
|
+
|
18
|
+
module Utopia
|
19
|
+
module XNode
|
20
|
+
OPENED_TAG = 0
|
21
|
+
CLOSED_TAG = 1
|
22
|
+
|
23
|
+
class UnbalancedTagError < StandardError
|
24
|
+
def initialize(scanner, start_pos, current_tag, closing_tag)
|
25
|
+
@scanner = scanner
|
26
|
+
@start_pos = start_pos
|
27
|
+
@current_tag = current_tag
|
28
|
+
@closing_tag = closing_tag
|
29
|
+
|
30
|
+
@starting_line = @scanner.calculate_line_number(@start_pos)
|
31
|
+
@ending_line = @scanner.calculate_line_number
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
"UnbalancedTagError: Tag #{@current_tag} (line #{@starting_line[0]}: #{@starting_line[4]}) has been closed by #{@closing_tag} (line #{@ending_line[0]}: #{@ending_line[4]})."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Processor
|
40
|
+
def initialize(content, delegate, options = {})
|
41
|
+
@delegate = delegate
|
42
|
+
@stack = []
|
43
|
+
|
44
|
+
@scanner = (options[:scanner] || Scanner).new(self, content)
|
45
|
+
end
|
46
|
+
|
47
|
+
def parse
|
48
|
+
@scanner.parse
|
49
|
+
end
|
50
|
+
|
51
|
+
def cdata(text)
|
52
|
+
# $stderr.puts "\tcdata: #{text}"
|
53
|
+
@delegate.cdata(text)
|
54
|
+
end
|
55
|
+
|
56
|
+
def comment(text)
|
57
|
+
cdata("<!#{text}>")
|
58
|
+
end
|
59
|
+
|
60
|
+
def begin_tag(tag_name, begin_tag_type)
|
61
|
+
# $stderr.puts "begin_tag: #{tag_name}, #{begin_tag_type}"
|
62
|
+
if begin_tag_type == OPENED_TAG
|
63
|
+
@stack << [Tag.new(tag_name, {}), @scanner.pos]
|
64
|
+
else
|
65
|
+
cur, pos = @stack.pop
|
66
|
+
|
67
|
+
if (tag_name != cur.name)
|
68
|
+
raise UnbalancedTagError.new(@scanner, pos, cur.name, tag_name)
|
69
|
+
end
|
70
|
+
|
71
|
+
@delegate.tag_end(cur)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def finish_tag(begin_tag_type, end_tag_type)
|
76
|
+
# $stderr.puts "finish_tag: #{begin_tag_type} #{end_tag_type}"
|
77
|
+
if begin_tag_type == OPENED_TAG # <...
|
78
|
+
if end_tag_type == CLOSED_TAG # <.../>
|
79
|
+
cur, pos = @stack.pop
|
80
|
+
cur.closed = true
|
81
|
+
|
82
|
+
@delegate.tag_complete(cur)
|
83
|
+
elsif end_tag_type == OPENED_TAG # <...>
|
84
|
+
cur, pos = @stack.last
|
85
|
+
|
86
|
+
@delegate.tag_begin(cur)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def attribute(name, value)
|
92
|
+
# $stderr.puts "\tattribute: #{name} = #{value}"
|
93
|
+
@stack.last[0].attributes[name] = value
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# Copyright (c) 2010 Samuel Williams. Released under the GNU GPLv3.
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'strscan'
|
17
|
+
require 'tempfile'
|
18
|
+
|
19
|
+
module Utopia
|
20
|
+
module XNode
|
21
|
+
class ScanError < StandardError
|
22
|
+
def initialize(message, scanner)
|
23
|
+
@message = message
|
24
|
+
|
25
|
+
@pos = scanner.pos
|
26
|
+
@line = scanner.calculate_line_number
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
if @line
|
31
|
+
"Scan Error: #{@message} @ [#{@line[0]}:#{@line[2]}]: #{@line[4]}"
|
32
|
+
else
|
33
|
+
"Scan Error [#{@pos}]: #{@message}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Scanner < StringScanner
|
39
|
+
CDATA = /[^<]+/m
|
40
|
+
TAG_PARAMETER = /\s*([^\s=\/>]*)=((['"])(.*?)\3)/um
|
41
|
+
|
42
|
+
def initialize(callback, string)
|
43
|
+
@callback = callback
|
44
|
+
super(string)
|
45
|
+
end
|
46
|
+
|
47
|
+
def calculate_line_number(at = pos)
|
48
|
+
line_no = 1
|
49
|
+
line_offset = offset = 0
|
50
|
+
|
51
|
+
string.lines.each do |line|
|
52
|
+
line_offset = offset
|
53
|
+
offset += line.size
|
54
|
+
|
55
|
+
if offset >= at
|
56
|
+
return [line_no, line_offset, at - line_offset, offset, line]
|
57
|
+
end
|
58
|
+
|
59
|
+
line_no += 1
|
60
|
+
end
|
61
|
+
|
62
|
+
return nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse
|
66
|
+
until eos?
|
67
|
+
pos = self.pos
|
68
|
+
|
69
|
+
scan_cdata
|
70
|
+
scan_tag
|
71
|
+
|
72
|
+
if pos == self.pos
|
73
|
+
raise ScanError.new("Scanner didn't move", self)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def scan_cdata
|
79
|
+
if scan(CDATA)
|
80
|
+
@callback.cdata(matched)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def scan_tag
|
85
|
+
if scan(/</)
|
86
|
+
if scan(/\//)
|
87
|
+
scan_tag_normal(CLOSED_TAG)
|
88
|
+
elsif scan(/!/)
|
89
|
+
scan_tag_comment
|
90
|
+
elsif scan(/\?/)
|
91
|
+
scan_tag_instruction
|
92
|
+
else
|
93
|
+
scan_tag_normal
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def scan_attributes
|
99
|
+
while scan(TAG_PARAMETER)
|
100
|
+
@callback.attribute(self[1], self[4])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def scan_tag_normal(begin_tag_type = OPENED_TAG)
|
105
|
+
if scan(/[^\s\/>]+/)
|
106
|
+
@callback.begin_tag(matched, begin_tag_type)
|
107
|
+
|
108
|
+
scan(/\s*/)
|
109
|
+
|
110
|
+
scan_attributes
|
111
|
+
|
112
|
+
scan(/\s*/)
|
113
|
+
|
114
|
+
if scan(/\/>/)
|
115
|
+
if begin_tag_type == CLOSED_TAG
|
116
|
+
raise ScanError.new("Tag cannot be closed at both ends!", self)
|
117
|
+
else
|
118
|
+
@callback.finish_tag(begin_tag_type, CLOSED_TAG)
|
119
|
+
end
|
120
|
+
elsif scan(/>/)
|
121
|
+
@callback.finish_tag(begin_tag_type, OPENED_TAG)
|
122
|
+
else
|
123
|
+
raise ScanError.new("Invalid characters in tag!", self)
|
124
|
+
end
|
125
|
+
else
|
126
|
+
raise ScanError.new("Invalid tag!", self)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def scan_tag_comment
|
131
|
+
if scan(/--/)
|
132
|
+
if scan_until(/(.*?)-->/m)
|
133
|
+
@callback.comment("--" + self[1] + "--")
|
134
|
+
else
|
135
|
+
raise ScanError.new("Comment is not closed!", self)
|
136
|
+
end
|
137
|
+
else
|
138
|
+
if scan_until(/(.*?)>/)
|
139
|
+
@callback.comment(self[1])
|
140
|
+
else
|
141
|
+
raise ScanError.new("Comment is not closed!", self)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def scan_tag_instruction
|
147
|
+
if scan_until(/(.*)\?>/)
|
148
|
+
@callback.instruction(self[1])
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|