utopia 1.2.1 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e53e0a1f210f0c4e8e14ff4f7e1cf21bbc705f18
4
- data.tar.gz: 3756ebee30e3b76f4712d7f129aecdf943d60931
3
+ metadata.gz: c561c449df8eaaff74066cd697d74d59144eed35
4
+ data.tar.gz: 31e712f26e682f8f6f002b3f7f425ef8b68199f1
5
5
  SHA512:
6
- metadata.gz: f751489f085bf36b52d6a0b842e127f56d1061d0050f1452ae9798d2deadd2fdef426c254a1ec15a7f06715ef01e56a5c78c2e5abea095f7cbd89c925d136776
7
- data.tar.gz: 679a51d33577028c5fdc27a66498617e9b5e4f764a483a387f9b005df33c6ded2439cebb8ba6f30be0a6c9d670694407289d22681553b916816328517dfa47e3
6
+ metadata.gz: 021df2ac5a3caddcb2c6a1e3951660670bc8e6244f6457791a15a3b93f377495270604575d4ee75b06850a26ef6f2ee56019bc7af6a5fdc44d8c48e2cc10c911
7
+ data.tar.gz: 1f30b81c891cebf3a35a5c9536569b18553f7306d32f119967b385c66dd230ae020ac37c30672c6c2763fd93c452d7984c5ef6baf3c85acf0f2faaab2e5ff1d0
data/Gemfile CHANGED
@@ -3,6 +3,11 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in utopia.gemspec
4
4
  gemspec
5
5
 
6
+ group :development do
7
+ gem 'pry'
8
+ gem 'pry-coolline'
9
+ end
10
+
6
11
  group :test do
7
12
  gem 'rack-test'
8
13
  gem 'simplecov'
@@ -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
+ }
@@ -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
- return load_controller_file(path)
74
+ load_controller_file(path)
73
75
  end
74
76
  end
75
77
 
76
- def load_controller_file(path)
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
- relative_path = (path - uri_path).to_a
74
-
75
- possible_actions = actions.select(relative_path)
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.rewrite.invoke!(self, request, path)
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
- # For a more detailed description see https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
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
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Utopia
22
- VERSION = "1.2.1"
22
+ VERSION = "1.2.2"
23
23
  end
@@ -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
 
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.1
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-09 00:00:00.000000000 Z
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