camping 1.5 → 1.5.180
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +29 -1
- data/lib/camping-unabridged.rb +33 -17
- data/lib/camping.rb +19 -16
- data/lib/camping/fastcgi.rb +109 -63
- data/lib/camping/reloader.rb +1 -7
- data/lib/camping/webrick.rb +1 -1
- data/test/test_xhtml_trans.rb +55 -0
- metadata +5 -4
data/Rakefile
CHANGED
@@ -2,13 +2,14 @@ require 'rake'
|
|
2
2
|
require 'rake/clean'
|
3
3
|
require 'rake/gempackagetask'
|
4
4
|
require 'rake/rdoctask'
|
5
|
+
require 'rake/testtask'
|
5
6
|
require 'fileutils'
|
6
7
|
include FileUtils
|
7
8
|
|
8
9
|
NAME = "camping"
|
9
10
|
REV = File.read(".svn/entries")[/committed-rev="(\d+)"/, 1] rescue nil
|
10
11
|
VERS = ENV['VERSION'] || ("1.5" + (REV ? ".#{REV}" : ""))
|
11
|
-
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
12
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config', 'test/test.log']
|
12
13
|
RDOC_OPTS = ['--quiet', '--title', "Camping, the Documentation",
|
13
14
|
"--opname", "index.html",
|
14
15
|
"--line-numbers",
|
@@ -74,11 +75,32 @@ spec =
|
|
74
75
|
s.bindir = "bin"
|
75
76
|
end
|
76
77
|
|
78
|
+
omni =
|
79
|
+
Gem::Specification.new do |s|
|
80
|
+
s.name = "camping-omnibus"
|
81
|
+
s.version = VERS
|
82
|
+
s.platform = Gem::Platform::RUBY
|
83
|
+
s.summary = "the camping meta-package for updating ActiveRecord, Mongrel and SQLite3 bindings"
|
84
|
+
s.description = s.summary
|
85
|
+
%w[author email homepage].each { |x| s.__send__("#{x}=", spec.__send__(x)) }
|
86
|
+
|
87
|
+
s.add_dependency('camping', "=#{VERS}")
|
88
|
+
s.add_dependency('activerecord')
|
89
|
+
s.add_dependency('sqlite3-ruby', '>=1.1.0.1')
|
90
|
+
s.add_dependency('mongrel')
|
91
|
+
s.add_dependency('acts_as_versioned')
|
92
|
+
s.add_dependency('RedCloth')
|
93
|
+
end
|
94
|
+
|
77
95
|
Rake::GemPackageTask.new(spec) do |p|
|
78
96
|
p.need_tar = true
|
79
97
|
p.gem_spec = spec
|
80
98
|
end
|
81
99
|
|
100
|
+
Rake::GemPackageTask.new(omni) do |p|
|
101
|
+
p.gem_spec = omni
|
102
|
+
end
|
103
|
+
|
82
104
|
task :install do
|
83
105
|
sh %{rake package}
|
84
106
|
sh %{sudo gem install pkg/#{NAME}-#{VERS}}
|
@@ -87,3 +109,9 @@ end
|
|
87
109
|
task :uninstall => [:clean] do
|
88
110
|
sh %{sudo gem uninstall #{NAME}}
|
89
111
|
end
|
112
|
+
|
113
|
+
Rake::TestTask.new(:test) do |t|
|
114
|
+
t.test_files = FileList['test/test_*.rb']
|
115
|
+
# t.warning = true
|
116
|
+
# t.verbose = true
|
117
|
+
end
|
data/lib/camping-unabridged.rb
CHANGED
@@ -203,10 +203,10 @@ module Camping
|
|
203
203
|
# </div>
|
204
204
|
#
|
205
205
|
def R(c,*g)
|
206
|
-
p=/\(.+?\)
|
207
|
-
g.inject(c.urls.find{|x|x.scan(p).size==g.size}.dup){|s,a|
|
206
|
+
p,h=/\(.+?\)/,g.grep(Hash)
|
207
|
+
(g-=h).inject(c.urls.find{|x|x.scan(p).size==g.size}.dup){|s,a|
|
208
208
|
s.sub p,C.escape((a[a.class.primary_key]rescue a))
|
209
|
-
}
|
209
|
+
}+(h.any?? "?"+h[0].map{|x|x.map{|z|C.escape z}*"="}*"&": "")
|
210
210
|
end
|
211
211
|
|
212
212
|
# Shows AR validation errors for the object passed.
|
@@ -337,7 +337,7 @@ module Camping
|
|
337
337
|
a.shift if a[0]==:render
|
338
338
|
m=Mab.new({},self)
|
339
339
|
s=m.capture{send(*a,&b)}
|
340
|
-
s=m.layout{s} if /^_/!~a[0].to_s and m.respond_to?:layout
|
340
|
+
s=m.capture{send(:layout){s}} if /^_/!~a[0].to_s and m.respond_to?:layout
|
341
341
|
s
|
342
342
|
end
|
343
343
|
|
@@ -368,12 +368,26 @@ module Camping
|
|
368
368
|
#
|
369
369
|
def r(s, b, h = {}); @status = s; @headers.merge!(h); @body = b; end
|
370
370
|
|
371
|
+
# Turn a controller into an array. This is designed to be used to pipe
|
372
|
+
# controllers into the <tt>r</tt> method. A great way to forward your
|
373
|
+
# requests!
|
374
|
+
#
|
375
|
+
# class Read < '/(\d+)'
|
376
|
+
# def get(id)
|
377
|
+
# Post.find(id)
|
378
|
+
# rescue
|
379
|
+
# r *Blog.get(:NotFound, @env.REQUEST_URI)
|
380
|
+
# end
|
381
|
+
# end
|
382
|
+
#
|
383
|
+
def to_a;[@status, @body, @headers] end
|
384
|
+
|
371
385
|
def initialize(r, e, m) #:nodoc:
|
372
386
|
e = H[e.to_hash]
|
373
387
|
@status, @method, @env, @headers, @root = 200, m.downcase, e,
|
374
388
|
{'Content-Type'=>'text/html'}, e.SCRIPT_NAME.sub(/\/$/,'')
|
375
389
|
@k = C.kp(e.HTTP_COOKIE)
|
376
|
-
qs = C.
|
390
|
+
qs = C.qsp(e.QUERY_STRING)
|
377
391
|
@in = r
|
378
392
|
if %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)|n.match(e.CONTENT_TYPE)
|
379
393
|
b = /(?:\r?\n|\A)#{Regexp::quote("--#$1")}(?:--)?\r$/
|
@@ -404,11 +418,11 @@ module Camping
|
|
404
418
|
end
|
405
419
|
o<<l
|
406
420
|
end
|
407
|
-
|
421
|
+
C.qsp(fn,'&;',fh,qs) if fn
|
408
422
|
fh[:tempfile].rewind if fh.is_a?H
|
409
423
|
end
|
410
424
|
elsif @method == "post"
|
411
|
-
qs.merge!(C.
|
425
|
+
qs.merge!(C.qsp(@in.read))
|
412
426
|
end
|
413
427
|
@cookies, @input = @k.dup, qs.dup
|
414
428
|
end
|
@@ -427,7 +441,9 @@ module Camping
|
|
427
441
|
# Used by the web server to convert the current request to a string. If you need to
|
428
442
|
# alter the way Camping builds HTTP headers, consider overriding this method.
|
429
443
|
def to_s
|
430
|
-
|
444
|
+
a=[]
|
445
|
+
@headers.map{|k,v|[*v].map{|x|a<<"#{k}: #{x}"}}
|
446
|
+
"Status: #{@status}#{Z+a*Z+Z*2+@body}"
|
431
447
|
end
|
432
448
|
|
433
449
|
end
|
@@ -518,7 +534,7 @@ module Camping
|
|
518
534
|
end
|
519
535
|
constants.map { |c|
|
520
536
|
k=const_get(c)
|
521
|
-
k.send:include,C,Base,Models
|
537
|
+
k.send :include,C,Base,Models
|
522
538
|
r[0,0]=k if !r.include?k
|
523
539
|
k.meta_def(:urls){["/#{c.downcase}"]}if !k.respond_to?:urls
|
524
540
|
}
|
@@ -614,28 +630,28 @@ module Camping
|
|
614
630
|
|
615
631
|
# Parses a query string into an Camping::H object.
|
616
632
|
#
|
617
|
-
# input = Camping.
|
633
|
+
# input = Camping.qsp("name=Philarp+Tremain&hair=sandy+blonde")
|
618
634
|
# input.name
|
619
635
|
# #=> "Philarp Tremaine"
|
620
636
|
#
|
621
637
|
# Also parses out the Hash-like syntax used in PHP and Rails and builds
|
622
638
|
# nested hashes from it.
|
623
639
|
#
|
624
|
-
# input = Camping.
|
640
|
+
# input = Camping.qsp("post[id]=1&post[user]=_why")
|
625
641
|
# #=> {'post' => {'id' => '1', 'user' => '_why'}}
|
626
642
|
#
|
627
|
-
def
|
643
|
+
def qsp(qs, d='&;', y=nil, z=H[])
|
628
644
|
m = proc {|_,o,n|o.u(n,&m)rescue([*o]<<n)}
|
629
645
|
(qs||'').
|
630
646
|
split(/[#{d}] */n).
|
631
|
-
inject(H[]) { |h,p| k, v=un(p).split('=',2)
|
647
|
+
inject((b,z=z,H[])[0]) { |h,p| k, v=un(p).split('=',2)
|
632
648
|
h.u(k.split(/[\]\[]+/).reverse.
|
633
|
-
inject(v) { |x,i| H[i,x] },&m)
|
649
|
+
inject(y||v) { |x,i| H[i,x] },&m)
|
634
650
|
}
|
635
651
|
end
|
636
652
|
|
637
653
|
# Parses a string of cookies from the <tt>Cookie</tt> header.
|
638
|
-
def kp(s); c =
|
654
|
+
def kp(s); c = qsp(s, ';,'); end
|
639
655
|
|
640
656
|
# Fields a request through Camping. For traditional CGI applications, the method can be
|
641
657
|
# executed without arguments.
|
@@ -661,7 +677,7 @@ module Camping
|
|
661
677
|
X.M
|
662
678
|
k,a=X.D un("/#{e['PATH_INFO']}".gsub(/\/+/,'/'))
|
663
679
|
k.new(r,e,(m=e['REQUEST_METHOD']||"GET")).Y.service *a
|
664
|
-
rescue
|
680
|
+
rescue Object=>x
|
665
681
|
X::ServerError.new(r,e,'get').service(k,m,x)
|
666
682
|
end
|
667
683
|
|
@@ -718,7 +734,7 @@ module Camping
|
|
718
734
|
#
|
719
735
|
# Models cannot be referred to in Views at this time.
|
720
736
|
module Models
|
721
|
-
autoload:Base,'camping/db'
|
737
|
+
autoload :Base,'camping/db'
|
722
738
|
def Y;self;end
|
723
739
|
end
|
724
740
|
|
data/lib/camping.rb
CHANGED
@@ -1,30 +1,33 @@
|
|
1
1
|
%w[active_support markaby tempfile uri].map{|l|require l}
|
2
2
|
module Camping;Apps=[];C=self;S=IO.read(__FILE__).sub(/S=I.+$/,'')
|
3
|
-
P="Cam\ping Problem!";module Helpers;def R
|
4
|
-
urls.find{|x|x.scan(p).size==g.size}.dup){|s,a|s.sub p,C.
|
5
|
-
a.class.primary_key]rescue a))}
|
3
|
+
P="Cam\ping Problem!";module Helpers;def R(c,*g);p,h=/\(.+?\)/,g.grep(Hash)
|
4
|
+
(g-=h).inject(c.urls.find{|x|x.scan(p).size==g.size}.dup){|s,a|s.sub p,C.
|
5
|
+
escape((a[a.class.primary_key]rescue a))}+(h.any?? "?"+h[0].map{|x|x.map{|z|C.
|
6
|
+
escape z}*"="}*"&": "")end;def URL c='/',*a;c=R(c,*a)if c.
|
6
7
|
respond_to?:urls;c=self/c;c="//"+@env.HTTP_HOST+c if c[/^\//];URI(c)end;def/p
|
7
8
|
p[/^\//]?@root+p : p end;def errors_for o;ul.errors{o.errors.each_full{|x|li x}
|
8
9
|
}if o.errors.any?end end;module Base;include Helpers;attr_accessor:input,
|
9
10
|
:cookies,:env,:headers,:body,:status,:root;def method_missing*a,&b
|
10
|
-
a.shift if a[0]==:render;m=Mab.new({},self);s=m.capture{send(*a,&b)}
|
11
|
-
/^_/!~a[0].to_s and m.respond_to?:layout
|
12
|
-
|
11
|
+
a.shift if a[0]==:render;m=Mab.new({},self);s=m.capture{send(*a,&b)}
|
12
|
+
s=m.capture{send(:layout){s}} if /^_/!~a[0].to_s and m.respond_to?:layout
|
13
|
+
s end;def r s,b,h={};@status=s;@headers.merge!h;@body=b end
|
14
|
+
def redirect*a;r 302,'','Location'=>URL(*a)end;Z="\r\n"
|
15
|
+
def to_a;[@status,@body,@headers]end
|
13
16
|
def initialize r,e,m;e=H[e.to_hash];@status,@method,@env,@headers,@root=200,m.
|
14
17
|
downcase,e,{'Content-Type'=>"text/html"},e.SCRIPT_NAME.sub(/\/$/,'')
|
15
|
-
@k=C.kp e.HTTP_COOKIE;q=C.
|
18
|
+
@k=C.kp e.HTTP_COOKIE;q=C.qsp e.QUERY_STRING;@in=r
|
16
19
|
if%r|\Amultipart/form-.*boundary=\"?([^\";,]+)|n.match e.CONTENT_TYPE
|
17
20
|
b=/(?:\r?\n|\A)#{Regexp::quote("--#$1")}(?:--)?\r$/;until@in.eof?;fh=H[];for l in@in
|
18
21
|
case l;when Z;break;when/^Content-D.+?: form-data;/;fh.u H[*$'.
|
19
22
|
scan(/(?:\s(\w+)="([^"]+)")/).flatten];when/^Content-Type: (.+?)(\r$|\Z)/m;fh[
|
20
23
|
:type]=$1;end;end;fn=fh[:name];o=if fh[:filename];o=fh[:tempfile]=Tempfile.new(:C)
|
21
24
|
o.binmode;else;fh=""end;while l=@in.read(16384);if l=~b;o<<$`.chomp;@in.seek(-$'.
|
22
|
-
size,IO::SEEK_CUR);break;end;o<<l;end;
|
23
|
-
fh.is_a?H;end;elsif@method=="post";q.u C.
|
25
|
+
size,IO::SEEK_CUR);break;end;o<<l;end;C.qsp(fn,'&;',fh,q) if fn;fh[:tempfile].rewind if
|
26
|
+
fh.is_a?H;end;elsif@method=="post";q.u C.qsp(@in.read)end;@cookies,@input=
|
24
27
|
@k.dup,q.dup end;def service*a;@body=send(@method,*a)if respond_to?@method
|
25
28
|
@headers["Set-Cookie"]=@cookies.map{|k,v|"#{k}=#{C.escape(v)}; path=#{self/'/'
|
26
|
-
}"if v!=@k[k]}-[nil];self end;def to_s;
|
27
|
-
|
29
|
+
}"if v!=@k[k]}-[nil];self end;def to_s;a=[];@headers.map{|k,v|[*v].map{|x|a<<
|
30
|
+
"#{k}: #{x}"}};"Status: #{@status}#{Z+a*Z+Z*2+@body}"end;end
|
28
31
|
X=module Controllers;@r=[];class<<self;def r;@r;end;def R*u;r=@r;Class.new{
|
29
32
|
meta_def(:urls){u};meta_def(:inherited){|x|r<<x}}end;def M;def M;end;constants.map{|c|
|
30
33
|
k=const_get(c);k.send:include,C,Base,Models;r[0,0]=k if !r.include?k;k.meta_def(
|
@@ -36,12 +39,12 @@ ServerError<R();def get k,m,e;r(500,Mab.new{h1 P;h2"#{k}.#{m}";h3"#{e.class
|
|
36
39
|
self;def goes m;eval S.gsub(/Camping/,m.to_s).gsub("A\pps=[]","Cam\ping::Apps<<\
|
37
40
|
self"),TOPLEVEL_BINDING;end;def escape s;s.to_s.gsub(/[^ \w.-]+/n){'%'+($&.
|
38
41
|
unpack('H2'*$&.size)*'%').upcase}.tr(' ','+')end;def un s;s.tr('+',' ').gsub(
|
39
|
-
/%([\da-f]{2})/in){[$1].pack('H*')}end;def
|
40
|
-
n,&m)rescue([*o]<<n)};q.to_s.split(/[#{d}] */n).inject(H[]){|h,p|k,v=un(p).
|
41
|
-
split('=',2);h.u k.split(/[\]\[]+/).reverse.inject(v){|x,i|H[i,x]},&m}end;def
|
42
|
-
kp s;c=
|
42
|
+
/%([\da-f]{2})/in){[$1].pack('H*')}end;def qsp q,d='&;',y=nil,z=H[];m=proc{|_,o,n|o.u(
|
43
|
+
n,&m)rescue([*o]<<n)};q.to_s.split(/[#{d}] */n).inject((b,z=z,H[])[0]){|h,p|k,v=un(p).
|
44
|
+
split('=',2);h.u k.split(/[\]\[]+/).reverse.inject(y||v){|x,i|H[i,x]},&m}end;def
|
45
|
+
kp s;c=qsp(s,';,')end;def run r=$stdin,e=ENV;X.M;k,a=X.D un("/#{e[
|
43
46
|
'PATH_INFO']}".gsub(/\/+/,'/'));k.new(r,e,(m=e['REQUEST_METHOD']||"GET")).Y.
|
44
|
-
service *a;rescue
|
47
|
+
service *a;rescue Object=>x;X::ServerError.new(r,e,'get').service(k,m,x)end
|
45
48
|
def method_missing m,c,*a;X.M;k=X.const_get(c).new(StringIO.new,H['HTTP_HOST',
|
46
49
|
'','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s);H.new(a.pop).each{|e,f|k.send(
|
47
50
|
"#{e}=",f)}if Hash===a[-1];k.service *a;end;end;module Views;include X,Helpers
|
data/lib/camping/fastcgi.rb
CHANGED
@@ -58,6 +58,8 @@ module Camping
|
|
58
58
|
class FastCGI
|
59
59
|
CHUNK_SIZE=(4 * 1024)
|
60
60
|
|
61
|
+
attr_reader :mounts
|
62
|
+
|
61
63
|
# Creates a Camping::FastCGI class with empty mounts.
|
62
64
|
def initialize
|
63
65
|
@mounts = {}
|
@@ -69,71 +71,12 @@ class FastCGI
|
|
69
71
|
dir.gsub!(/\/+$/, '')
|
70
72
|
@mounts[dir] = app
|
71
73
|
end
|
74
|
+
|
72
75
|
#
|
73
76
|
# Starts the FastCGI main loop.
|
74
|
-
def start
|
77
|
+
def start(&blk)
|
75
78
|
FCGI.each do |req|
|
76
|
-
|
77
|
-
begin
|
78
|
-
root, path = "/"
|
79
|
-
if ENV['FORCE_ROOT'] and ENV['FORCE_ROOT'].to_i == 1
|
80
|
-
path = req.env['REQUEST_URI']
|
81
|
-
else
|
82
|
-
root = req.env['SCRIPT_NAME']
|
83
|
-
path = req.env['PATH_INFO']
|
84
|
-
end
|
85
|
-
|
86
|
-
dir, app = @mounts.max { |a,b| match(path, a[0]) <=> match(path, b[0]) }
|
87
|
-
unless dir and app
|
88
|
-
dir, app = '/', Camping
|
89
|
-
end
|
90
|
-
yield dir, app if block_given?
|
91
|
-
|
92
|
-
req.env['SERVER_SCRIPT_NAME'] = req.env['SCRIPT_NAME']
|
93
|
-
req.env['SERVER_PATH_INFO'] = req.env['PATH_INFO']
|
94
|
-
req.env['SCRIPT_NAME'] = File.join(root, dir)
|
95
|
-
req.env['PATH_INFO'] = path.gsub(/^#{dir}/, '')
|
96
|
-
|
97
|
-
controller = app.run(req.in, req.env)
|
98
|
-
sendfile = nil
|
99
|
-
headers = {}
|
100
|
-
controller.headers.each do |k, v|
|
101
|
-
if k =~ /^X-SENDFILE$/i and !ENV['SERVER_X_SENDFILE']
|
102
|
-
sendfile = v
|
103
|
-
else
|
104
|
-
headers[k] = v
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
body = controller.body
|
109
|
-
controller.body = ""
|
110
|
-
controller.headers = headers
|
111
|
-
|
112
|
-
req.out << controller.to_s
|
113
|
-
if sendfile
|
114
|
-
File.open(sendfile, "rb") do |f|
|
115
|
-
while chunk = f.read(CHUNK_SIZE) and chunk.length > 0
|
116
|
-
req.out << chunk
|
117
|
-
end
|
118
|
-
end
|
119
|
-
elsif body.respond_to? :read
|
120
|
-
while chunk = body.read(CHUNK_SIZE) and chunk.length > 0
|
121
|
-
req.out << chunk
|
122
|
-
end
|
123
|
-
body.close if body.respond_to? :close
|
124
|
-
else
|
125
|
-
req.out << body.to_s
|
126
|
-
end
|
127
|
-
rescue Exception => e
|
128
|
-
req.out << "Content-Type: text/html\r\n\r\n" +
|
129
|
-
"<h1>Camping Problem!</h1>" +
|
130
|
-
"<h2><strong>#{root}</strong>#{path}</h2>" +
|
131
|
-
"<h3>#{e.class} #{esc e.message}</h3>" +
|
132
|
-
"<ul>" + e.backtrace.map { |bt| "<li>#{esc bt}</li>" }.join + "</ul>" +
|
133
|
-
"<hr /><p>#{req.env.inspect}</p>"
|
134
|
-
ensure
|
135
|
-
req.finish
|
136
|
-
end
|
79
|
+
camp_do(req, &blk)
|
137
80
|
end
|
138
81
|
end
|
139
82
|
|
@@ -173,7 +116,7 @@ class FastCGI
|
|
173
116
|
fast.start do |dir, app|
|
174
117
|
Dir[File.join(path, dir, '*.rb')].each do |script|
|
175
118
|
smount = "/" + File.basename(script, '.rb')
|
176
|
-
script_load[script] unless
|
119
|
+
script_load[script] unless fast.mounts.has_key? smount
|
177
120
|
end
|
178
121
|
end
|
179
122
|
else
|
@@ -183,6 +126,71 @@ class FastCGI
|
|
183
126
|
|
184
127
|
private
|
185
128
|
|
129
|
+
def camp_do(req)
|
130
|
+
root, path, dir, app = "/"
|
131
|
+
if ENV['FORCE_ROOT'] and ENV['FORCE_ROOT'].to_i == 1
|
132
|
+
path = req.env['SCRIPT_NAME']
|
133
|
+
else
|
134
|
+
root = req.env['SCRIPT_NAME']
|
135
|
+
path = req.env['PATH_INFO']
|
136
|
+
end
|
137
|
+
|
138
|
+
dir, app = @mounts.max { |a,b| match(path, a[0]) <=> match(path, b[0]) }
|
139
|
+
unless dir and app
|
140
|
+
dir, app = '/', Camping
|
141
|
+
end
|
142
|
+
yield dir, app if block_given?
|
143
|
+
|
144
|
+
req.env['SERVER_SCRIPT_NAME'] = req.env['SCRIPT_NAME']
|
145
|
+
req.env['SERVER_PATH_INFO'] = req.env['PATH_INFO']
|
146
|
+
req.env['SCRIPT_NAME'] = File.join(root, dir)
|
147
|
+
req.env['PATH_INFO'] = path.gsub(/^#{dir}/, '')
|
148
|
+
|
149
|
+
controller = app.run(SeekStream.new(req.in), req.env)
|
150
|
+
sendfile = nil
|
151
|
+
headers = {}
|
152
|
+
controller.headers.each do |k, v|
|
153
|
+
if k =~ /^X-SENDFILE$/i and !ENV['SERVER_X_SENDFILE']
|
154
|
+
sendfile = v
|
155
|
+
else
|
156
|
+
headers[k] = v
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
body = controller.body
|
161
|
+
controller.body = ""
|
162
|
+
controller.headers = headers
|
163
|
+
|
164
|
+
req.out << controller.to_s
|
165
|
+
if sendfile
|
166
|
+
File.open(sendfile, "rb") do |f|
|
167
|
+
while chunk = f.read(CHUNK_SIZE) and chunk.length > 0
|
168
|
+
req.out << chunk
|
169
|
+
end
|
170
|
+
end
|
171
|
+
elsif body.respond_to? :read
|
172
|
+
while chunk = body.read(CHUNK_SIZE) and chunk.length > 0
|
173
|
+
req.out << chunk
|
174
|
+
end
|
175
|
+
body.close if body.respond_to? :close
|
176
|
+
else
|
177
|
+
req.out << body.to_s
|
178
|
+
end
|
179
|
+
rescue Exception => e
|
180
|
+
req.out << server_error(root, path, exc, req)
|
181
|
+
ensure
|
182
|
+
req.finish
|
183
|
+
end
|
184
|
+
|
185
|
+
def server_error(root, path, exc, req)
|
186
|
+
"Content-Type: text/html\r\n\r\n" +
|
187
|
+
"<h1>Camping Problem!</h1>" +
|
188
|
+
"<h2><strong>#{root}</strong>#{path}</h2>" +
|
189
|
+
"<h3>#{exc.class} #{esc exc.message}</h3>" +
|
190
|
+
"<ul>" + exc.backtrace.map { |bt| "<li>#{esc bt}</li>" }.join + "</ul>" +
|
191
|
+
"<hr /><p>#{req.env.inspect}</p>"
|
192
|
+
end
|
193
|
+
|
186
194
|
def match(path, mount)
|
187
195
|
m = path.match(/^#{Regexp::quote mount}(\/|$)/)
|
188
196
|
if m; m.end(0)
|
@@ -194,5 +202,43 @@ class FastCGI
|
|
194
202
|
str.gsub(/&/n, '&').gsub(/\"/n, '"').gsub(/>/n, '>').gsub(/</n, '<')
|
195
203
|
end
|
196
204
|
|
205
|
+
class SeekStream
|
206
|
+
def initialize(stream)
|
207
|
+
@last_read = ""
|
208
|
+
@stream = stream
|
209
|
+
@buffer = ""
|
210
|
+
end
|
211
|
+
def eof?
|
212
|
+
@buffer.empty? && @stream.eof?
|
213
|
+
end
|
214
|
+
def each
|
215
|
+
while true
|
216
|
+
pull(1024) until eof? or @buffer.index("\n")
|
217
|
+
return nil if eof?
|
218
|
+
yield @buffer.slice!(0..(@buffer.index("\n") || -1))
|
219
|
+
end
|
220
|
+
end
|
221
|
+
def pull(len)
|
222
|
+
@buffer += @stream.read(len).to_s
|
223
|
+
end
|
224
|
+
def read(len = 16384)
|
225
|
+
pull(len)
|
226
|
+
@last_read =
|
227
|
+
if eof?
|
228
|
+
nil
|
229
|
+
else
|
230
|
+
@buffer.slice!(0...len)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
def seek(len, typ)
|
234
|
+
raise NotImplementedError, "only IO::SEEK_CUR is supported with SeekStream" if typ != IO::SEEK_CUR
|
235
|
+
raise NotImplementedError, "only rewinding is supported with SeekStream" if len > 0
|
236
|
+
raise NotImplementedError, "rewinding #{-len} past the buffer #{@last_read.size} start not supported with SeekStream" if -len > @last_read.size
|
237
|
+
@buffer = @last_read[len..-1] + @buffer
|
238
|
+
@last_read = ""
|
239
|
+
self
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
197
243
|
end
|
198
244
|
end
|
data/lib/camping/reloader.rb
CHANGED
@@ -146,13 +146,7 @@ class Reloader
|
|
146
146
|
Camping::Models::Base.logger = Logger.new(@log == "-" ? STDOUT : @log)
|
147
147
|
end
|
148
148
|
|
149
|
-
|
150
|
-
Camping::Models::Session.create_schema
|
151
|
-
rescue MissingSourceFile
|
152
|
-
puts "** #$0 stopped: SQLite3 not found, please install."
|
153
|
-
puts "** See http://code.whytheluckystiff.net/camping/wiki/BeAlertWhenOnSqlite3 for instructions."
|
154
|
-
exit
|
155
|
-
end
|
149
|
+
Camping::Models::Session.create_schema
|
156
150
|
|
157
151
|
if @database and @database[:adapter] == 'sqlite3'
|
158
152
|
begin
|
data/lib/camping/webrick.rb
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'mosquito'
|
2
|
+
|
3
|
+
Camping.goes :XhtmlTrans
|
4
|
+
|
5
|
+
module XhtmlTrans
|
6
|
+
module Controllers
|
7
|
+
class WithLayout < R '/with_layout'
|
8
|
+
def get
|
9
|
+
render :with_layout
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class WithoutLayout < R '/without_layout'
|
14
|
+
def get
|
15
|
+
render :_without_layout
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module Views
|
21
|
+
def layout
|
22
|
+
xhtml_transitional do
|
23
|
+
head do title "title" end
|
24
|
+
body do capture { yield } end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def with_layout
|
29
|
+
h1 "With layout"
|
30
|
+
end
|
31
|
+
|
32
|
+
def _without_layout
|
33
|
+
xhtml_transitional do
|
34
|
+
head do title "title" end
|
35
|
+
body do h1 "Without layout" end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class XhtmlTransTest < Camping::FunctionalTest
|
42
|
+
def test_with_layout
|
43
|
+
get '/with_layout'
|
44
|
+
|
45
|
+
assert(@response.body =~ /DOCTYPE/, "No doctype defined")
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_without_layout
|
49
|
+
get '/without_layout'
|
50
|
+
|
51
|
+
assert(@response.body =~ /DOCTYPE/, "No doctype defined")
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.0.
|
2
|
+
rubygems_version: 0.9.0.8
|
3
3
|
specification_version: 1
|
4
4
|
name: camping
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version:
|
7
|
-
date:
|
6
|
+
version: 1.5.180
|
7
|
+
date: 2007-01-06 00:00:00 -07:00
|
8
8
|
summary: minature rails for stay-at-home moms
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- Rakefile
|
35
35
|
- bin/camping
|
36
36
|
- doc/camping.1.gz
|
37
|
+
- test/test_xhtml_trans.rb
|
37
38
|
- lib/camping
|
38
39
|
- lib/camping.rb
|
39
40
|
- lib/camping-unabridged.rb
|
@@ -45,9 +46,9 @@ files:
|
|
45
46
|
- extras/permalink.gif
|
46
47
|
- extras/Camping.gif
|
47
48
|
- extras/flipbook_rdoc.rb
|
49
|
+
- examples/campsh.rb
|
48
50
|
- examples/tepee.rb
|
49
51
|
- examples/blog.rb
|
50
|
-
- examples/campsh.rb
|
51
52
|
- CHANGELOG
|
52
53
|
test_files: []
|
53
54
|
|