utopia 0.9.17

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