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,6 @@
1
+
2
+ require 'mkmf'
3
+
4
+ dir_config('fast_scanner')
5
+
6
+ create_makefile("fast_scanner")
@@ -0,0 +1,289 @@
1
+ #include "ruby.h"
2
+
3
+ #define OPENED_TAG 0
4
+ #define CLOSED_TAG 1
5
+
6
+ static VALUE rb_XNode;
7
+ static VALUE rb_XNode_ScanError;
8
+ static VALUE rb_XNode_Scanner;
9
+
10
+ static ID rb_XNode_CDATA_Callback;
11
+ static ID rb_XNode_BeginTag_Callback;
12
+ static ID rb_XNode_FinishTag_Callback;
13
+ static ID rb_XNode_Attribute_Callback;
14
+ static ID rb_XNode_Comment_Callback;
15
+ static ID rb_XNode_Instruction_Callback;
16
+ static ID rb_XNode_Comment_Callback;
17
+
18
+ #define NewObject(type) (type*)malloc(sizeof(type))
19
+
20
+ typedef struct {
21
+ VALUE delegate;
22
+ VALUE content;
23
+ } XNode_Scanner;
24
+
25
+ typedef char Character;
26
+ typedef Character * Iterator;
27
+
28
+ static void XNode_Scanner_Mark(XNode_Scanner * scanner) {
29
+ rb_gc_mark(scanner->delegate);
30
+ rb_gc_mark(scanner->content);
31
+ }
32
+
33
+ static void XNode_Scanner_Free(XNode_Scanner * scanner) {
34
+ free(scanner);
35
+ }
36
+
37
+ static VALUE XNode_Scanner_Allocate(VALUE klass) {
38
+ XNode_Scanner * scanner = NewObject(XNode_Scanner);
39
+
40
+ return Data_Wrap_Struct(klass, XNode_Scanner_Mark, XNode_Scanner_Free, scanner);
41
+ }
42
+
43
+ static VALUE XNode_Scanner_Initialize(VALUE self, VALUE delegate, VALUE content) {
44
+ Check_Type(content, T_STRING);
45
+
46
+ XNode_Scanner * scanner;
47
+
48
+ Data_Get_Struct(self, XNode_Scanner, scanner);
49
+
50
+ scanner->delegate = delegate;
51
+ scanner->content = content;
52
+
53
+ return Qnil;
54
+ }
55
+
56
+ static VALUE rb_str_from_iterators(Iterator start, Iterator end) {
57
+ return rb_str_new(start, end - start);
58
+ }
59
+
60
+ static int is_whitespace(Iterator i) {
61
+ return (*i == ' ' || *i == '\r' || *i == '\n' || *i == '\t');
62
+ }
63
+
64
+ static int is_tag_character(Iterator i) {
65
+ return (*i == '<' || *i == '>' || *i == '/');
66
+ }
67
+
68
+ static int is_tag_name(Iterator i) {
69
+ return !(is_whitespace(i) || is_tag_character(i));
70
+ }
71
+
72
+ static Iterator expect_character(XNode_Scanner * scanner, Iterator start, Iterator end, Character c) {
73
+ if (start >= end || *start != c) {
74
+ VALUE message = rb_str_new2("Expected Character ");
75
+ rb_str_cat(message, &c, 1);
76
+ VALUE exception = rb_exc_new3(rb_XNode_ScanError, message);
77
+ rb_exc_raise(exception);
78
+ }
79
+
80
+ return start + 1;
81
+ }
82
+
83
+ static Iterator skip_whitespace(Iterator start, Iterator end) {
84
+ while (start < end) {
85
+ if (!is_whitespace(start))
86
+ break;
87
+
88
+ ++start;
89
+ }
90
+
91
+ return start;
92
+ }
93
+
94
+ static Iterator XNode_Scanner_Parse_CDATA(XNode_Scanner * scanner, Iterator start, Iterator end) {
95
+ Iterator cdata_start = start;
96
+
97
+ while (start < end && *start != '<') {
98
+ ++start;
99
+ }
100
+
101
+ Iterator cdata_end = start;
102
+
103
+ if (cdata_start != cdata_end) {
104
+ VALUE cdata = rb_str_from_iterators(cdata_start, cdata_end);
105
+ rb_funcall(scanner->delegate, rb_XNode_CDATA_Callback, 1, cdata);
106
+ }
107
+
108
+ return start;
109
+ }
110
+
111
+ static Iterator XNode_Scanner_Parse_Attributes(XNode_Scanner * scanner, Iterator start, Iterator end) {
112
+ while (start < end && !is_tag_character(start)) {
113
+ start = skip_whitespace(start, end);
114
+
115
+ Iterator attribute_name_start = start;
116
+
117
+ while (start < end && *start != '=') {
118
+ ++start;
119
+ }
120
+
121
+ Iterator attribute_name_end = start;
122
+
123
+ start = expect_character(scanner, start, end, '=');
124
+ start = expect_character(scanner, start, end, '"');
125
+
126
+ Iterator attribute_value_start = start;
127
+
128
+ while (start < end && *start != '"') {
129
+ ++start;
130
+ }
131
+
132
+ Iterator attribute_value_end = start;
133
+ start = expect_character(scanner, start, end, '"');
134
+
135
+ VALUE attribute_name = rb_str_from_iterators(attribute_name_start, attribute_name_end);
136
+ VALUE attribute_value = rb_str_from_iterators(attribute_value_start, attribute_value_end);
137
+ rb_funcall(scanner->delegate, rb_XNode_Attribute_Callback, 2, attribute_name, attribute_value);
138
+
139
+ start = skip_whitespace(start, end);
140
+ }
141
+
142
+ return start;
143
+ }
144
+
145
+ static Iterator XNode_Scanner_Parse_Tag_Normal(XNode_Scanner * scanner, Iterator start, Iterator end, int begin_tag_type) {
146
+ Iterator tag_name_start = start;
147
+ int finish_tag_type;
148
+
149
+ while (start < end && is_tag_name(start)) {
150
+ ++start;
151
+ }
152
+
153
+ Iterator tag_name_end = start;
154
+
155
+ VALUE tag_name = rb_str_from_iterators(tag_name_start, tag_name_end);
156
+ rb_funcall(scanner->delegate, rb_XNode_BeginTag_Callback, 2, tag_name, INT2FIX(begin_tag_type));
157
+
158
+ start = skip_whitespace(start, end);
159
+
160
+ if (!is_tag_character(start))
161
+ start = XNode_Scanner_Parse_Attributes(scanner, start, end);
162
+
163
+ if (*start == '/') {
164
+ if (begin_tag_type == CLOSED_TAG) {
165
+ VALUE exception = rb_exc_new2(rb_XNode_ScanError, "Tag cannot be closed at both ends!");
166
+ rb_exc_raise(exception);
167
+ }
168
+
169
+ finish_tag_type = CLOSED_TAG;
170
+ start += 2;
171
+ } else if (*start == '>') {
172
+ finish_tag_type = OPENED_TAG;
173
+ ++start;
174
+ }
175
+
176
+ rb_funcall(scanner->delegate, rb_XNode_FinishTag_Callback, 2, INT2FIX(begin_tag_type), INT2FIX(finish_tag_type));
177
+
178
+ return start;
179
+ }
180
+
181
+ static Iterator XNode_Scanner_Parse_Tag_Comment(XNode_Scanner * scanner, Iterator start, Iterator end) {
182
+ Iterator comment_start = start;
183
+
184
+ while (start < end && *start != '>') {
185
+ ++start;
186
+ }
187
+
188
+ Iterator comment_end = start;
189
+
190
+ start = expect_character(scanner, start, end, '>');
191
+
192
+ VALUE comment = rb_str_from_iterators(comment_start, comment_end);
193
+ rb_funcall(scanner->delegate, rb_XNode_Comment_Callback, 1, comment);
194
+
195
+ return start;
196
+ }
197
+
198
+ static Iterator XNode_Scanner_Parse_Tag_Instruction(XNode_Scanner * scanner, Iterator start, Iterator end) {
199
+ Iterator instruction_start = start;
200
+
201
+ while ((start+1) < end && *start != '?' && *(start+1) != '>') {
202
+ ++start;
203
+ }
204
+
205
+ Iterator instruction_end = start;
206
+
207
+ start = expect_character(scanner, start, end, '?');
208
+ start = expect_character(scanner, start, end, '>');
209
+
210
+ VALUE instruction = rb_str_from_iterators(instruction_start, instruction_end);
211
+ rb_funcall(scanner->delegate, rb_XNode_Instruction_Callback, 1, instruction);
212
+
213
+ return start;
214
+ }
215
+
216
+ static Iterator XNode_Scanner_Parse_Tag(XNode_Scanner * scanner, Iterator start, Iterator end) {
217
+ if (*start == '<') {
218
+ ++start;
219
+
220
+ if (*start == '/') {
221
+ ++start;
222
+ start = XNode_Scanner_Parse_Tag_Normal(scanner, start, end, CLOSED_TAG);
223
+ } else if (*start == '!') {
224
+ ++start;
225
+ start = XNode_Scanner_Parse_Tag_Comment(scanner, start, end);
226
+ } else if (*start == '?') {
227
+ ++start;
228
+ start = XNode_Scanner_Parse_Tag_Instruction(scanner, start, end);
229
+ } else {
230
+ start = XNode_Scanner_Parse_Tag_Normal(scanner, start, end, OPENED_TAG);
231
+ }
232
+ }
233
+
234
+ return start;
235
+ }
236
+
237
+ static Iterator XNode_Scanner_Parse_Document(XNode_Scanner * scanner) {
238
+ Iterator current, start, end;
239
+
240
+ start = RSTRING(scanner->content)->ptr;
241
+ end = start + RSTRING(scanner->content)->len;
242
+
243
+ while (start < end) {
244
+ current = start;
245
+
246
+ current = XNode_Scanner_Parse_CDATA(scanner, current, end);
247
+ current = XNode_Scanner_Parse_Tag(scanner, current, end);
248
+
249
+ if (current == start) {
250
+ /* We did not parse anything! */
251
+ VALUE message = rb_str_new2("Parser Stuck at ");
252
+
253
+ int len = 10;
254
+ if (current + len > end)
255
+ len = end - current;
256
+
257
+ rb_str_cat(message, current, len);
258
+ VALUE exception = rb_exc_new3(rb_XNode_ScanError, message);
259
+ rb_exc_raise(exception);
260
+ }
261
+
262
+ start = current;
263
+ }
264
+ }
265
+
266
+ static VALUE XNode_Scanner_Parse(VALUE self) {
267
+ XNode_Scanner * scanner;
268
+
269
+ Data_Get_Struct(self, XNode_Scanner, scanner);
270
+
271
+ XNode_Scanner_Parse_Document(scanner);
272
+ }
273
+
274
+ void Init_xnode() {
275
+ rb_XNode = rb_define_module("XNode");
276
+ rb_XNode_ScanError = rb_define_class_under(rb_XNode, "ScanError", rb_eStandardError);
277
+ rb_XNode_Scanner = rb_define_class_under(rb_XNode, "Scanner", rb_cObject);
278
+
279
+ rb_define_alloc_func(rb_XNode_Scanner, XNode_Scanner_Allocate);
280
+ rb_define_method(rb_XNode_Scanner, "initialize", XNode_Scanner_Initialize, 2);
281
+ rb_define_method(rb_XNode_Scanner, "parse", XNode_Scanner_Parse, 0);
282
+
283
+ rb_XNode_CDATA_Callback = rb_intern("cdata");
284
+ rb_XNode_BeginTag_Callback = rb_intern("begin_tag");
285
+ rb_XNode_FinishTag_Callback = rb_intern("finish_tag");
286
+ rb_XNode_Attribute_Callback = rb_intern("attribute");
287
+ rb_XNode_Comment_Callback = rb_intern("comment");
288
+ rb_XNode_Instruction_Callback = rb_intern("instruction");
289
+ }
data/lib/utopia.rb ADDED
@@ -0,0 +1,2 @@
1
+
2
+
@@ -0,0 +1,96 @@
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
+ # [Etanni] Copyright (c) 2008 Michael Fellinger <m.fellinger@gmail.com>
17
+ #
18
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
19
+ # of this software and associated documentation files (the "Software"), to
20
+ # deal in the Software without restriction, including without limitation the
21
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
22
+ # sell copies of the Software, and to permit persons to whom the Software is
23
+ # furnished to do so, subject to the following conditions:
24
+ #
25
+ # The above copyright notice and this permission notice shall be included in
26
+ # all copies or substantial portions of the Software.
27
+ #
28
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
31
+ # THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
32
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
33
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34
+
35
+ require 'digest/md5'
36
+
37
+ module Utopia
38
+
39
+ class Etanni
40
+ SEPARATOR = Digest::MD5.hexdigest(Time.new.to_s)
41
+ START = "\n_out_ << <<#{SEPARATOR}.chomp!\n"
42
+ STOP = "\n#{SEPARATOR}\n"
43
+ REPLACEMENT = "#{STOP}\\1#{START}"
44
+
45
+ def initialize(template, compiled = false)
46
+ if compiled
47
+ @compiled = template
48
+ else
49
+ @template = template
50
+ compile!
51
+ end
52
+ end
53
+
54
+ def compile!
55
+ temp = @template.dup
56
+ temp.strip!
57
+ temp.gsub!(/<\?r\s+(.*?)\s+\?>/m, REPLACEMENT)
58
+ @compiled = "_out_ = [<<#{SEPARATOR}.chomp!]\n#{temp}#{STOP}_out_"
59
+ end
60
+
61
+ def result(binding, filename = '<Etanni>')
62
+ eval(@compiled, binding, filename).join
63
+ end
64
+
65
+ attr :compiled
66
+ end
67
+
68
+ class TemplateCache
69
+ CACHE_PREFIX = ".cache."
70
+ CACHE_ENABLED = true
71
+
72
+ def self.cache_path(path)
73
+ File.join(File.dirname(path), CACHE_PREFIX + File.basename(path))
74
+ end
75
+
76
+ def self.mtime(path)
77
+ File.symlink?(path) ? File.lstat(file_name).mtime : File.mtime(path)
78
+ end
79
+
80
+ def initialize(path, template_class = Etanni)
81
+ @path = path
82
+ @cache_path = TemplateCache.cache_path(@path)
83
+
84
+ if !File.exist?(@cache_path) || (TemplateCache.mtime(@path) > TemplateCache.mtime(@cache_path))
85
+ @template = template_class.new(File.read(@path))
86
+ File.open(@cache_path, "w") { |f| f.write(@template.compiled) }
87
+ else
88
+ @template = template_class.new(File.read(@cache_path), true)
89
+ end
90
+ end
91
+
92
+ def result(binding)
93
+ @template.result(binding, @cache_path)
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,87 @@
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 'active_support'
17
+
18
+ class Date
19
+ alias_method :old_cmp, :<=>
20
+
21
+ def <=> (other)
22
+ # Comparing a Date with something that has a time component truncates the time
23
+ # component, thus we need to check if the other object has a more exact comparison
24
+ # function.
25
+ if other.respond_to?(:hour)
26
+ return (other <=> self) * -1
27
+ else
28
+ old_cmp(other)
29
+ end
30
+ end
31
+ end
32
+
33
+ class String
34
+ HTML_ESCAPE = {"&" => "&amp;", "<" => "&lt;", ">" => "&gt;", "\"" => "&quot;"}
35
+ HTML_ESCAPE_PATTERN = Regexp.new("[" + Regexp.quote(HTML_ESCAPE.keys.join) + "]")
36
+
37
+ def to_html
38
+ gsub(HTML_ESCAPE_PATTERN){|c| HTML_ESCAPE[c]}
39
+ end
40
+
41
+ def to_title
42
+ (" " + self).gsub(/[ \-_](.)/){" " + $1.upcase}
43
+ end
44
+
45
+ def to_snake
46
+ self.gsub("::", "").gsub(/([A-Z]+)/){"_" + $1.downcase}.sub(/^_+/, "")
47
+ end
48
+ end
49
+
50
+ class Hash
51
+ def symbolize_keys
52
+ inject({}) do |options, (key, value)|
53
+ options[(key.to_sym rescue key) || key] = value
54
+ options
55
+ end
56
+ end
57
+ end
58
+
59
+ class Array
60
+ def find_index(&block)
61
+ each_with_index do |item, index|
62
+ if yield(item)
63
+ return index
64
+ end
65
+ end
66
+
67
+ return nil
68
+ end
69
+
70
+ def split_at(&block)
71
+ index = find_index(&block)
72
+
73
+ if index
74
+ return [self[0...index], self[index], self[index+1..-1]]
75
+ end
76
+
77
+ return [[], nil, []]
78
+ end
79
+ end
80
+
81
+ if defined? Rack
82
+ class Rack::Request
83
+ def self.new(*args)
84
+ super(*args)
85
+ end
86
+ end
87
+ end