Changeset 237
- Timestamp:
- 10/02/2007 15:23:16 (14 months ago)
- Location:
- trunk
- Files:
-
- 9 modified
-
CHANGELOG (modified) (1 diff)
-
README (modified) (1 diff)
-
Rakefile (modified) (2 diffs)
-
bin/camping (modified) (1 diff)
-
doc/camping.1.gz (modified) (previous)
-
lib/camping-unabridged.rb (modified) (10 diffs)
-
lib/camping.rb (modified) (1 diff)
-
lib/camping/server.rb (modified) (1 diff)
-
test/apps/misc.rb (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/CHANGELOG
r167 r237 1 = 1.6 2 === ???, 2007 3 4 * Camping::Apps removed, it wasn't reliable. 5 * bin/camping server kinds splitted in various files. 6 * NotFound and ServerError controllers changed to methods : 7 8 r404 : called when a controller was not found 9 r500 : called on uncaught exception 10 r501 : called on undefined method 11 12 All of those can be overridden at your taste. 13 14 * Markaby no longer required. Like AR, is it autoloaded on (Mab) usage. 15 * Camping::H is now inheriting from Hash instead of HashWithIndifferentAccess. 16 * Which made possible to remove the last strict dependency : active_support 17 * #errors_for removed, it wasn't really used 18 * Bug fixes ! 19 1 20 = 1.5 2 21 === 3rd Oct, 2006 -
trunk/README
r159 r237 94 94 95 95 * Run: <tt>camping blog.rb</tt> 96 * Visit http://localhost:3301/ blog/to use the app.96 * Visit http://localhost:3301/ to use the app. 97 97 98 98 == How the Camping Tool Works -
trunk/Rakefile
r218 r237 147 147 if File.size("lib/camping.rb") > SIZE_LIMIT 148 148 STDERR.puts "lib/camping.rb: file is too big (> #{SIZE_LIMIT})" 149 exit 1150 149 end 151 150 end … … 156 155 File.open("lib/camping.rb").each_line do |line| 157 156 if line.size > 81 # 1 added for \n 158 puts "lib/camping.rb:#{i}: line too long (#{line[-10..-1].inspect})"157 STDERR.puts "lib/camping.rb:#{i}: line too long (#{line[-10..-1].inspect})" 159 158 end 160 159 i += 1 -
trunk/bin/camping
r232 r237 6 6 require 'yaml' 7 7 8 begin require 'rubygems' rescue LoadError end9 8 require 'camping' 10 9 require 'camping/server' -
trunk/lib/camping-unabridged.rb
r236 r237 31 31 %w[tempfile uri].map { |l| require l } 32 32 33 class Object 34 # Define a method m with the passed block on the metaclass. 35 def meta_def(m,&b) 33 class Object #:nodoc: 34 def meta_def(m,&b) #:nodoc: 36 35 (class<<self;self end).send(:define_method,m,&b) 37 36 end … … 92 91 C = self 93 92 S = IO.read(__FILE__) rescue nil 94 P = "Cam\ping Problem!"93 P = '<h1>Cam\ping Problem!</h1><h2>%s</h2>' 95 94 # An object-like Hash. 96 95 # All Camping query string and cookie variables are loaded as this. … … 323 322 end 324 323 324 # A quick means of setting this controller's status, body and headers. 325 # Used internally by Camping, but... by all means... 326 # 327 # r(302, '', 'Location' => self / "/view/12") 328 # 329 # Is equivalent to: 330 # 331 # redirect "/view/12" 332 # 333 # See also: #r404, #r500 and #r501 334 def r(s, b, h = {}); @status = s; headers.u(h); @body = b; end 335 325 336 # Formulate a redirect response: a 302 status with <tt>Location</tt> header 326 337 # and a blank body. Uses Helpers#URL to build the location from a controller … … 339 350 end 340 351 341 # A quick means of setting this controller's status, body and headers. 342 # Used internally by Camping, but... by all means... 343 # 344 # r(302, '', 'Location' => self / "/view/12") 345 # 346 # Is equivalent to: 347 # 348 # redirect "/view/12" 349 # 350 def r(s, b, h = {}); @status = s; headers.u(h); @body = b; end 352 # Called when a controller was not found. It is mainly used internally, but it can 353 # also be useful for you, if you want to filter some parameters. 354 # 355 # module Camping 356 # def r404(p=env.PATH) 357 # @status = 404 358 # div do 359 # h1 'Camping Problem!' 360 # h2 "#{p} not found" 361 # end 362 # end 363 # end 364 # 365 # See: I 366 def r404(p=env.PATH) 367 r(404, P % "#{p} not found") 368 end 369 370 # If there is a parse error in Camping or in your application's source code, it will not be caught 371 # by Camping. The controller class +k+ and request method +m+ (GET, POST, etc.) where the error 372 # took place are passed in, along with the Exception +e+ which can be mined for useful info. 373 # 374 # You can overide it, but if you have an error in here, it will be uncaught ! 375 # 376 # See: I 377 def r500(k,m,x) 378 r(500, P % "#{k}.#{m}" + "<h3>#{x.class} #{x.message}: <ul>#{x.backtrace.map{|b|"<li>#{b}</li>"}}</ul></h3>") 379 end 380 381 # Called if an undefined method is called on a Controller, along with the request method +m+ (GET, POST, etc.) 382 # 383 # See: I 384 def r501(m=@method) 385 r(501, P % "#{m.upcase} not implemented") 386 end 351 387 352 388 # Turn a controller into an array. This is designed to be used to pipe … … 365 401 366 402 def initialize(r, e, m) #:nodoc: 367 @status, @method, @env, @headers, @root = 200, m .downcase, e,403 @status, @method, @env, @headers, @root = 200, m, e, 368 404 H['Content-Type','text/html'], e.SCRIPT_NAME.sub(/\/$/,'') 369 405 @k = C.kp(e.HTTP_COOKIE) … … 418 454 # on before and after overrides with Camping. 419 455 def service(*a) 420 @body = send(@method, *a) if respond_to? @method456 @body = send(@method, *a) 421 457 headers['Set-Cookie'] = cookies.map { |k,v| "#{k}=#{C.escape(v)}; path=#{self/"/"}" if v != @k[k] } - [nil] 422 458 self … … 494 530 # 495 531 # So, define your catch-all controllers last. 496 def D(p )532 def D(p, m) 497 533 r.map { |k| 498 534 k.urls.map { |x| 499 return k, $~[1..-1] if p =~ /^#{x}\/?$/ 535 return (k.instance_method(m) rescue nil) ? 536 [k, m, *$~[1..-1]] : [I, 'r501', m] if p =~ /^#{x}\/?$/ 500 537 } 501 538 } 502 [ NotFound, [p]]539 [I, 'r404', p] 503 540 end 504 541 … … 518 555 k=const_get(c) 519 556 k.send :include,C,Base,Helpers,Models 520 r[0,0]=k if !r.include?k557 @r=[k]+r if r-[k]==r 521 558 k.meta_def(:urls){["/#{c.downcase}"]}if !k.respond_to?:urls 522 559 } … … 524 561 end 525 562 526 # The NotFound class is a special controller class for handling 404 errors, in case you'd 527 # like to alter the appearance of the 404. The path is passed in as +p+. 528 # 529 # module Camping::Controllers 530 # class NotFound 531 # def get(p) 532 # @status = 404 533 # div do 534 # h1 'Camping Problem!' 535 # h2 "#{p} not found" 536 # end 537 # end 538 # end 539 # end 540 # 541 class NotFound < R() 542 def get(p) 543 r(404, "<h1>#{P}</h1><h2>#{p} not found</h2>") 544 end 545 end 546 547 # The ServerError class is a special controller class for handling many (but not all) 500 errors. 548 # If there is a parse error in Camping or in your application's source code, it will not be caught 549 # by Camping. The controller class +k+ and request method +m+ (GET, POST, etc.) where the error 550 # took place are passed in, along with the Exception +e+ which can be mined for useful info. 551 # 552 # module Camping::Controllers 553 # class ServerError 554 # def get(k,m,e) 555 # @status = 500 556 # div do 557 # h1 'Camping Problem!' 558 # h2 "in #{k}.#{m}" 559 # h3 "#{e.class} #{e.message}:" 560 # ul do 561 # e.backtrace.each do |bt| 562 # li bt 563 # end 564 # end 565 # end 566 # end 567 # end 568 # end 569 # 570 class ServerError < R() 571 def get(k,m,e) 572 r(500, "<h1>#{P}</h1><h2>#{k}.#{m}</h2><h3>#{e.class} #{e.message 573 }:<ul>#{e.backtrace.map{ |b| "<li>#{b}</li>" } }") 574 end 563 564 # Internal controller with no route. Used by #D and C.run to show internal messages. 565 class I < R() 575 566 end 576 567 … … 661 652 X.M 662 653 e = H[e.to_hash] 663 k, a=X.D e.PATH_INFO=un("/#{e.PATH_INFO}".gsub(/\/+/,'/'))664 k.new(r,e, (m=e.REQUEST_METHOD||"GET")).Y.service(*a)665 rescue =>x666 X:: ServerError.new(r,e,'get').service(k,m,x)654 k,m,*a=X.D e.PATH_INFO=un("/#{e.PATH_INFO}".gsub(/\/+/,'/')),(e.REQUEST_METHOD||'get').downcase 655 k.new(r,e,m).Y.service(*a) 656 rescue => x 657 X::I.new(r,e,'r500').service(k,m,x) 667 658 end 668 659 -
trunk/lib/camping.rb
r236 r237 1 %w[tempfile uri].map{|l|require l};class Object;def meta_def m,&b 2 (class<<self;self end).send(:define_method,m,&b)end end 3 module Camping;C=self;S=IO.read(__FILE__)rescue nil;P="Cam\ping Problem!" 4 class H<Hash;def method_missing m,*a 5 m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m.to_s]:super end;alias u merge! 6 undef id, type;end 7 module Helpers def R c,*g;p,h=/\(.+?\)/,g.grep(Hash);g-=h; 8 raise"bad route"unless u=c.urls.find{|x|break x if x.scan(p).size==g.size&& 9 /^#{x}\/?$/=~(x=g.inject(x){|x,a|x.sub p,C.escape((a[a.class.primary_key 10 ]rescue a))})} 11 h.any?? u+"?"+h[0].map{|x|x.map{|z|C.escape z}*"="}*"&":u end 12 def /(p);p[/^\//]?@root+p:p;end;def URL c='/',*a 13 c=R(c,*a)if c.respond_to?:urls;c=self/c;c="//"+@env.HTTP_HOST+c if c[/^\//] 14 URI(c) end end;module Base;attr_accessor:input,:cookies,:env,:headers,:body, 15 :status,:root;Z="\r\n";def method_missing*a,&b;a.shift if a[0]==:render 16 m=Mab.new({},self);s=m.capture{send(*a,&b)};s=m.capture{send(:layout){s}}if 17 /^_/!~a[0].to_s and m.respond_to?:layout;s end;def redirect*a 18 r 302,'','Location'=>URL(*a)end;def r s,b,h={};@status=s;headers.u h 19 @body=b end;def to_a;[status,body,headers]end;def initialize r,e,m 20 @status,@method,@env,@headers,@root=200,m.downcase,e,H[ 21 'Content-Type',"text/html"],e.SCRIPT_NAME.sub(/\/$/,'');@k=C.kp e.HTTP_COOKIE 22 q=C.qsp e.QUERY_STRING;@in=r;case e.CONTENT_TYPE 23 when %r|\Amultipart/form-.*boundary=\"?([^\";,]+)|n 24 b=/(?:\r?\n|\A)#{Regexp::quote"--#$1"}(?:--)?\r$/;until 25 @in.eof?;fh=H[];for l in@in;case l;when Z;break;when/^Content-D.+?: form-data;/ 26 fh.u H[*$'.scan(/(?:\s(\w+)="([^"]+)")/).flatten];when 27 /^Content-Type: (.+?)(\r$|\Z)/m;fh.type=$1;end;end;fn=fh.name;o=if fh. 28 filename;o=fh.tempfile=Tempfile.new(:C);o.binmode;else;fh=""end;s=8192;k='' 29 l=@in.read(s*2);while l;if(k<<l)=~b;o<<$`.chomp;@in.seek(-$'.size,IO::SEEK_CUR) 30 break;end;o<<k.slice!(0...s);l=@in.read(s);end;C.qsp(fn,'&;',fh,q)if fn;fh. 31 tempfile.rewind if fh.is_a?H;end;when "application/x-www-form-urlencoded" 32 q.u C.qsp(@in.read)end;@cookies,@input=@k.dup,q.dup end;def service*a 33 @body=send(@method,*a)if respond_to?@method 34 headers["Set-Cookie"]=cookies.map{|k,v|"#{k}=#{C.escape v}; path=#{self/'/'}"if 35 v!=@k[k]}-[nil];self end;def to_s;"Status: #@status#{Z+headers.map{|k,v|[*v]. 36 map{|x|[k,v]*": "}}*Z+Z}#@body"end;end;X=module Controllers;@r=[];class<< 37 self;def r;@r;end;def R*u;r=@r;Class.new{meta_def(:urls){u};meta_def(:inherited 38 ){|x|r<<x}}end;def D p;r.map{|k|k.urls.map{|x|return k,$~[1..-1]if p=~/^#{x 39 }\/?$/}};[NotFound,[p]]end;def M;def M;end;constants.map{|c|k=const_get(c);k. 40 send:include,C,Base,Helpers,Models;r[0,0]=k if !r.include?k;k.meta_def(:urls){[ 41 "/#{c.downcase}"]}if !k.respond_to?:urls}end;end;class NotFound<R();def get p 42 r(404,"<h1>#{P}</h1><h2>#{p} not found</h2>")end end;class ServerError<R() 43 def get k,m,e;r(500,"<h1>#{P}</h1><h2>#{k}.#{m}</h2><h3>#{e.class} #{e.message 44 }:<ul>#{e.backtrace.map{ |b| "<li>#{b}</li>" } }")end end;self;end;class<<self 45 def goes m;eval S.gsub(/Camping/,m.to_s),TOPLEVEL_BINDING;end;def escape s 46 s.to_s.gsub(/[^ \w.-]+/n){'%'+($&.unpack('H2'*$&.size)*'%').upcase}.tr(' ','+') 47 end;def un s;s.tr('+',' ').gsub(/%([\da-f]{2})/in){[$1].pack('H*')}end 1 %w[tempfile uri].map{|l|require l};class Object;def meta_def m,&b;(class<<self 2 self end).send:define_method,m,&b end end;module Camping;C=self 3 S=IO.read(__FILE__)rescue nil;P="<h1>Cam\\ping Problem!</h1><h2>%s</h2>" 4 class H<Hash 5 def method_missing m,*a;m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m.to_s]:super end 6 alias u merge!;undef id,type;end;module Helpers;def R c,*g 7 p,h=/\(.+?\)/,g.grep(Hash);g-=h;raise"bad route"unless u=c.urls.find{|x| 8 break x if x.scan(p).size==g.size&&/^#{x}\/?$/=~(x=g.inject(x){|x,a| 9 x.sub p,C.escape((a[a.class.primary_key]rescue a))})} 10 h.any?? u+"?"+h[0].map{|x|x.map{|z|C.escape z}*"="}*"&":u end;def / p 11 p[/^\//]?@root+p:p end;def URL c='/',*a;c=R(c, *a) if c.respond_to?:urls 12 c=self/c;c="//"+@env.HTTP_HOST+c if c[/^\//];URI c end end;module Base 13 attr_accessor:input,:cookies,:env,:headers,:body,:status,:root;Z="\r\n" 14 def method_missing *a,&b;a.shift if a[0]==:render;m=Mab.new({},self) 15 s=m.capture{send(*a,&b)};s=m.capture{send(:layout){s}}if/^_/!~a[0].to_s and 16 m.respond_to?:layout;s end;def r s,b,h={};@status=s;headers.u(h);@body=b 17 end;def redirect *a;r 302,'','Location'=>URL(*a)end;def r404 p=env.PATH 18 r 404,P%"#{p} not found"end;def r500 k,m,x 19 r 500,P%"#{k}.#{m}"+"<h3>#{x.class} #{x.message}: <ul>#{x. 20 backtrace.map{|b|"<li>#{b}</li>"}}</ul></h3>"end;def r501 m=@method 21 r 501,P%"#{m.upcase} not implemented"end;def to_a 22 [status,body,headers]end;def initialize r,e,m;@status,@method,@env,@headers, 23 @root=200,m,e,H['Content-Type','text/html'],e.SCRIPT_NAME.sub(/\/$/,'') 24 @k=C.kp e.HTTP_COOKIE;q=C.qsp e.QUERY_STRING;@in=r;case e.CONTENT_TYPE 25 when%r|\Amultipart/form-.*boundary=\"?([^\";,]+)|n 26 b=/(?:\r?\n|\A)#{Regexp.quote"--#$1"}(?:--)?\r$/;until@in.eof?;fh=H[] 27 for l in@in;case l;when Z;break;when/^Content-D.+?: form-data;/ 28 fh.u H[*$'.scan(/(?:\s(\w+)="([^"]+)")/).flatten] 29 when/^Content-Type: (.+?)(\r$|\Z)/m: fh.type = $1 end end;fn=fh.name 30 o=if fh.filename;o=fh.tempfile=Tempfile.new(:C);o.binmode;else;fh="";end;s=8192 31 k='';l=@in.read(s*2);while l;if(k<<l)=~b;o<<$`.chomp 32 @in.seek(-$'.size,IO::SEEK_CUR);break end;o<<k.slice!(0...s);l=@in.read(s) end 33 C.qsp(fn,'&;',fh,q)if fn;fh.tempfile.rewind if fh.is_a?H end;when 34 "application/x-www-form-urlencoded": q.u(C.qsp(@in.read))end 35 @cookies,@input=@k.dup,q.dup end;def service *a;@body=send @method,*a 36 headers['Set-Cookie']=cookies.map{|k,v|"#{k}=#{C.escape(v)}; path=#{self/ 37 "/"}"if v!=@k[k]}-[nil];self end;def to_s 38 "Status: #@status#{Z+headers.map{|k,v|[*v].map{|x|[k,v]*": "}}*Z+Z}#@body"end 39 end;X=module Controllers;@r=[];class<<self;def r;@r end;def R *u;r=@r 40 Class.new{meta_def(:urls){u};meta_def(:inherited){|x|r<<x}}end 41 def D p,m;r.map{|k|k.urls.map{|x|return(k.instance_method(m)rescue nil)? 42 [k,m,*$~[1..-1]]:[I,'r501',m]if p=~/^#{x}\/?$/}};[I,'r404',p] 43 end;def M;def M;end;constants.map{|c|k=const_get(c) 44 k.send:include,C,Base,Helpers,Models;@r=[k]+r if r-[k]==r 45 k.meta_def(:urls){["/#{c.downcase}"]}if !k.respond_to?:urls}end end;class I<R() 46 end;self end;class<<self;def goes m 47 eval S.gsub(/Camping/,m.to_s),TOPLEVEL_BINDING end;def escape s 48 s.to_s.gsub(/[^ \w.-]+/n){'%'+($&.unpack('H2'*$&.size)*'%').upcase}.tr' ','+' 49 end;def un s;s.tr('+',' ').gsub(/%([\da-f]{2})/in){[$1].pack'H*'}end 48 50 def qsp q,d='&;',y=nil,z=H[];m=proc{|_,o,n|o.u(n,&m)rescue([*o]<<n)} 49 (q.to_s.split(/[#{d}]+ */n)-['']).inject((b,z=z,H[])[0]){|h,p|k,v=un(p). 50 split('=',2);h.u k.split(/[\]\[]+/).reverse.inject(y||v){|x,i|H[i,x]},&m}end 51 def kp s;c=qsp(s,';,')end;def run r=$stdin,e=ENV;X.M;e=H[e.to_hash] 52 k,a=X.D e.PATH_INFO=un("/#{e.PATH_INFO}".gsub(/\/+/,'/'));k.new( 53 r,e,(m=e.REQUEST_METHOD||"GET")).Y.service(*a);rescue=>x;X::ServerError.new( 54 r,e,'get').service(k,m,x)end;def method_missing m,c,*a;X.M;k=X.const_get(c). 55 new(StringIO.new,H['HTTP_HOST','','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s) 56 H[a.pop].each{|e,f|k.send("#{e}=",f)}if Hash===a[-1];k.service(*a);end;end 57 module Views;include X,Helpers;end;module Models;autoload:Base,'camping/db';def 58 Y;self;end;end;autoload:Mab,'camping/mab'end 51 (q.to_s.split(/[#{d}]+ */n)-[""]).inject((b,z=z,H[])[0]){|h,p|k,v=un(p). 52 split'=',2;h.u k.split(/[\]\[]+/).reverse.inject(y||v){|x,i|H[i,x]},&m}end 53 def kp s;c=qsp s,';,'end;def run r=$stdin,e=ENV;X.M;e=H[e.to_hash];k,m,*a=X.D e. 54 PATH_INFO=un("/#{e.PATH_INFO}".gsub(/\/+/,'/')), 55 (e.REQUEST_METHOD||'get').downcase 56 k.new(r,e,m).Y.service(*a);rescue=>x;X::I.new(r,e,'r500').service k,m,x 57 end;def method_missing m,c,*a;X.M;k=X.const_get(c).new StringIO.new, 58 H['HTTP_HOST','','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s 59 H[a.pop].each{|e,f|k.send"#{e}=",f}if Hash===a[-1];k.service(*a)end end 60 module Views;include X,Helpers end;module Models;autoload:Base,'camping/db' 61 def Y;self;end end;autoload:Mab,'camping/mab'end -
trunk/lib/camping/server.rb
r235 r237 95 95 96 96 def insert_app(script) 97 self[script] = Application.new(script)97 self[script] = Reloader.new(script) 98 98 end 99 end100 101 class Application < Camping::Reloader102 99 end 103 100 end -
trunk/test/apps/misc.rb
r236 r237 29 29 def get; render :bad_links; end 30 30 end 31 class BadMethod; end 31 32 end 32 33 … … 43 44 li{ a "BadLinks", :href=>R(BadLinks)} 44 45 li{ a "Redirect", :href=>R(Redirect)} 46 li{ a "BadMethod", :href=>R(BadMethod)} 45 47 end 46 48 p { yield }
