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,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