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