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.
Files changed (37) hide show
  1. data/ext/utopia/xnode/fast_scanner/extconf.rb +6 -0
  2. data/ext/utopia/xnode/fast_scanner/parser.c +289 -0
  3. data/lib/utopia.rb +2 -0
  4. data/lib/utopia/etanni.rb +96 -0
  5. data/lib/utopia/extensions.rb +87 -0
  6. data/lib/utopia/link.rb +243 -0
  7. data/lib/utopia/middleware.rb +33 -0
  8. data/lib/utopia/middleware/all.rb +24 -0
  9. data/lib/utopia/middleware/benchmark.rb +47 -0
  10. data/lib/utopia/middleware/content.rb +139 -0
  11. data/lib/utopia/middleware/content/node.rb +363 -0
  12. data/lib/utopia/middleware/controller.rb +198 -0
  13. data/lib/utopia/middleware/directory_index.rb +54 -0
  14. data/lib/utopia/middleware/localization.rb +94 -0
  15. data/lib/utopia/middleware/localization/name.rb +64 -0
  16. data/lib/utopia/middleware/logger.rb +68 -0
  17. data/lib/utopia/middleware/redirector.rb +171 -0
  18. data/lib/utopia/middleware/requester.rb +116 -0
  19. data/lib/utopia/middleware/static.rb +186 -0
  20. data/lib/utopia/path.rb +193 -0
  21. data/lib/utopia/response_helper.rb +22 -0
  22. data/lib/utopia/session/encrypted_cookie.rb +115 -0
  23. data/lib/utopia/tag.rb +84 -0
  24. data/lib/utopia/tags.rb +32 -0
  25. data/lib/utopia/tags/all.rb +25 -0
  26. data/lib/utopia/tags/env.rb +24 -0
  27. data/lib/utopia/tags/fortune.rb +20 -0
  28. data/lib/utopia/tags/gallery.rb +175 -0
  29. data/lib/utopia/tags/google_analytics.rb +37 -0
  30. data/lib/utopia/tags/node.rb +24 -0
  31. data/lib/utopia/tags/override.rb +28 -0
  32. data/lib/utopia/time_store.rb +102 -0
  33. data/lib/utopia/version.rb +24 -0
  34. data/lib/utopia/xnode.rb +17 -0
  35. data/lib/utopia/xnode/processor.rb +97 -0
  36. data/lib/utopia/xnode/scanner.rb +153 -0
  37. 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
@@ -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