sinarey 1.0.4 → 1.0.5
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/README.md +3 -0
- data/demo/app.rb +26 -3
- data/demo/app2.rb +1 -2
- data/demo/config.ru +4 -1
- data/demo/notfound.rb +1 -2
- data/lib/sinarey/base.rb +1194 -1219
- data/lib/sinarey/router.rb +120 -125
- data/lib/sinarey/version.rb +3 -3
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d806350786262ea84053daf3d50fd560f8ed578
|
4
|
+
data.tar.gz: e1144b327ea25f94fe868ae852a805e5a51ad0ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 303a3809add2b65e12454e5f37a0a4597cc923d14b009dac7f1ff73e71211f2ae434adc6166f53a1b5dcf2c6a1db958679c45f99e550ff0ee86226ef22c94ac5
|
7
|
+
data.tar.gz: cd18c2100e9ca2fc341bf1efb3d51e94fc8d328948721c74dc422b4ddb35040cd0e18352d34bf7285c29b9be026e9b4ab1e6fca27e10064c5dd96c62db5c471c
|
data/README.md
CHANGED
data/demo/app.rb
CHANGED
@@ -1,19 +1,30 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
#require 'sinarey/base'
|
2
|
+
require 'sinarey/base'
|
4
3
|
|
5
4
|
class Application < Sinatra::SinareyBase
|
6
5
|
|
6
|
+
before do
|
7
|
+
puts "before at app1"
|
8
|
+
end
|
9
|
+
|
10
|
+
before "/app1/:id" do
|
11
|
+
puts "before app1 # #{params[:id]}"
|
12
|
+
end
|
13
|
+
|
14
|
+
after do
|
15
|
+
p env['sinarey.params']
|
16
|
+
end
|
17
|
+
|
7
18
|
error do
|
8
19
|
'error at app1'
|
9
20
|
end
|
10
21
|
|
11
22
|
get '/' do
|
23
|
+
|
12
24
|
'index'
|
13
25
|
end
|
14
26
|
|
15
27
|
get '/app1' do
|
16
|
-
p '123'
|
17
28
|
'app1'
|
18
29
|
end
|
19
30
|
|
@@ -23,7 +34,19 @@ class Application < Sinatra::SinareyBase
|
|
23
34
|
end
|
24
35
|
|
25
36
|
get '/app1/:id' do
|
37
|
+
|
26
38
|
"app1 # #{params[:id]}"
|
27
39
|
end
|
28
40
|
|
41
|
+
get '/app1/*.*' do
|
42
|
+
|
43
|
+
"app1 # #{params[splat]}"
|
44
|
+
end
|
45
|
+
|
46
|
+
get %r{^/tracks/([\d]+)/([\d]+)$} do |id, track_id|
|
47
|
+
params[:id] = id
|
48
|
+
params[:track_id] = track_id
|
49
|
+
"tracks #{params[:id]}"
|
50
|
+
end
|
51
|
+
|
29
52
|
end
|
data/demo/app2.rb
CHANGED
data/demo/config.ru
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../lib',__dir__)
|
3
3
|
|
4
4
|
require_relative 'app'
|
5
5
|
require_relative 'app2'
|
@@ -11,4 +11,7 @@ appRouter = Sinarey::Router.new do
|
|
11
11
|
mount Application2
|
12
12
|
notfound NotfoundApp
|
13
13
|
end
|
14
|
+
|
14
15
|
run appRouter
|
16
|
+
|
17
|
+
#run Application
|
data/demo/notfound.rb
CHANGED
data/lib/sinarey/base.rb
CHANGED
@@ -1,1220 +1,1195 @@
|
|
1
|
-
require 'sinatra/base'
|
2
|
-
|
3
|
-
module Sinatra
|
4
|
-
|
5
|
-
class SinareyBase
|
6
|
-
include Rack::Utils
|
7
|
-
include Helpers
|
8
|
-
include Templates
|
9
|
-
|
10
|
-
URI_INSTANCE = URI.const_defined?(:Parser) ? URI::Parser.new : URI
|
11
|
-
|
12
|
-
attr_accessor :app, :env, :request, :response, :params
|
13
|
-
attr_reader :template_cache
|
14
|
-
|
15
|
-
def initialize(app = nil)
|
16
|
-
super()
|
17
|
-
@app = app
|
18
|
-
@template_cache = Tilt::Cache.new
|
19
|
-
yield self if block_given?
|
20
|
-
end
|
21
|
-
|
22
|
-
# Rack call interface.
|
23
|
-
def call(env)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
@
|
30
|
-
@
|
31
|
-
@
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
route
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
@response['Content-Type']
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
#
|
176
|
-
#
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
if
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
if
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
end
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
elsif
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
end
|
312
|
-
|
313
|
-
#
|
314
|
-
def
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
#
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
if
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
#
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
end
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
def
|
568
|
-
|
569
|
-
end
|
570
|
-
|
571
|
-
def
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
def
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
end
|
593
|
-
|
594
|
-
def
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
def
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
def
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
#
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
def
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
end
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
end
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
def
|
770
|
-
|
771
|
-
if
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
end
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
end
|
985
|
-
|
986
|
-
def
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
end
|
1014
|
-
|
1015
|
-
def
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
def
|
1052
|
-
if
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
set :
|
1098
|
-
set :
|
1099
|
-
set :
|
1100
|
-
set :
|
1101
|
-
set :
|
1102
|
-
set :
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
set :
|
1126
|
-
set :
|
1127
|
-
|
1128
|
-
set :
|
1129
|
-
set :
|
1130
|
-
set :
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
(<<-HTML).gsub(/^ {10}/, '')
|
1197
|
-
<!DOCTYPE html>
|
1198
|
-
<html>
|
1199
|
-
<head>
|
1200
|
-
<style type="text/css">
|
1201
|
-
body { text-align:center;font-family:helvetica,arial;font-size:22px;
|
1202
|
-
color:#888;margin:20px}
|
1203
|
-
#c {margin:0 auto;width:500px;text-align:left}
|
1204
|
-
</style>
|
1205
|
-
</head>
|
1206
|
-
<body>
|
1207
|
-
<h2>Sinatra doesn’t know this ditty.</h2>
|
1208
|
-
<img src='#{uri "/__sinatra__/404.png"}'>
|
1209
|
-
<div id="c">
|
1210
|
-
Try this:
|
1211
|
-
<pre>#{code}</pre>
|
1212
|
-
</div>
|
1213
|
-
</body>
|
1214
|
-
</html>
|
1215
|
-
HTML
|
1216
|
-
end
|
1217
|
-
end
|
1218
|
-
end
|
1219
|
-
|
1
|
+
require 'sinatra/base'
|
2
|
+
|
3
|
+
module Sinatra
|
4
|
+
|
5
|
+
class SinareyBase
|
6
|
+
include Rack::Utils
|
7
|
+
include Helpers
|
8
|
+
include Templates
|
9
|
+
|
10
|
+
URI_INSTANCE = URI.const_defined?(:Parser) ? URI::Parser.new : URI
|
11
|
+
|
12
|
+
attr_accessor :app, :env, :request, :response, :params
|
13
|
+
attr_reader :template_cache
|
14
|
+
|
15
|
+
def initialize(app = nil)
|
16
|
+
super()
|
17
|
+
@app = app
|
18
|
+
@template_cache = Tilt::Cache.new
|
19
|
+
yield self if block_given?
|
20
|
+
end
|
21
|
+
|
22
|
+
# Rack call interface.
|
23
|
+
def call(env)
|
24
|
+
env['rack.framework'] = "Sinarey"
|
25
|
+
dup.call!(env)
|
26
|
+
end
|
27
|
+
|
28
|
+
def call!(env) # :nodoc:
|
29
|
+
@env = env
|
30
|
+
@request = Request.new(env)
|
31
|
+
@response = Response.new
|
32
|
+
@params = indifferent_params(@request.params)
|
33
|
+
template_cache.clear if settings.reload_templates
|
34
|
+
force_encoding(@params)
|
35
|
+
|
36
|
+
route = @request.path_info
|
37
|
+
route.chop! if (char=route[-1]) and char=='/' # ignore last '/' char
|
38
|
+
|
39
|
+
@response['Content-Type'] = nil
|
40
|
+
invoke { dispatch! }
|
41
|
+
invoke { error_block!(response.status) } unless @env['sinatra.error']
|
42
|
+
|
43
|
+
unless @response['Content-Type']
|
44
|
+
if Array === body and body[0].respond_to? :content_type
|
45
|
+
content_type body[0].content_type
|
46
|
+
else
|
47
|
+
content_type :html
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
@response.finish
|
52
|
+
end
|
53
|
+
|
54
|
+
# Access settings defined with Base.set.
|
55
|
+
def self.settings
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
# Access settings defined with Base.set.
|
60
|
+
def settings
|
61
|
+
self.class.settings
|
62
|
+
end
|
63
|
+
|
64
|
+
def options
|
65
|
+
warn "Sinatra::Base#options is deprecated and will be removed, " \
|
66
|
+
"use #settings instead."
|
67
|
+
settings
|
68
|
+
end
|
69
|
+
|
70
|
+
# Exit the current block, halts any further processing
|
71
|
+
# of the request, and returns the specified response.
|
72
|
+
def halt(*response)
|
73
|
+
response = response.first if response.length == 1
|
74
|
+
throw :halt, response
|
75
|
+
end
|
76
|
+
|
77
|
+
# Forward the request to the downstream app -- middleware only.
|
78
|
+
def forward
|
79
|
+
fail "downstream app not set" unless @app.respond_to? :call
|
80
|
+
status, headers, body = @app.call env
|
81
|
+
@response.status = status
|
82
|
+
@response.body = body
|
83
|
+
@response.headers.merge! headers
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# Run filters defined on the class and all superclasses.
|
90
|
+
def filter!(type, base = settings)
|
91
|
+
filter! type, base.superclass if base.superclass.respond_to?(:filters)
|
92
|
+
base.filters[type].each { |args| process_filter(*args) }
|
93
|
+
end
|
94
|
+
|
95
|
+
# Run routes defined on the class and all superclasses.
|
96
|
+
def route!(base = settings)
|
97
|
+
if router = env['sinarey.router']
|
98
|
+
return mount_route!(router)
|
99
|
+
end
|
100
|
+
|
101
|
+
if turbo_route = (turbo_routes = base.turbo_routes[@request.request_method]) && (path_info = turbo_routes[@request.path_info])
|
102
|
+
turbo_route.tap do |block_id|
|
103
|
+
process_turbo_route do |*args|
|
104
|
+
block = base.blocks[block_id]
|
105
|
+
action_block_wrap(block, *args)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
elsif routes = base.routes[@request.request_method]
|
109
|
+
routes.each do |pattern, keys, conditions, block_id|
|
110
|
+
process_route(pattern, keys, conditions) do |*args|
|
111
|
+
block = base.blocks[block_id]
|
112
|
+
action_block_wrap(block, *args)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Run routes defined in superclass.
|
118
|
+
if base.superclass.respond_to?(:routes)
|
119
|
+
return route!(base.superclass)
|
120
|
+
end
|
121
|
+
|
122
|
+
route_missing
|
123
|
+
end
|
124
|
+
|
125
|
+
def mount_route!(options,base = settings)
|
126
|
+
type,block_id = options[:type],options[:block_id]
|
127
|
+
case type
|
128
|
+
when :turbo
|
129
|
+
process_turbo_route do |*args|
|
130
|
+
block = base.blocks[block_id]
|
131
|
+
action_block_wrap(block, *args)
|
132
|
+
end
|
133
|
+
when :normal
|
134
|
+
match,keys,conditions = options[:match],options[:keys],options[:conditions]
|
135
|
+
process_mount_route(match, keys, conditions) do |*args|
|
136
|
+
block = base.blocks[block_id]
|
137
|
+
action_block_wrap(block, *args)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
route_missing
|
142
|
+
end
|
143
|
+
|
144
|
+
def action_block_wrap(block, *args)
|
145
|
+
env['sinatra.route'] = block.instance_variable_get(:@route_name)
|
146
|
+
route_eval { block[*args] }
|
147
|
+
ensure
|
148
|
+
env['sinarey.params'] = @params
|
149
|
+
end
|
150
|
+
|
151
|
+
# Run a route block and throw :halt with the result.
|
152
|
+
def route_eval
|
153
|
+
throw :halt, yield
|
154
|
+
end
|
155
|
+
|
156
|
+
def process_mount_route(match, keys, conditions, block_id = nil, values = [], &callback)
|
157
|
+
values += match.captures.map! { |v| force_encoding URI_INSTANCE.unescape(v) if v }
|
158
|
+
|
159
|
+
if values.any?
|
160
|
+
original, @params = params, params.merge('splat' => [], 'captures' => values)
|
161
|
+
keys.zip(values) do |k,v|
|
162
|
+
if Array === @params[k]
|
163
|
+
@params[k] << v
|
164
|
+
elsif v
|
165
|
+
@params[k] = v
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
(block_id && (block = settings.blocks[block_id])) ? block[self, values] : yield(self, values)
|
171
|
+
ensure
|
172
|
+
@params = original if original
|
173
|
+
end
|
174
|
+
|
175
|
+
# If the current request matches pattern and conditions, fill params
|
176
|
+
# with keys and call the given block.
|
177
|
+
# Revert params afterwards.
|
178
|
+
#
|
179
|
+
# Returns pass block.
|
180
|
+
|
181
|
+
def process_route(pattern, keys, conditions, block_id = nil, values = [], &callback)
|
182
|
+
route = @request.path_info
|
183
|
+
return unless match = pattern.match(route)
|
184
|
+
|
185
|
+
process_mount_route(match, keys, conditions, block_id, values, &callback)
|
186
|
+
end
|
187
|
+
|
188
|
+
def process_turbo_route(block = nil)
|
189
|
+
block ? block[self, []] : yield(self, [])
|
190
|
+
end
|
191
|
+
|
192
|
+
def process_filter(pattern, keys, conditions, block = nil, values = [])
|
193
|
+
route = @request.path_info
|
194
|
+
route = '/' if route.empty? and not settings.empty_path_info?
|
195
|
+
return unless match = pattern.match(route)
|
196
|
+
values += match.captures.map! { |v| force_encoding URI_INSTANCE.unescape(v) if v }
|
197
|
+
|
198
|
+
if values.any?
|
199
|
+
original, @params = params, params.merge('splat' => [], 'captures' => values)
|
200
|
+
keys.zip(values) { |k,v| Array === @params[k] ? @params[k] << v : @params[k] = v if v }
|
201
|
+
end
|
202
|
+
|
203
|
+
block ? block[self, values] : yield(self, values)
|
204
|
+
ensure
|
205
|
+
@params = original if original
|
206
|
+
end
|
207
|
+
|
208
|
+
# No matching route was found. The default
|
209
|
+
# implementation is to forward the request downstream when running
|
210
|
+
# as middleware (@app is non-nil); when no downstream app is set, raise
|
211
|
+
# a NotFound exception. Subclasses can override this method to perform
|
212
|
+
# custom route miss logic.
|
213
|
+
def route_missing
|
214
|
+
if @app
|
215
|
+
forward
|
216
|
+
else
|
217
|
+
raise NotFound
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# Attempt to serve static files from public directory. Throws :halt when
|
222
|
+
# a matching file is found, returns nil otherwise.
|
223
|
+
def static!
|
224
|
+
return if (public_dir = settings.public_folder).nil?
|
225
|
+
path = File.expand_path("#{public_dir}#{unescape(request.path_info)}" )
|
226
|
+
return unless File.file?(path)
|
227
|
+
|
228
|
+
env['sinatra.static_file'] = path
|
229
|
+
cache_control(*settings.static_cache_control) if settings.static_cache_control?
|
230
|
+
send_file path, :disposition => nil
|
231
|
+
end
|
232
|
+
|
233
|
+
# Enable string or symbol key access to the nested params hash.
|
234
|
+
def indifferent_params(object)
|
235
|
+
case object
|
236
|
+
when Hash
|
237
|
+
new_hash = indifferent_hash
|
238
|
+
object.each { |key, value| new_hash[key] = indifferent_params(value) }
|
239
|
+
new_hash
|
240
|
+
when Array
|
241
|
+
object.map { |item| indifferent_params(item) }
|
242
|
+
else
|
243
|
+
object
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Creates a Hash with indifferent access.
|
248
|
+
def indifferent_hash
|
249
|
+
Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
|
250
|
+
end
|
251
|
+
|
252
|
+
# Run the block with 'throw :halt' support and apply result to the response.
|
253
|
+
def invoke
|
254
|
+
res = catch(:halt) { yield }
|
255
|
+
res = [res] if Fixnum === res or String === res
|
256
|
+
if Array === res and Fixnum === res.first
|
257
|
+
res = res.dup
|
258
|
+
status(res.shift)
|
259
|
+
body(res.pop)
|
260
|
+
headers(*res)
|
261
|
+
elsif res.respond_to? :each
|
262
|
+
body res
|
263
|
+
end
|
264
|
+
nil # avoid double setting the same response tuple twice
|
265
|
+
end
|
266
|
+
|
267
|
+
# Dispatch a request with error handling.
|
268
|
+
def dispatch!
|
269
|
+
invoke do
|
270
|
+
static! if settings.static? && (request.get? || request.head?)
|
271
|
+
filter! :before
|
272
|
+
route!
|
273
|
+
end
|
274
|
+
rescue ::Exception => boom
|
275
|
+
invoke { handle_exception!(boom) }
|
276
|
+
ensure
|
277
|
+
begin
|
278
|
+
filter! :after
|
279
|
+
rescue ::Exception => boom
|
280
|
+
invoke { handle_exception!(boom) } unless @env['sinatra.error']
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
# Error handling during requests.
|
285
|
+
def handle_exception!(boom)
|
286
|
+
@env['sinatra.error'] = boom
|
287
|
+
|
288
|
+
if boom.respond_to? :http_status
|
289
|
+
status(boom.http_status)
|
290
|
+
elsif settings.use_code? and boom.respond_to? :code and boom.code.between? 400, 599
|
291
|
+
status(boom.code)
|
292
|
+
else
|
293
|
+
status(500)
|
294
|
+
end
|
295
|
+
|
296
|
+
status(500) unless status.between? 400, 599
|
297
|
+
|
298
|
+
if server_error?
|
299
|
+
dump_errors! boom if settings.dump_errors?
|
300
|
+
raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
|
301
|
+
end
|
302
|
+
|
303
|
+
if not_found?
|
304
|
+
body '<h1>Not Found</h1>'
|
305
|
+
end
|
306
|
+
|
307
|
+
res = error_block!(boom.class, boom) || error_block!(status, boom)
|
308
|
+
return res if res or not server_error?
|
309
|
+
raise boom if settings.raise_errors? or settings.show_exceptions?
|
310
|
+
error_block! Exception, boom
|
311
|
+
end
|
312
|
+
|
313
|
+
# Find an custom error block for the key(s) specified.
|
314
|
+
def error_block!(key, *block_params)
|
315
|
+
base = settings
|
316
|
+
while base.respond_to?(:errors)
|
317
|
+
next base = base.superclass unless args_array = base.errors[key]
|
318
|
+
args_array.reverse_each do |args|
|
319
|
+
first = args == args_array.first
|
320
|
+
args += [block_params]
|
321
|
+
resp = process_route(*args)
|
322
|
+
return resp unless resp.nil? && !first
|
323
|
+
end
|
324
|
+
end
|
325
|
+
return false unless key.respond_to? :superclass and key.superclass < Exception
|
326
|
+
error_block!(key.superclass, *block_params)
|
327
|
+
end
|
328
|
+
|
329
|
+
def dump_errors!(boom)
|
330
|
+
msg = ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
|
331
|
+
@env['rack.errors'].puts(msg)
|
332
|
+
end
|
333
|
+
|
334
|
+
class << self
|
335
|
+
CALLERS_TO_IGNORE = [ # :nodoc:
|
336
|
+
/\/sinatra(\/(base|main|showexceptions))?\.rb$/, # all sinatra code
|
337
|
+
/lib\/tilt.*\.rb$/, # all tilt code
|
338
|
+
/^\(.*\)$/, # generated code
|
339
|
+
/rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks
|
340
|
+
/active_support/, # active_support require hacks
|
341
|
+
/bundler(\/runtime)?\.rb/, # bundler require hacks
|
342
|
+
/<internal:/, # internal in ruby >= 1.9.2
|
343
|
+
/src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
|
344
|
+
]
|
345
|
+
|
346
|
+
# contrary to what the comment said previously, rubinius never supported this
|
347
|
+
if defined?(RUBY_IGNORE_CALLERS)
|
348
|
+
warn "RUBY_IGNORE_CALLERS is deprecated and will no longer be supported by Sinatra 2.0"
|
349
|
+
CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS)
|
350
|
+
end
|
351
|
+
|
352
|
+
attr_reader :blocks, :turbo_routes, :routes, :filters, :templates, :errors
|
353
|
+
|
354
|
+
# Removes all routes, filters, middleware and extension hooks from the
|
355
|
+
# current class (not routes/filters/... defined by its superclass).
|
356
|
+
def reset!
|
357
|
+
@conditions = []
|
358
|
+
@routes = {}
|
359
|
+
@turbo_routes = {}
|
360
|
+
@blocks = {}
|
361
|
+
@filters = {:before => [], :after => []}
|
362
|
+
@errors = {}
|
363
|
+
@middleware = []
|
364
|
+
@prototype = nil
|
365
|
+
@extensions = []
|
366
|
+
|
367
|
+
if superclass.respond_to?(:templates)
|
368
|
+
@templates = Hash.new { |hash,key| superclass.templates[key] }
|
369
|
+
else
|
370
|
+
@templates = {}
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
# Extension modules registered on this class and all superclasses.
|
375
|
+
def extensions
|
376
|
+
if superclass.respond_to?(:extensions)
|
377
|
+
(@extensions + superclass.extensions).uniq
|
378
|
+
else
|
379
|
+
@extensions
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
# Middleware used in this class and all superclasses.
|
384
|
+
def middleware
|
385
|
+
if superclass.respond_to?(:middleware)
|
386
|
+
superclass.middleware + @middleware
|
387
|
+
else
|
388
|
+
@middleware
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
# Sets an option to the given value. If the value is a proc,
|
393
|
+
# the proc will be called every time the option is accessed.
|
394
|
+
def set(option, value = (not_set = true), ignore_setter = false, &block)
|
395
|
+
raise ArgumentError if block and !not_set
|
396
|
+
value, not_set = block, false if block
|
397
|
+
|
398
|
+
if not_set
|
399
|
+
raise ArgumentError unless option.respond_to?(:each)
|
400
|
+
option.each { |k,v| set(k, v) }
|
401
|
+
return self
|
402
|
+
end
|
403
|
+
|
404
|
+
if respond_to?("#{option}=") and not ignore_setter
|
405
|
+
return __send__("#{option}=", value)
|
406
|
+
end
|
407
|
+
|
408
|
+
setter = proc { |val| set option, val, true }
|
409
|
+
getter = proc { value }
|
410
|
+
|
411
|
+
case value
|
412
|
+
when Proc
|
413
|
+
getter = value
|
414
|
+
when Symbol, Fixnum, FalseClass, TrueClass, NilClass
|
415
|
+
getter = value.inspect
|
416
|
+
when Hash
|
417
|
+
setter = proc do |val|
|
418
|
+
val = value.merge val if Hash === val
|
419
|
+
set option, val, true
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
define_singleton("#{option}=", setter) if setter
|
424
|
+
define_singleton(option, getter) if getter
|
425
|
+
define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
|
426
|
+
self
|
427
|
+
end
|
428
|
+
|
429
|
+
# Same as calling `set :option, true` for each of the given options.
|
430
|
+
def enable(*opts)
|
431
|
+
opts.each { |key| set(key, true) }
|
432
|
+
end
|
433
|
+
|
434
|
+
# Same as calling `set :option, false` for each of the given options.
|
435
|
+
def disable(*opts)
|
436
|
+
opts.each { |key| set(key, false) }
|
437
|
+
end
|
438
|
+
|
439
|
+
# Define a custom error handler. Optionally takes either an Exception
|
440
|
+
# class, or an HTTP status code to specify which errors should be
|
441
|
+
# handled.
|
442
|
+
def error(*codes, &block)
|
443
|
+
args = compile! "ERROR", //, block
|
444
|
+
codes = codes.map { |c| Array(c) }.flatten
|
445
|
+
codes << Exception if codes.empty?
|
446
|
+
codes.each { |c| (@errors[c] ||= []) << args }
|
447
|
+
end
|
448
|
+
|
449
|
+
# Sugar for `error(404) { ... }`
|
450
|
+
def not_found(&block)
|
451
|
+
error(404, &block)
|
452
|
+
error(Sinatra::NotFound, &block)
|
453
|
+
end
|
454
|
+
|
455
|
+
# Define a named template. The block must return the template source.
|
456
|
+
def template(name, &block)
|
457
|
+
filename, line = caller_locations.first
|
458
|
+
templates[name] = [block, filename, line.to_i]
|
459
|
+
end
|
460
|
+
|
461
|
+
# Define the layout template. The block must return the template source.
|
462
|
+
def layout(name = :layout, &block)
|
463
|
+
template name, &block
|
464
|
+
end
|
465
|
+
|
466
|
+
# Load embedded templates from the file; uses the caller's __FILE__
|
467
|
+
# when no file is specified.
|
468
|
+
def inline_templates=(file = nil)
|
469
|
+
file = (file.nil? || file == true) ? (caller_files.first || File.expand_path($0)) : file
|
470
|
+
|
471
|
+
begin
|
472
|
+
io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file)
|
473
|
+
app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2)
|
474
|
+
rescue Errno::ENOENT
|
475
|
+
app, data = nil
|
476
|
+
end
|
477
|
+
|
478
|
+
if data
|
479
|
+
if app and app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
|
480
|
+
encoding = $2
|
481
|
+
else
|
482
|
+
encoding = settings.default_encoding
|
483
|
+
end
|
484
|
+
lines = app.count("\n") + 1
|
485
|
+
template = nil
|
486
|
+
force_encoding data, encoding
|
487
|
+
data.each_line do |line|
|
488
|
+
lines += 1
|
489
|
+
if line =~ /^@@\s*(.*\S)\s*$/
|
490
|
+
template = force_encoding('', encoding)
|
491
|
+
templates[$1.to_sym] = [template, file, lines]
|
492
|
+
elsif template
|
493
|
+
template << line
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
# Lookup or register a mime type in Rack's mime registry.
|
500
|
+
def mime_type(type, value = nil)
|
501
|
+
return type if type.nil?
|
502
|
+
return type.to_s if type.to_s.include?('/')
|
503
|
+
type = ".#{type}" unless type.to_s[0] == ?.
|
504
|
+
return Rack::Mime.mime_type(type, nil) unless value
|
505
|
+
Rack::Mime::MIME_TYPES[type] = value
|
506
|
+
end
|
507
|
+
|
508
|
+
# provides all mime types matching type, including deprecated types:
|
509
|
+
# mime_types :html # => ['text/html']
|
510
|
+
# mime_types :js # => ['application/javascript', 'text/javascript']
|
511
|
+
def mime_types(type)
|
512
|
+
type = mime_type type
|
513
|
+
type =~ /^application\/(xml|javascript)$/ ? [type, "text/#$1"] : [type]
|
514
|
+
end
|
515
|
+
|
516
|
+
# Define a before filter; runs before all requests within the same
|
517
|
+
# context as route handlers and may access/modify the request and
|
518
|
+
# response.
|
519
|
+
def before(path = nil, options = {}, &block)
|
520
|
+
add_filter(:before, path, options, &block)
|
521
|
+
end
|
522
|
+
|
523
|
+
# Define an after filter; runs after all requests within the same
|
524
|
+
# context as route handlers and may access/modify the request and
|
525
|
+
# response.
|
526
|
+
def after(path = nil, options = {}, &block)
|
527
|
+
add_filter(:after, path, options, &block)
|
528
|
+
end
|
529
|
+
|
530
|
+
# add a filter
|
531
|
+
def add_filter(type, path = nil, options = {}, &block)
|
532
|
+
path, options = //, path if path.respond_to?(:each_pair)
|
533
|
+
filters[type] << compile_filter!(type, path || //, block, options)
|
534
|
+
end
|
535
|
+
|
536
|
+
# Add a route condition. The route is considered non-matching when the
|
537
|
+
# block returns false.
|
538
|
+
def condition(name = "#{caller.first[/`.*'/]} condition", &block)
|
539
|
+
@conditions << generate_method(name, &block)
|
540
|
+
end
|
541
|
+
|
542
|
+
def public=(value)
|
543
|
+
warn ":public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead"
|
544
|
+
set(:public_folder, value)
|
545
|
+
end
|
546
|
+
|
547
|
+
def public_dir=(value)
|
548
|
+
self.public_folder = value
|
549
|
+
end
|
550
|
+
|
551
|
+
def public_dir
|
552
|
+
public_folder
|
553
|
+
end
|
554
|
+
|
555
|
+
# Defining a `GET` handler also automatically defines
|
556
|
+
# a `HEAD` handler.
|
557
|
+
def get(path, opts = {}, &block)
|
558
|
+
conditions = @conditions.dup
|
559
|
+
route('GET', path, opts, &block)
|
560
|
+
|
561
|
+
@conditions = conditions
|
562
|
+
route('HEAD', path, opts, &block)
|
563
|
+
end
|
564
|
+
|
565
|
+
def put(path, opts = {}, &bk) route 'PUT', path, opts, &bk end
|
566
|
+
def post(path, opts = {}, &bk) route 'POST', path, opts, &bk end
|
567
|
+
def delete(path, opts = {}, &bk) route 'DELETE', path, opts, &bk end
|
568
|
+
def head(path, opts = {}, &bk) route 'HEAD', path, opts, &bk end
|
569
|
+
def options(path, opts = {}, &bk) route 'OPTIONS', path, opts, &bk end
|
570
|
+
def patch(path, opts = {}, &bk) route 'PATCH', path, opts, &bk end
|
571
|
+
def link(path, opts = {}, &bk) route 'LINK', path, opts, &bk end
|
572
|
+
def unlink(path, opts = {}, &bk) route 'UNLINK', path, opts, &bk end
|
573
|
+
|
574
|
+
# Makes the methods defined in the block and in the Modules given
|
575
|
+
# in `extensions` available to the handlers and templates
|
576
|
+
def helpers(*extensions, &block)
|
577
|
+
class_eval(&block) if block_given?
|
578
|
+
include(*extensions) if extensions.any?
|
579
|
+
end
|
580
|
+
|
581
|
+
# Register an extension. Alternatively take a block from which an
|
582
|
+
# extension will be created and registered on the fly.
|
583
|
+
def register(*extensions, &block)
|
584
|
+
extensions << Module.new(&block) if block_given?
|
585
|
+
@extensions += extensions
|
586
|
+
extensions.each do |extension|
|
587
|
+
extend extension
|
588
|
+
extension.registered(self) if extension.respond_to?(:registered)
|
589
|
+
end
|
590
|
+
end
|
591
|
+
|
592
|
+
def development?; environment == :development end
|
593
|
+
def production?; environment == :production end
|
594
|
+
def test?; environment == :test end
|
595
|
+
|
596
|
+
# Set configuration options for Sinatra and/or the app.
|
597
|
+
# Allows scoping of settings for certain environments.
|
598
|
+
def configure(*envs)
|
599
|
+
yield self if envs.empty? || envs.include?(environment.to_sym)
|
600
|
+
end
|
601
|
+
|
602
|
+
# Use the specified Rack middleware
|
603
|
+
def use(middleware, *args, &block)
|
604
|
+
@prototype = nil
|
605
|
+
@middleware << [middleware, args, block]
|
606
|
+
end
|
607
|
+
|
608
|
+
# Stop the self-hosted server if running.
|
609
|
+
def quit!
|
610
|
+
return unless running?
|
611
|
+
# Use Thin's hard #stop! if available, otherwise just #stop.
|
612
|
+
running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
|
613
|
+
$stderr.puts "== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
|
614
|
+
set :running_server, nil
|
615
|
+
set :handler_name, nil
|
616
|
+
end
|
617
|
+
|
618
|
+
alias_method :stop!, :quit!
|
619
|
+
|
620
|
+
# Run the Sinatra app as a self-hosted server using
|
621
|
+
# Thin, Puma, Mongrel, or WEBrick (in that order). If given a block, will call
|
622
|
+
# with the constructed handler once we have taken the stage.
|
623
|
+
def run!(options = {}, &block)
|
624
|
+
return if running?
|
625
|
+
set options
|
626
|
+
handler = detect_rack_handler
|
627
|
+
handler_name = handler.name.gsub(/.*::/, '')
|
628
|
+
server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
|
629
|
+
server_settings.merge!(:Port => port, :Host => bind)
|
630
|
+
|
631
|
+
begin
|
632
|
+
start_server(handler, server_settings, handler_name, &block)
|
633
|
+
rescue Errno::EADDRINUSE
|
634
|
+
$stderr.puts "== Someone is already performing on port #{port}!"
|
635
|
+
raise
|
636
|
+
ensure
|
637
|
+
quit!
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
alias_method :start!, :run!
|
642
|
+
|
643
|
+
# Check whether the self-hosted server is running or not.
|
644
|
+
def running?
|
645
|
+
running_server?
|
646
|
+
end
|
647
|
+
|
648
|
+
# The prototype instance used to process requests.
|
649
|
+
def prototype
|
650
|
+
@prototype ||= new
|
651
|
+
end
|
652
|
+
|
653
|
+
# Create a new instance without middleware in front of it.
|
654
|
+
alias new! new unless method_defined? :new!
|
655
|
+
|
656
|
+
# Create a new instance of the class fronted by its middleware
|
657
|
+
# pipeline. The object is guaranteed to respond to #call but may not be
|
658
|
+
# an instance of the class new was called on.
|
659
|
+
def new(*args, &bk)
|
660
|
+
instance = new!(*args, &bk)
|
661
|
+
Wrapper.new(build(instance).to_app, instance)
|
662
|
+
end
|
663
|
+
|
664
|
+
# Creates a Rack::Builder instance with all the middleware set up and
|
665
|
+
# the given +app+ as end point.
|
666
|
+
def build(app)
|
667
|
+
builder = Rack::Builder.new
|
668
|
+
setup_default_middleware builder
|
669
|
+
setup_middleware builder
|
670
|
+
builder.run app
|
671
|
+
builder
|
672
|
+
end
|
673
|
+
|
674
|
+
def call(env)
|
675
|
+
synchronize { prototype.call(env) }
|
676
|
+
end
|
677
|
+
|
678
|
+
# Like Kernel#caller but excluding certain magic entries and without
|
679
|
+
# line / method information; the resulting array contains filenames only.
|
680
|
+
def caller_files
|
681
|
+
cleaned_caller(1).flatten
|
682
|
+
end
|
683
|
+
|
684
|
+
# Like caller_files, but containing Arrays rather than strings with the
|
685
|
+
# first element being the file, and the second being the line.
|
686
|
+
def caller_locations
|
687
|
+
cleaned_caller 2
|
688
|
+
end
|
689
|
+
|
690
|
+
private
|
691
|
+
|
692
|
+
# Starts the server by running the Rack Handler.
|
693
|
+
def start_server(handler, server_settings, handler_name)
|
694
|
+
handler.run(self, server_settings) do |server|
|
695
|
+
unless handler_name =~ /cgi/i
|
696
|
+
$stderr.puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
|
697
|
+
"on #{port} for #{environment} with backup from #{handler_name}"
|
698
|
+
end
|
699
|
+
|
700
|
+
setup_traps
|
701
|
+
set :running_server, server
|
702
|
+
set :handler_name, handler_name
|
703
|
+
server.threaded = settings.threaded if server.respond_to? :threaded=
|
704
|
+
|
705
|
+
yield server if block_given?
|
706
|
+
end
|
707
|
+
end
|
708
|
+
|
709
|
+
def setup_traps
|
710
|
+
if traps?
|
711
|
+
at_exit { quit! }
|
712
|
+
|
713
|
+
[:INT, :TERM].each do |signal|
|
714
|
+
old_handler = trap(signal) do
|
715
|
+
quit!
|
716
|
+
old_handler.call if old_handler.respond_to?(:call)
|
717
|
+
end
|
718
|
+
end
|
719
|
+
|
720
|
+
set :traps, false
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
724
|
+
# Dynamically defines a method on settings.
|
725
|
+
def define_singleton(name, content = Proc.new)
|
726
|
+
# replace with call to singleton_class once we're 1.9 only
|
727
|
+
(class << self; self; end).class_eval do
|
728
|
+
undef_method(name) if method_defined? name
|
729
|
+
String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
|
730
|
+
end
|
731
|
+
end
|
732
|
+
|
733
|
+
# Condition for matching host name. Parameter might be String or Regexp.
|
734
|
+
def host_name(pattern)
|
735
|
+
condition { pattern === request.host }
|
736
|
+
end
|
737
|
+
|
738
|
+
# Condition for matching user agent. Parameter should be Regexp.
|
739
|
+
# Will set params[:agent].
|
740
|
+
def user_agent(pattern)
|
741
|
+
condition do
|
742
|
+
if request.user_agent.to_s =~ pattern
|
743
|
+
@params[:agent] = $~[1..-1]
|
744
|
+
true
|
745
|
+
else
|
746
|
+
false
|
747
|
+
end
|
748
|
+
end
|
749
|
+
end
|
750
|
+
alias_method :agent, :user_agent
|
751
|
+
|
752
|
+
# Condition for matching mimetypes. Accepts file extensions.
|
753
|
+
def provides(*types)
|
754
|
+
types.map! { |t| mime_types(t) }
|
755
|
+
types.flatten!
|
756
|
+
condition do
|
757
|
+
if type = response['Content-Type']
|
758
|
+
types.include? type or types.include? type[/^[^;]+/]
|
759
|
+
elsif type = request.preferred_type(types)
|
760
|
+
params = (type.respond_to?(:params) ? type.params : {})
|
761
|
+
content_type(type, params)
|
762
|
+
true
|
763
|
+
else
|
764
|
+
false
|
765
|
+
end
|
766
|
+
end
|
767
|
+
end
|
768
|
+
|
769
|
+
def route(verb, path, options = {}, &block)
|
770
|
+
if path.class == String
|
771
|
+
return if path.empty?
|
772
|
+
path.chop! if (char=path[-1]) and char=='/'
|
773
|
+
end
|
774
|
+
|
775
|
+
# Because of self.options.host
|
776
|
+
host_name(options.delete(:host)) if options.key?(:host)
|
777
|
+
|
778
|
+
if /^[a-zA-Z0-9\-\/_]*$/ === path
|
779
|
+
turbo_signature = turbo_compile!(verb, path, block, options)
|
780
|
+
@turbo_routes[verb] ||= {}
|
781
|
+
@turbo_routes[verb][path] = turbo_signature
|
782
|
+
else
|
783
|
+
signature = compile!(verb, path, block, options)
|
784
|
+
(@routes[verb] ||= []) << signature
|
785
|
+
end
|
786
|
+
invoke_hook(:route_added, verb, path, block)
|
787
|
+
signature
|
788
|
+
end
|
789
|
+
|
790
|
+
def invoke_hook(name, *args)
|
791
|
+
extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
|
792
|
+
end
|
793
|
+
|
794
|
+
def generate_method(method_name, &block)
|
795
|
+
define_method(method_name, &block)
|
796
|
+
method = instance_method method_name
|
797
|
+
remove_method method_name
|
798
|
+
method
|
799
|
+
end
|
800
|
+
|
801
|
+
def turbo_compile!(verb, path, block, options = {})
|
802
|
+
method_name = "#{verb} #{path}"
|
803
|
+
unbound_method = generate_method(method_name, &block)
|
804
|
+
wrapper = block.arity != 0 ?
|
805
|
+
proc { |a,p| unbound_method.bind(a).call(*p) } :
|
806
|
+
proc { |a,p| unbound_method.bind(a).call }
|
807
|
+
wrapper.instance_variable_set(:@route_name, method_name)
|
808
|
+
@blocks ||= {}
|
809
|
+
block_id = @blocks.size + 1
|
810
|
+
@blocks[block_id] = wrapper
|
811
|
+
block_id
|
812
|
+
end
|
813
|
+
|
814
|
+
def compile!(verb, path, block, options = {})
|
815
|
+
options.each_pair { |option, args| send(option, *args) }
|
816
|
+
method_name = "#{verb} #{path}"
|
817
|
+
unbound_method = generate_method(method_name, &block)
|
818
|
+
pattern, keys = compile path
|
819
|
+
conditions, @conditions = @conditions, []
|
820
|
+
|
821
|
+
wrapper = block.arity != 0 ?
|
822
|
+
proc { |a,p| unbound_method.bind(a).call(*p) } :
|
823
|
+
proc { |a,p| unbound_method.bind(a).call }
|
824
|
+
wrapper.instance_variable_set(:@route_name, method_name)
|
825
|
+
@blocks ||= {}
|
826
|
+
block_id = @blocks.size + 1
|
827
|
+
@blocks[block_id] = wrapper
|
828
|
+
[ pattern, keys, conditions, block_id ]
|
829
|
+
end
|
830
|
+
|
831
|
+
def compile_filter!(verb, path, block, options = {})
|
832
|
+
options.each_pair { |option, args| send(option, *args) }
|
833
|
+
method_name = "#{verb} #{path}"
|
834
|
+
unbound_method = generate_method(method_name, &block)
|
835
|
+
pattern, keys = compile path
|
836
|
+
conditions, @conditions = @conditions, []
|
837
|
+
|
838
|
+
wrapper = block.arity != 0 ?
|
839
|
+
proc { |a,p| unbound_method.bind(a).call(*p) } :
|
840
|
+
proc { |a,p| unbound_method.bind(a).call }
|
841
|
+
wrapper.instance_variable_set(:@route_name, method_name)
|
842
|
+
|
843
|
+
[ pattern, keys, conditions, wrapper ]
|
844
|
+
end
|
845
|
+
|
846
|
+
|
847
|
+
def compile(path)
|
848
|
+
if path.respond_to? :to_str
|
849
|
+
keys = []
|
850
|
+
|
851
|
+
# We append a / at the end if there was one.
|
852
|
+
# Reason: Splitting does not split off an empty
|
853
|
+
# string at the end if the split separator
|
854
|
+
# is at the end.
|
855
|
+
#
|
856
|
+
postfix = '/' if path =~ /\/\z/
|
857
|
+
|
858
|
+
# Split the path into pieces in between forward slashes.
|
859
|
+
#
|
860
|
+
segments = path.split('/').map! do |segment|
|
861
|
+
ignore = []
|
862
|
+
|
863
|
+
# Special character handling.
|
864
|
+
#
|
865
|
+
pattern = segment.to_str.gsub(/[^\?\%\\\/\:\*\w]/) do |c|
|
866
|
+
ignore << escaped(c).join if c.match(/[\.@]/)
|
867
|
+
patt = encoded(c)
|
868
|
+
patt.gsub(/%[\da-fA-F]{2}/) do |match|
|
869
|
+
match.split(//).map! {|char| char =~ /[A-Z]/ ? "[#{char}#{char.tr('A-Z', 'a-z')}]" : char}.join
|
870
|
+
end
|
871
|
+
end
|
872
|
+
|
873
|
+
ignore = ignore.uniq.join
|
874
|
+
|
875
|
+
# Key handling.
|
876
|
+
#
|
877
|
+
pattern.gsub(/((:\w+)|\*)/) do |match|
|
878
|
+
if match == "*"
|
879
|
+
keys << 'splat'
|
880
|
+
"(.*?)"
|
881
|
+
else
|
882
|
+
keys << $2[1..-1]
|
883
|
+
ignore_pattern = safe_ignore(ignore)
|
884
|
+
|
885
|
+
ignore_pattern
|
886
|
+
end
|
887
|
+
end
|
888
|
+
end
|
889
|
+
|
890
|
+
# Special case handling.
|
891
|
+
#
|
892
|
+
if segment = segments.pop
|
893
|
+
if segment.match(/\[\^\\\./)
|
894
|
+
parts = segment.rpartition(/\[\^\\\./)
|
895
|
+
parts[1] = '[^'
|
896
|
+
segments << parts.join
|
897
|
+
else
|
898
|
+
segments << segment
|
899
|
+
end
|
900
|
+
end
|
901
|
+
[/\A#{segments.join('/')}#{postfix}\z/, keys]
|
902
|
+
elsif path.respond_to?(:keys) && path.respond_to?(:match)
|
903
|
+
[path, path.keys]
|
904
|
+
elsif path.respond_to?(:names) && path.respond_to?(:match)
|
905
|
+
[path, path.names]
|
906
|
+
elsif path.respond_to? :match
|
907
|
+
[path, []]
|
908
|
+
else
|
909
|
+
raise TypeError, path
|
910
|
+
end
|
911
|
+
end
|
912
|
+
|
913
|
+
def encoded(char)
|
914
|
+
enc = URI_INSTANCE.escape(char)
|
915
|
+
enc = "(?:#{escaped(char, enc).join('|')})" if enc == char
|
916
|
+
enc = "(?:#{enc}|#{encoded('+')})" if char == " "
|
917
|
+
enc
|
918
|
+
end
|
919
|
+
|
920
|
+
def escaped(char, enc = URI_INSTANCE.escape(char))
|
921
|
+
[Regexp.escape(enc), URI_INSTANCE.escape(char, /./)]
|
922
|
+
end
|
923
|
+
|
924
|
+
def safe_ignore(ignore)
|
925
|
+
unsafe_ignore = []
|
926
|
+
ignore = ignore.gsub(/%[\da-fA-F]{2}/) do |hex|
|
927
|
+
unsafe_ignore << hex[1..2]
|
928
|
+
''
|
929
|
+
end
|
930
|
+
unsafe_patterns = unsafe_ignore.map! do |unsafe|
|
931
|
+
chars = unsafe.split(//).map! do |char|
|
932
|
+
if char =~ /[A-Z]/
|
933
|
+
char <<= char.tr('A-Z', 'a-z')
|
934
|
+
end
|
935
|
+
char
|
936
|
+
end
|
937
|
+
|
938
|
+
"|(?:%[^#{chars[0]}].|%[#{chars[0]}][^#{chars[1]}])"
|
939
|
+
end
|
940
|
+
if unsafe_patterns.length > 0
|
941
|
+
"((?:[^#{ignore}/?#%]#{unsafe_patterns.join()})+)"
|
942
|
+
else
|
943
|
+
"([^#{ignore}/?#]+)"
|
944
|
+
end
|
945
|
+
end
|
946
|
+
|
947
|
+
def setup_default_middleware(builder)
|
948
|
+
builder.use ExtendedRack
|
949
|
+
builder.use ShowExceptions if show_exceptions?
|
950
|
+
builder.use Rack::MethodOverride if method_override?
|
951
|
+
builder.use Rack::Head
|
952
|
+
setup_logging builder
|
953
|
+
setup_sessions builder
|
954
|
+
setup_protection builder
|
955
|
+
end
|
956
|
+
|
957
|
+
def setup_middleware(builder)
|
958
|
+
middleware.each { |c,a,b| builder.use(c, *a, &b) }
|
959
|
+
end
|
960
|
+
|
961
|
+
def setup_logging(builder)
|
962
|
+
if logging?
|
963
|
+
setup_common_logger(builder)
|
964
|
+
setup_custom_logger(builder)
|
965
|
+
elsif logging == false
|
966
|
+
setup_null_logger(builder)
|
967
|
+
end
|
968
|
+
end
|
969
|
+
|
970
|
+
def setup_null_logger(builder)
|
971
|
+
builder.use Rack::NullLogger
|
972
|
+
end
|
973
|
+
|
974
|
+
def setup_common_logger(builder)
|
975
|
+
builder.use Sinatra::CommonLogger
|
976
|
+
end
|
977
|
+
|
978
|
+
def setup_custom_logger(builder)
|
979
|
+
if logging.respond_to? :to_int
|
980
|
+
builder.use Rack::Logger, logging
|
981
|
+
else
|
982
|
+
builder.use Rack::Logger
|
983
|
+
end
|
984
|
+
end
|
985
|
+
|
986
|
+
def setup_protection(builder)
|
987
|
+
return unless protection?
|
988
|
+
options = Hash === protection ? protection.dup : {}
|
989
|
+
protect_session = options.fetch(:session) { sessions? }
|
990
|
+
options[:except] = Array options[:except]
|
991
|
+
options[:except] += [:session_hijacking, :remote_token] unless protect_session
|
992
|
+
options[:reaction] ||= :drop_session
|
993
|
+
builder.use Rack::Protection, options
|
994
|
+
end
|
995
|
+
|
996
|
+
def setup_sessions(builder)
|
997
|
+
return unless sessions?
|
998
|
+
options = {}
|
999
|
+
options[:secret] = session_secret if session_secret?
|
1000
|
+
options.merge! sessions.to_hash if sessions.respond_to? :to_hash
|
1001
|
+
builder.use Rack::Session::Cookie, options
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
def detect_rack_handler
|
1005
|
+
servers = Array(server)
|
1006
|
+
servers.each do |server_name|
|
1007
|
+
begin
|
1008
|
+
return Rack::Handler.get(server_name.to_s)
|
1009
|
+
rescue LoadError, NameError
|
1010
|
+
end
|
1011
|
+
end
|
1012
|
+
fail "Server handler (#{servers.join(',')}) not found."
|
1013
|
+
end
|
1014
|
+
|
1015
|
+
def inherited(subclass)
|
1016
|
+
subclass.reset!
|
1017
|
+
subclass.set :app_file, caller_files.first unless subclass.app_file?
|
1018
|
+
super
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
@@mutex = Mutex.new
|
1022
|
+
def synchronize(&block)
|
1023
|
+
if lock?
|
1024
|
+
@@mutex.synchronize(&block)
|
1025
|
+
else
|
1026
|
+
yield
|
1027
|
+
end
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
# used for deprecation warnings
|
1031
|
+
def warn(message)
|
1032
|
+
super message + "\n\tfrom #{cleaned_caller.first.join(':')}"
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
# Like Kernel#caller but excluding certain magic entries
|
1036
|
+
def cleaned_caller(keep = 3)
|
1037
|
+
caller(1).
|
1038
|
+
map! { |line| line.split(/:(?=\d|in )/, 3)[0,keep] }.
|
1039
|
+
reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
|
1040
|
+
end
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
# Fixes encoding issues by
|
1044
|
+
# * defaulting to UTF-8
|
1045
|
+
# * casting params to Encoding.default_external
|
1046
|
+
#
|
1047
|
+
# The latter might not be necessary if Rack handles it one day.
|
1048
|
+
# Keep an eye on Rack's LH #100.
|
1049
|
+
def force_encoding(*args) settings.force_encoding(*args) end
|
1050
|
+
if defined? Encoding
|
1051
|
+
def self.force_encoding(data, encoding = default_encoding)
|
1052
|
+
return if data == settings || data.is_a?(Tempfile)
|
1053
|
+
if data.respond_to? :force_encoding
|
1054
|
+
data.force_encoding(encoding).encode!
|
1055
|
+
elsif data.respond_to? :each_value
|
1056
|
+
data.each_value { |v| force_encoding(v, encoding) }
|
1057
|
+
elsif data.respond_to? :each
|
1058
|
+
data.each { |v| force_encoding(v, encoding) }
|
1059
|
+
end
|
1060
|
+
data
|
1061
|
+
end
|
1062
|
+
else
|
1063
|
+
def self.force_encoding(data, *) data end
|
1064
|
+
end
|
1065
|
+
|
1066
|
+
reset!
|
1067
|
+
|
1068
|
+
set :environment, (ENV['RACK_ENV'] || :development).to_sym
|
1069
|
+
set :raise_errors, Proc.new { test? }
|
1070
|
+
set :dump_errors, Proc.new { !test? }
|
1071
|
+
set :show_exceptions, Proc.new { development? }
|
1072
|
+
set :sessions, false
|
1073
|
+
set :logging, false
|
1074
|
+
set :protection, true
|
1075
|
+
set :method_override, false
|
1076
|
+
set :use_code, false
|
1077
|
+
set :default_encoding, "utf-8"
|
1078
|
+
set :x_cascade, true
|
1079
|
+
set :add_charset, %w[javascript xml xhtml+xml json].map { |t| "application/#{t}" }
|
1080
|
+
settings.add_charset << /^text\//
|
1081
|
+
|
1082
|
+
# explicitly generating a session secret eagerly to play nice with preforking
|
1083
|
+
begin
|
1084
|
+
require 'securerandom'
|
1085
|
+
set :session_secret, SecureRandom.hex(64)
|
1086
|
+
rescue LoadError, NotImplementedError
|
1087
|
+
# SecureRandom raises a NotImplementedError if no random device is available
|
1088
|
+
set :session_secret, "%064x" % Kernel.rand(2**256-1)
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
class << self
|
1092
|
+
alias_method :methodoverride?, :method_override?
|
1093
|
+
alias_method :methodoverride=, :method_override=
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
set :run, false # start server via at-exit hook?
|
1097
|
+
set :running_server, nil
|
1098
|
+
set :handler_name, nil
|
1099
|
+
set :traps, true
|
1100
|
+
set :server, %w[HTTP webrick]
|
1101
|
+
set :bind, Proc.new { development? ? 'localhost' : '0.0.0.0' }
|
1102
|
+
set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567)
|
1103
|
+
|
1104
|
+
ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
|
1105
|
+
|
1106
|
+
if ruby_engine == 'macruby'
|
1107
|
+
server.unshift 'control_tower'
|
1108
|
+
else
|
1109
|
+
server.unshift 'reel'
|
1110
|
+
server.unshift 'mongrel' if ruby_engine.nil?
|
1111
|
+
server.unshift 'puma' if ruby_engine != 'rbx'
|
1112
|
+
server.unshift 'thin' if ruby_engine != 'jruby'
|
1113
|
+
server.unshift 'puma' if ruby_engine == 'rbx'
|
1114
|
+
server.unshift 'trinidad' if ruby_engine == 'jruby'
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
set :absolute_redirects, true
|
1118
|
+
set :prefixed_redirects, false
|
1119
|
+
set :empty_path_info, nil
|
1120
|
+
|
1121
|
+
set :app_file, nil
|
1122
|
+
set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
|
1123
|
+
set :views, Proc.new { root && File.join(root, 'views') }
|
1124
|
+
set :reload_templates, Proc.new { development? }
|
1125
|
+
set :lock, false
|
1126
|
+
set :threaded, true
|
1127
|
+
|
1128
|
+
set :public_folder, Proc.new { root && File.join(root, 'public') }
|
1129
|
+
set :static, Proc.new { public_folder && File.exist?(public_folder) }
|
1130
|
+
set :static_cache_control, false
|
1131
|
+
|
1132
|
+
error ::Exception do
|
1133
|
+
response.status = 500
|
1134
|
+
content_type 'text/html'
|
1135
|
+
'<h1>Internal Server Error</h1>'
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
after do
|
1139
|
+
env['sinarey.params'] ||= @params
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
configure :development do
|
1143
|
+
get '/__sinatra__/:image.png' do
|
1144
|
+
filename = File.dirname(__FILE__) + "/images/#{params[:image]}.png"
|
1145
|
+
content_type :png
|
1146
|
+
send_file filename
|
1147
|
+
end
|
1148
|
+
|
1149
|
+
error NotFound do
|
1150
|
+
content_type 'text/html'
|
1151
|
+
|
1152
|
+
if self.class == Sinatra::Application
|
1153
|
+
code = <<-RUBY.gsub(/^ {12}/, '')
|
1154
|
+
#{request.request_method.downcase} '#{request.path_info}' do
|
1155
|
+
"Hello World"
|
1156
|
+
end
|
1157
|
+
RUBY
|
1158
|
+
else
|
1159
|
+
code = <<-RUBY.gsub(/^ {12}/, '')
|
1160
|
+
class #{self.class}
|
1161
|
+
#{request.request_method.downcase} '#{request.path_info}' do
|
1162
|
+
"Hello World"
|
1163
|
+
end
|
1164
|
+
end
|
1165
|
+
RUBY
|
1166
|
+
|
1167
|
+
file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(/^\//, '')
|
1168
|
+
code = "# in #{file}\n#{code}" unless file.empty?
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
(<<-HTML).gsub(/^ {10}/, '')
|
1172
|
+
<!DOCTYPE html>
|
1173
|
+
<html>
|
1174
|
+
<head>
|
1175
|
+
<style type="text/css">
|
1176
|
+
body { text-align:center;font-family:helvetica,arial;font-size:22px;
|
1177
|
+
color:#888;margin:20px}
|
1178
|
+
#c {margin:0 auto;width:500px;text-align:left}
|
1179
|
+
</style>
|
1180
|
+
</head>
|
1181
|
+
<body>
|
1182
|
+
<h2>Sinatra doesn’t know this ditty.</h2>
|
1183
|
+
<img src='#{uri "/__sinatra__/404.png"}'>
|
1184
|
+
<div id="c">
|
1185
|
+
Try this:
|
1186
|
+
<pre>#{code}</pre>
|
1187
|
+
</div>
|
1188
|
+
</body>
|
1189
|
+
</html>
|
1190
|
+
HTML
|
1191
|
+
end
|
1192
|
+
end
|
1193
|
+
end
|
1194
|
+
|
1220
1195
|
end
|