utopia 1.2.1 → 1.2.2
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.
- checksums.yaml +4 -4
- data/Gemfile +5 -0
- data/ext/utopia/xnode/fast_scanner/extconf.rb +6 -0
- data/ext/utopia/xnode/fast_scanner/parser.c +289 -0
- data/lib/utopia/controller.rb +6 -5
- data/lib/utopia/controller/base.rb +6 -11
- data/lib/utopia/controller/rewrite.rb +7 -1
- data/lib/utopia/http.rb +5 -1
- data/lib/utopia/version.rb +1 -1
- data/spec/utopia/controller/middleware_spec.rb +6 -0
- data/spec/utopia/controller/middleware_spec/empty/controller.rb +0 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c561c449df8eaaff74066cd697d74d59144eed35
|
4
|
+
data.tar.gz: 31e712f26e682f8f6f002b3f7f425ef8b68199f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 021df2ac5a3caddcb2c6a1e3951660670bc8e6244f6457791a15a3b93f377495270604575d4ee75b06850a26ef6f2ee56019bc7af6a5fdc44d8c48e2cc10c911
|
7
|
+
data.tar.gz: 1f30b81c891cebf3a35a5c9536569b18553f7306d32f119967b385c66dd230ae020ac37c30672c6c2763fd93c452d7984c5ef6baf3c85acf0f2faaab2e5ff1d0
|
data/Gemfile
CHANGED
@@ -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/controller.rb
CHANGED
@@ -53,6 +53,8 @@ module Utopia
|
|
53
53
|
if options[:cache_controllers]
|
54
54
|
@controllers = Concurrent::Map.new
|
55
55
|
end
|
56
|
+
|
57
|
+
self.freeze
|
56
58
|
end
|
57
59
|
|
58
60
|
attr :app
|
@@ -69,12 +71,11 @@ module Utopia
|
|
69
71
|
load_controller_file(path)
|
70
72
|
end
|
71
73
|
else
|
72
|
-
|
74
|
+
load_controller_file(path)
|
73
75
|
end
|
74
76
|
end
|
75
77
|
|
76
|
-
def load_controller_file(
|
77
|
-
uri_path = path
|
78
|
+
def load_controller_file(uri_path)
|
78
79
|
base_path = File.join(@root, uri_path.components)
|
79
80
|
|
80
81
|
controller_path = File.join(base_path, CONTROLLER_RB)
|
@@ -84,10 +85,10 @@ module Utopia
|
|
84
85
|
klass = Class.new(Base)
|
85
86
|
|
86
87
|
# base_path is expected to be a string representing a filesystem path:
|
87
|
-
klass.const_set(:BASE_PATH, base_path)
|
88
|
+
klass.const_set(:BASE_PATH, base_path.freeze)
|
88
89
|
|
89
90
|
# uri_path is expected to be an instance of Path:
|
90
|
-
klass.const_set(:URI_PATH, uri_path)
|
91
|
+
klass.const_set(:URI_PATH, uri_path.dup.freeze)
|
91
92
|
|
92
93
|
klass.const_set(:CONTROLLER, self)
|
93
94
|
|
@@ -49,14 +49,6 @@ module Utopia
|
|
49
49
|
path.dirname == uri_path
|
50
50
|
end
|
51
51
|
|
52
|
-
def patterns
|
53
|
-
@patterns ||= []
|
54
|
-
end
|
55
|
-
|
56
|
-
def match(pattern, &block)
|
57
|
-
patterns << [pattern, block]
|
58
|
-
end
|
59
|
-
|
60
52
|
def actions
|
61
53
|
@actions ||= Action.new
|
62
54
|
end
|
@@ -70,9 +62,12 @@ module Utopia
|
|
70
62
|
end
|
71
63
|
|
72
64
|
def lookup(path)
|
73
|
-
|
74
|
-
|
75
|
-
|
65
|
+
if @actions
|
66
|
+
relative_path = (path - uri_path).to_a
|
67
|
+
return @actions.select(relative_path)
|
68
|
+
else
|
69
|
+
[]
|
70
|
+
end
|
76
71
|
end
|
77
72
|
end
|
78
73
|
|
@@ -110,12 +110,18 @@ module Utopia
|
|
110
110
|
def rewrite
|
111
111
|
@rewriter ||= Rewriter.new
|
112
112
|
end
|
113
|
+
|
114
|
+
def rewrite_request(controller, request, path)
|
115
|
+
if @rewriter
|
116
|
+
@rewriter.invoke!(controller, request, path)
|
117
|
+
end
|
118
|
+
end
|
113
119
|
end
|
114
120
|
|
115
121
|
# Rewrite the path before processing the request if possible.
|
116
122
|
def passthrough(request, path)
|
117
123
|
catch_response do
|
118
|
-
self.class.
|
124
|
+
self.class.rewrite_request(self, request, path)
|
119
125
|
end || super
|
120
126
|
end
|
121
127
|
end
|
data/lib/utopia/http.rb
CHANGED
@@ -22,6 +22,8 @@ require 'rack'
|
|
22
22
|
|
23
23
|
module Utopia
|
24
24
|
module HTTP
|
25
|
+
# A list of commonly used HTTP status codes.
|
26
|
+
# For help choosing the right status code, see http://racksburg.com/choosing-an-http-status-code/
|
25
27
|
STATUS_CODES = {
|
26
28
|
:success => 200,
|
27
29
|
:created => 201,
|
@@ -44,7 +46,8 @@ module Utopia
|
|
44
46
|
:unavailable => 503
|
45
47
|
}
|
46
48
|
|
47
|
-
#
|
49
|
+
# A list of human readable descriptions for a given status code.
|
50
|
+
# For a more detailed description, see https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
48
51
|
STATUS_DESCRIPTIONS = {
|
49
52
|
200 => 'OK'.freeze,
|
50
53
|
201 => 'Created'.freeze,
|
@@ -80,6 +83,7 @@ module Utopia
|
|
80
83
|
CONTENT_TYPE = 'Content-Type'.freeze
|
81
84
|
LOCATION = 'Location'.freeze
|
82
85
|
|
86
|
+
# A small HTTP status wrapper that verifies the status code within a given range.
|
83
87
|
class Status
|
84
88
|
def initialize(code, valid_range = 100...600)
|
85
89
|
if code.is_a? Symbol
|
data/lib/utopia/version.rb
CHANGED
@@ -30,6 +30,12 @@ module Utopia::Controller::MiddlewareSpec
|
|
30
30
|
|
31
31
|
let(:app) {Rack::Builder.parse_file(File.expand_path('../middleware_spec.ru', __FILE__)).first}
|
32
32
|
|
33
|
+
it "should successfully call empty controller" do
|
34
|
+
get "/empty/index"
|
35
|
+
|
36
|
+
expect(last_response.status).to be == 404
|
37
|
+
end
|
38
|
+
|
33
39
|
it "should successfully call the controller method" do
|
34
40
|
get "/controller/hello-world"
|
35
41
|
|
File without changes
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: utopia
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-12-
|
11
|
+
date: 2015-12-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: trenni
|
@@ -169,6 +169,8 @@ files:
|
|
169
169
|
- README.md
|
170
170
|
- Rakefile
|
171
171
|
- bin/utopia
|
172
|
+
- ext/utopia/xnode/fast_scanner/extconf.rb
|
173
|
+
- ext/utopia/xnode/fast_scanner/parser.c
|
172
174
|
- lib/utopia.rb
|
173
175
|
- lib/utopia/content.rb
|
174
176
|
- lib/utopia/content/link.rb
|
@@ -258,6 +260,7 @@ files:
|
|
258
260
|
- spec/utopia/controller/middleware_spec/controller/controller.rb
|
259
261
|
- spec/utopia/controller/middleware_spec/controller/index.xnode
|
260
262
|
- spec/utopia/controller/middleware_spec/controller/nested/controller.rb
|
263
|
+
- spec/utopia/controller/middleware_spec/empty/controller.rb
|
261
264
|
- spec/utopia/controller/rewrite_spec.rb
|
262
265
|
- spec/utopia/controller/sequence_spec.rb
|
263
266
|
- spec/utopia/exception_handler_spec.rb
|
@@ -346,6 +349,7 @@ test_files:
|
|
346
349
|
- spec/utopia/controller/middleware_spec/controller/controller.rb
|
347
350
|
- spec/utopia/controller/middleware_spec/controller/index.xnode
|
348
351
|
- spec/utopia/controller/middleware_spec/controller/nested/controller.rb
|
352
|
+
- spec/utopia/controller/middleware_spec/empty/controller.rb
|
349
353
|
- spec/utopia/controller/rewrite_spec.rb
|
350
354
|
- spec/utopia/controller/sequence_spec.rb
|
351
355
|
- spec/utopia/exception_handler_spec.rb
|