utopia 0.10.0 → 0.11.0
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/.gitignore +17 -0
- data/.travis.yml +9 -0
- data/Gemfile +8 -0
- data/README.md +72 -0
- data/Rakefile +9 -0
- data/bin/utopia +42 -13
- data/lib/utopia/extensions/array.rb +19 -3
- data/lib/utopia/extensions/date.rb +19 -3
- data/lib/utopia/extensions/hash.rb +19 -3
- data/lib/utopia/extensions/maybe.rb +19 -0
- data/lib/utopia/extensions/rack.rb +21 -3
- data/lib/utopia/extensions/regexp.rb +19 -3
- data/lib/utopia/extensions/string.rb +19 -3
- data/lib/utopia/http.rb +56 -0
- data/lib/utopia/link.rb +19 -3
- data/lib/utopia/middleware/all.rb +19 -3
- data/lib/utopia/middleware/content/node.rb +21 -5
- data/lib/utopia/middleware/content/processor.rb +118 -0
- data/lib/utopia/middleware/content.rb +22 -7
- data/lib/utopia/middleware/controller.rb +19 -7
- data/lib/utopia/middleware/directory_index.rb +19 -3
- data/lib/utopia/middleware/localization/name.rb +19 -3
- data/lib/utopia/middleware/localization.rb +19 -3
- data/lib/utopia/middleware/logger.rb +19 -3
- data/lib/utopia/middleware/redirector.rb +19 -5
- data/lib/utopia/middleware/requester.rb +19 -3
- data/lib/utopia/middleware/static.rb +20 -6
- data/lib/utopia/middleware.rb +22 -5
- data/lib/utopia/path.rb +26 -6
- data/lib/utopia/session/encrypted_cookie.rb +19 -3
- data/lib/utopia/setup/Gemfile +8 -0
- data/lib/utopia/setup/config.ru +4 -13
- data/lib/utopia/setup.rb +25 -4
- data/lib/utopia/tag.rb +45 -24
- data/lib/utopia/tags/all.rb +19 -3
- data/lib/utopia/tags/deferred.rb +19 -3
- data/lib/utopia/tags/environment.rb +29 -0
- data/lib/utopia/tags/node.rb +19 -3
- data/lib/utopia/tags/override.rb +19 -3
- data/lib/utopia/tags.rb +19 -3
- data/lib/utopia/time_store.rb +19 -3
- data/lib/utopia/version.rb +20 -10
- data/lib/utopia.rb +20 -17
- data/test/content_root/_heading.xnode +1 -0
- data/test/content_root/index.xnode +1 -0
- data/test/test_content_middleware.rb +86 -0
- data/test/test_path.rb +32 -0
- data/utopia.gemspec +31 -0
- metadata +56 -42
- data/ext/utopia/xnode/fast_scanner/extconf.rb +0 -6
- data/ext/utopia/xnode/fast_scanner/parser.c +0 -289
- data/lib/utopia/http_status_codes.rb +0 -39
- data/lib/utopia/middleware/benchmark.rb +0 -29
- data/lib/utopia/middleware/filter.rb +0 -35
- data/lib/utopia/tags/env.rb +0 -13
- data/lib/utopia/tags/fortune.rb +0 -9
- data/lib/utopia/tags/gallery.rb +0 -275
- data/lib/utopia/tags/google_analytics.rb +0 -21
- data/lib/utopia/trenni.rb +0 -149
- data/lib/utopia/xnode/processor.rb +0 -86
- data/lib/utopia/xnode/scanner.rb +0 -159
- data/lib/utopia/xnode.rb +0 -6
@@ -1,289 +0,0 @@
|
|
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
|
-
}
|
@@ -1,39 +0,0 @@
|
|
1
|
-
# This file is part of the "Utopia Framework" project, and is released under the MIT license.
|
2
|
-
# Copyright 2010 Samuel Williams. All rights reserved.
|
3
|
-
# See <utopia.rb> for licensing details.
|
4
|
-
|
5
|
-
module Utopia
|
6
|
-
|
7
|
-
HTTP_STATUS_CODES = {
|
8
|
-
:success => 200,
|
9
|
-
:created => 201,
|
10
|
-
:accepted => 202,
|
11
|
-
:moved => 301,
|
12
|
-
:found => 302,
|
13
|
-
:see_other => 303,
|
14
|
-
:not_modified => 304,
|
15
|
-
:redirect => 307,
|
16
|
-
:bad_request => 400,
|
17
|
-
:unauthorized => 401,
|
18
|
-
:forbidden => 403,
|
19
|
-
:not_found => 404,
|
20
|
-
:unsupported_method => 405,
|
21
|
-
:gone => 410,
|
22
|
-
:teapot => 418,
|
23
|
-
:error => 500,
|
24
|
-
:unimplemented => 501,
|
25
|
-
:unavailable => 503
|
26
|
-
}
|
27
|
-
|
28
|
-
HTTP_STATUS_DESCRIPTIONS = {
|
29
|
-
400 => "Bad Request",
|
30
|
-
401 => "Permission Denied",
|
31
|
-
403 => "Access Forbidden",
|
32
|
-
404 => "Resource Not Found",
|
33
|
-
405 => "Unsupported Method",
|
34
|
-
416 => "Byte range unsatisfiable",
|
35
|
-
500 => "Internal Server Error",
|
36
|
-
501 => "Not Implemented",
|
37
|
-
503 => "Service Unavailable"
|
38
|
-
}
|
39
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# This file is part of the "Utopia Framework" project, and is released under the MIT license.
|
2
|
-
# Copyright 2010 Samuel Williams. All rights reserved.
|
3
|
-
# See <utopia.rb> for licensing details.
|
4
|
-
|
5
|
-
require 'utopia/middleware'
|
6
|
-
|
7
|
-
module Utopia
|
8
|
-
module Middleware
|
9
|
-
|
10
|
-
class Benchmark
|
11
|
-
def initialize(app, options = {})
|
12
|
-
@app = app
|
13
|
-
@tag = options[:tag] || "{{benchmark}}"
|
14
|
-
end
|
15
|
-
|
16
|
-
def call(env)
|
17
|
-
start = Time.now
|
18
|
-
response = @app.call(env)
|
19
|
-
finish = Time.now
|
20
|
-
|
21
|
-
time = "%0.4f" % (finish - start)
|
22
|
-
env['rack.errors'].puts "Benchmark: Request #{env["PATH_INFO"]} took #{time}s"
|
23
|
-
|
24
|
-
return response
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
end
|
29
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
# This file is part of the "Utopia Framework" project, and is released under the MIT license.
|
2
|
-
# Copyright 2010 Samuel Williams. All rights reserved.
|
3
|
-
# See <utopia.rb> for licensing details.
|
4
|
-
require 'utopia/middleware'
|
5
|
-
|
6
|
-
module Utopia
|
7
|
-
module Middleware
|
8
|
-
|
9
|
-
# This class filters a incoming request and only executes a given block if it matches the given filter path.
|
10
|
-
class Filter
|
11
|
-
def initialize(app, filter, &block)
|
12
|
-
@app = app
|
13
|
-
@filter = filter
|
14
|
-
branch = Rack::Builder.new(&block)
|
15
|
-
branch.run(@app)
|
16
|
-
@process = branch.to_app
|
17
|
-
end
|
18
|
-
|
19
|
-
def applicable(request)
|
20
|
-
return request.path.index(@filter) != nil
|
21
|
-
end
|
22
|
-
|
23
|
-
def call(env)
|
24
|
-
request = Rack::Request.new(env)
|
25
|
-
|
26
|
-
if applicable(request)
|
27
|
-
@process.call(env)
|
28
|
-
else
|
29
|
-
@app.call(env)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
35
|
-
end
|
data/lib/utopia/tags/env.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
# This file is part of the "Utopia Framework" project, and is released under the MIT license.
|
2
|
-
# Copyright 2010 Samuel Williams. All rights reserved.
|
3
|
-
# See <utopia.rb> for licensing details.
|
4
|
-
|
5
|
-
require 'utopia/tags'
|
6
|
-
|
7
|
-
Utopia::Tags.create("env") do |transaction, state|
|
8
|
-
only = state[:only].split(",").collect(&:to_sym) rescue []
|
9
|
-
|
10
|
-
if defined?(UTOPIA_ENV) && only.include?(UTOPIA_ENV)
|
11
|
-
transaction.parse_xml(state.content)
|
12
|
-
end
|
13
|
-
end
|
data/lib/utopia/tags/fortune.rb
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
# This file is part of the "Utopia Framework" project, and is released under the MIT license.
|
2
|
-
# Copyright 2010 Samuel Williams. All rights reserved.
|
3
|
-
# See <utopia.rb> for licensing details.
|
4
|
-
|
5
|
-
require 'utopia/tags'
|
6
|
-
|
7
|
-
Utopia::Tags.create("fortune") do |transaction, state|
|
8
|
-
"<pre>#{`fortune`.to_html}</pre>"
|
9
|
-
end
|
data/lib/utopia/tags/gallery.rb
DELETED
@@ -1,275 +0,0 @@
|
|
1
|
-
# This file is part of the "Utopia Framework" project, and is released under the MIT license.
|
2
|
-
# Copyright 2010 Samuel Williams. All rights reserved.
|
3
|
-
# See <utopia.rb> for licensing details.
|
4
|
-
|
5
|
-
require 'utopia/tags'
|
6
|
-
|
7
|
-
require 'RMagick'
|
8
|
-
require 'fileutils'
|
9
|
-
|
10
|
-
class Utopia::Tags::Gallery
|
11
|
-
module Processes
|
12
|
-
class Thumbnail
|
13
|
-
def initialize(size = [800, 800])
|
14
|
-
@size = size
|
15
|
-
end
|
16
|
-
|
17
|
-
def call(img)
|
18
|
-
# Only resize an image if it is bigger than the given size.
|
19
|
-
if (img.columns > @size[0] || img.rows > @size[1])
|
20
|
-
img.resize_to_fit(*@size)
|
21
|
-
else
|
22
|
-
img
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def default_extension(path)
|
27
|
-
ext = path.original.extension
|
28
|
-
|
29
|
-
case ext
|
30
|
-
when /pdf/i
|
31
|
-
return "png"
|
32
|
-
else
|
33
|
-
return ext.downcase
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Resize the image to fit within the specified dimensions while retaining the aspect ratio of the original image. If necessary, crop the image in the larger dimension.
|
39
|
-
class CropThumbnail < Thumbnail
|
40
|
-
def call(img)
|
41
|
-
img.resize_to_fill(*@size)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
class DocumentThumbnail < Thumbnail
|
46
|
-
def call(img)
|
47
|
-
img = super(img)
|
48
|
-
|
49
|
-
shadow = img.dup
|
50
|
-
|
51
|
-
shadow = shadow.colorize(1, 1, 1, 'gray50')
|
52
|
-
shadow.background_color = 'transparent'
|
53
|
-
shadow.border!(10, 10, 'transparent')
|
54
|
-
|
55
|
-
shadow = shadow.gaussian_blur_channel(5, 5, Magick::AlphaChannel)
|
56
|
-
|
57
|
-
shadow.composite(img, 5, 5, Magick::OverCompositeOp)
|
58
|
-
end
|
59
|
-
|
60
|
-
def default_extension(path)
|
61
|
-
return "png"
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
class PhotoThumbnail < Thumbnail
|
66
|
-
def call(img)
|
67
|
-
img = super(img)
|
68
|
-
|
69
|
-
shadow = img.dup
|
70
|
-
|
71
|
-
shadow = shadow.colorize(1, 1, 1, '#999999ff')
|
72
|
-
shadow.background_color = 'transparent'
|
73
|
-
shadow.border!(10, 10, '#99999900')
|
74
|
-
|
75
|
-
shadow = shadow.gaussian_blur_channel(5, 5, Magick::AlphaChannel)
|
76
|
-
|
77
|
-
shadow.composite(img, 5, 5, Magick::OverCompositeOp)
|
78
|
-
end
|
79
|
-
|
80
|
-
def default_extension(path)
|
81
|
-
return "png"
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
CACHE_DIR = "_cache"
|
87
|
-
PROCESSES = {
|
88
|
-
:pdf_thumbnail => Processes::DocumentThumbnail.new([300, 300]),
|
89
|
-
:photo_thumbnail => Processes::PhotoThumbnail.new([300, 300]),
|
90
|
-
:large => Processes::Thumbnail.new([800, 800]),
|
91
|
-
:square_thumbnail => Processes::CropThumbnail.new([300, 300]),
|
92
|
-
:thumbnail => Processes::Thumbnail.new([300, 300])
|
93
|
-
}
|
94
|
-
|
95
|
-
class ImagePath
|
96
|
-
def initialize(original_path)
|
97
|
-
@original_path = original_path
|
98
|
-
@cache_root = @original_path.dirname + CACHE_DIR
|
99
|
-
|
100
|
-
@extensions = {}
|
101
|
-
end
|
102
|
-
|
103
|
-
attr :cache_root
|
104
|
-
attr :extensions
|
105
|
-
|
106
|
-
def original
|
107
|
-
@original_path
|
108
|
-
end
|
109
|
-
|
110
|
-
def self.append_suffix(name, suffix, extension = nil)
|
111
|
-
components = name.split(".")
|
112
|
-
|
113
|
-
components.insert(-2, suffix)
|
114
|
-
|
115
|
-
if (extension)
|
116
|
-
components[-1] = extension
|
117
|
-
end
|
118
|
-
|
119
|
-
return components.join(".")
|
120
|
-
end
|
121
|
-
|
122
|
-
def processed(process = nil)
|
123
|
-
if process
|
124
|
-
name = @original_path.basename
|
125
|
-
return cache_root + ImagePath.append_suffix(name, process.to_s, @extensions[process.to_sym])
|
126
|
-
else
|
127
|
-
return @original_path
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
def to_html(process = nil)
|
132
|
-
Tag.new("img", {"src" => path(process)}).to_html
|
133
|
-
end
|
134
|
-
|
135
|
-
def to_s
|
136
|
-
@original_path.to_s
|
137
|
-
end
|
138
|
-
|
139
|
-
def method_missing(name, *args)
|
140
|
-
return processed(name.to_s)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
class ImageMetadata
|
145
|
-
def initialize(metadata)
|
146
|
-
@metadata = metadata
|
147
|
-
end
|
148
|
-
|
149
|
-
attr :metadata
|
150
|
-
|
151
|
-
def [] (key)
|
152
|
-
@metadata[key.to_s]
|
153
|
-
end
|
154
|
-
|
155
|
-
def to_s
|
156
|
-
@metadata['caption'] || ''
|
157
|
-
end
|
158
|
-
|
159
|
-
# A bit of a hack to ease migration.
|
160
|
-
def to_html
|
161
|
-
to_s.to_html
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
def initialize(node, path)
|
166
|
-
@node = node
|
167
|
-
@path = path
|
168
|
-
end
|
169
|
-
|
170
|
-
def metadata
|
171
|
-
metadata_path = @node.local_path(@path + "gallery.yaml")
|
172
|
-
|
173
|
-
if File.exist? metadata_path
|
174
|
-
return YAML::load(File.read(metadata_path))
|
175
|
-
else
|
176
|
-
return {}
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
def images(options = {})
|
181
|
-
options[:filter] ||= /(jpg|png)$/i
|
182
|
-
|
183
|
-
paths = []
|
184
|
-
local_path = @node.local_path(@path)
|
185
|
-
|
186
|
-
Dir.entries(local_path).each do |filename|
|
187
|
-
next unless filename.match(options[:filter])
|
188
|
-
|
189
|
-
fullpath = File.join(local_path, filename)
|
190
|
-
|
191
|
-
paths << ImagePath.new(@path + filename)
|
192
|
-
end
|
193
|
-
|
194
|
-
if options[:process]
|
195
|
-
paths.each do |path|
|
196
|
-
processed_image(path, options[:process])
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
return paths
|
201
|
-
end
|
202
|
-
|
203
|
-
def processed_image(image_path, processes)
|
204
|
-
# Create the local cache directory if it doesn't exist already
|
205
|
-
local_cache_path = @node.local_path(image_path.cache_root)
|
206
|
-
|
207
|
-
unless File.exist? local_cache_path
|
208
|
-
FileUtils.mkdir local_cache_path
|
209
|
-
end
|
210
|
-
|
211
|
-
# Calculate the new name for the processed image
|
212
|
-
local_original_path = @node.local_path(image_path.original)
|
213
|
-
|
214
|
-
if processes.kind_of? String
|
215
|
-
processes = processes.split(",").collect{|p| p.split(":")}
|
216
|
-
end
|
217
|
-
|
218
|
-
processes.each do |process_name, extension|
|
219
|
-
process_name = process_name.to_sym
|
220
|
-
|
221
|
-
process = PROCESSES[process_name]
|
222
|
-
extension ||= process.default_extension(image_path)
|
223
|
-
|
224
|
-
image_path.extensions[process_name] = extension if extension
|
225
|
-
|
226
|
-
local_processed_path = @node.local_path(image_path.processed(process_name))
|
227
|
-
|
228
|
-
unless File.exists? local_processed_path
|
229
|
-
image = Magick::ImageList.new(local_original_path)
|
230
|
-
image.scene = 0
|
231
|
-
|
232
|
-
processed_image = process.call(image)
|
233
|
-
processed_image.write(local_processed_path)
|
234
|
-
|
235
|
-
# Run GC to free up any memory.
|
236
|
-
processed_image = nil
|
237
|
-
GC.start if defined? GC
|
238
|
-
end
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
def self.call(transaction, state)
|
243
|
-
gallery = new(transaction.end_tags[-2].node, Utopia::Path.create(state["path"] || "./"))
|
244
|
-
metadata = gallery.metadata
|
245
|
-
metadata.default = {}
|
246
|
-
|
247
|
-
tag_name = state["tag"] || "img"
|
248
|
-
gallery_class = state["class"] || "gallery"
|
249
|
-
|
250
|
-
options = {}
|
251
|
-
options[:process] = state["process"]
|
252
|
-
options[:filter] = Regexp.new("(#{state["filetypes"]})$", "i") if state["filetypes"]
|
253
|
-
|
254
|
-
filter = Regexp.new(state["filter"], Regexp::IGNORECASE) if state["filter"]
|
255
|
-
|
256
|
-
transaction.tag("div", "class" => gallery_class) do |node|
|
257
|
-
images = gallery.images(options).sort do |a, b|
|
258
|
-
if (metadata[a.original.basename]["order"] && metadata[b.original.basename]["order"])
|
259
|
-
metadata[a.original.basename]["order"] <=> metadata[b.original.basename]["order"]
|
260
|
-
else
|
261
|
-
a.original.basename <=> b.original.basename
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
images.each do |path|
|
266
|
-
next if filter and !filter.match(path.original.basename)
|
267
|
-
|
268
|
-
alt = ImageMetadata.new(metadata[path.original.basename])
|
269
|
-
transaction.tag(tag_name, "src" => path, "alt" => alt)
|
270
|
-
end
|
271
|
-
end
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
Utopia::Tags.register("gallery", Utopia::Tags::Gallery)
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# This file is part of the "Utopia Framework" project, and is released under the MIT license.
|
2
|
-
# Copyright 2010 Samuel Williams. All rights reserved.
|
3
|
-
# See <utopia.rb> for licensing details.
|
4
|
-
|
5
|
-
require 'utopia/tags'
|
6
|
-
|
7
|
-
Utopia::Tags.create("google_analytics") do |transaction, state|
|
8
|
-
html = <<EOF
|
9
|
-
<script type="text/javascript">
|
10
|
-
var _gaq = _gaq || []; _gaq.push(['_setAccount', #{state[:id].to_quoted_string}]); _gaq.push(['_trackPageview']);
|
11
|
-
(function() {
|
12
|
-
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
13
|
-
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
14
|
-
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
15
|
-
})();
|
16
|
-
</script>
|
17
|
-
EOF
|
18
|
-
|
19
|
-
|
20
|
-
transaction.cdata(html)
|
21
|
-
end
|