Changeset 134
- Timestamp:
- 07/12/2006 12:44:46 (2 years ago)
- Location:
- trunk
- Files:
-
- 6 modified
-
Rakefile (modified) (1 diff)
-
examples/blog.rb (modified) (1 diff)
-
lib/camping-unabridged.rb (modified) (15 diffs)
-
lib/camping.rb (modified) (1 diff)
-
lib/camping/db.rb (modified) (1 diff)
-
lib/camping/reloader.rb (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Rakefile
r131 r134 61 61 62 62 s.add_dependency('activesupport', '>=1.3.1') 63 s.add_dependency('markaby', '> 0.4')63 s.add_dependency('markaby', '>=0.4.65') 64 64 s.add_dependency('metaid') 65 65 s.required_ruby_version = '>= 1.8.2' -
trunk/examples/blog.rb
r129 r134 71 71 div do 72 72 code args.inspect; br; br 73 code ENV.inspect; br73 code @env.inspect; br 74 74 code "Link: #{R(Info, 1, 2)}" 75 75 end -
trunk/lib/camping-unabridged.rb
r129 r134 29 29 # in its <tt>examples/camping</tt> directory. 30 30 # 31 %w[active_ recordmarkaby metaid tempfile uri].each { |lib| require lib }31 %w[active_support/core_ext/hash markaby metaid tempfile uri].each { |lib| require lib } 32 32 33 33 # == Camping … … 207 207 # 208 208 def R(c,*g) 209 p =/\(.+?\)/209 p=/\(.+?\)/ 210 210 g.inject(c.urls.find{|x|x.scan(p).size==g.size}.dup){|s,a| 211 s.sub (p,C.escape((a.__send__(a.class.primary_key) rescue a)))211 s.sub p,C.escape((a[a.class.primary_key]rescue a)) 212 212 } 213 213 end 214 214 215 # Shows AR validation errors for the object passed. 215 216 # There is no output if there are no errors. … … 309 310 include Helpers 310 311 attr_accessor :input, :cookies, :env, :headers, :body, :status, :root 312 Z = "\r\n" 313 311 314 # Display a view, calling it by its method name +m+. If a <tt>layout</tt> 312 315 # method is found in Camping::Views, it will be used to wrap the HTML. … … 334 337 # If you have a <tt>layout</tt> method in Camping::Views, it will be used to 335 338 # wrap the HTML. 336 def method_missing(m, *a, &b) 337 str = m==:render ? markaview(*a, &b):eval("markaby.#{m}(*a, &b)") 338 str = markaview(:layout) { str } if Views.method_defined? :layout 339 str 339 def method_missing(*a,&b) 340 a.shift if a[0]==:render 341 m=markaby 342 s=m.capture{send(*a,&b)} 343 s=m.layout{s} if m.respond_to?:layout 344 "#{s}" 340 345 end 341 346 … … 380 385 for l in @in 381 386 case l 382 when "\r\n": break387 when Z: break 383 388 when /^Content-Disposition: form-data;/ 384 389 fh.u H[*$'.scan(/(?:\s(\w+)="([^"]+)")/).flatten] … … 419 424 def service(*a) 420 425 @body = send(@method, *a) if respond_to? @method 421 @headers['Set-Cookie'] = @cookies.map { |k,v| "#{k}=#{C.escape(v)}; path=#{self/"/"}" if v != @k[k] } .compact426 @headers['Set-Cookie'] = @cookies.map { |k,v| "#{k}=#{C.escape(v)}; path=#{self/"/"}" if v != @k[k] } - [nil] 422 427 self 423 428 end … … 426 431 # alter the way Camping builds HTTP headers, consider overriding this method. 427 432 def to_s 428 "Status: #{@status} \r\n#{@headers.map{|k,v|[*v].map{|x|"#{k}: #{x}"}}*"\r\n"}\r\n\r\n#{@body}"433 "Status: #{@status}#{Z+@headers.map{|k,v|[*v].map{|x|"#{k}: #{x}"}}*Z+Z*2+@body}" 429 434 end 430 435 431 436 def markaby #:nodoc: 432 Mab.new( instance_variables.map { |iv| 433 [iv[1..-1], instance_variable_get(iv)] } ) 434 end 435 436 def markaview m,*a,&b #:nodoc: 437 h=markaby 438 h.send m,*a,&b 439 h.to_s 437 Mab.new({},self) 440 438 end 441 439 end 442 440 441 X = 443 442 # Controllers is a module for placing classes which handle URLs. This is done 444 443 # by defining a route to each class using the Controllers::R method. … … 461 460 # uncaught by your application. 462 461 module Controllers 462 @r = [] 463 463 class << self 464 attr_accessor :routes464 def r; @r end 465 465 # Add routes to a controller class by piling them into the R method. 466 466 # … … 482 482 # Most of the time the rules inferred by dispatch method Controllers::D will get you 483 483 # by just fine. 484 def R(*u); Class.new{meta_def(:urls){u};meta_def(:inherited){|x|X.routes<<x}}; end 485 486 # Dispatch routes to controller classes. Classes are searched in no particular order. 484 def R *u 485 r=@r 486 Class.new { 487 meta_def(:urls){u} 488 meta_def(:inherited){|x|r<<x} 489 } 490 end 491 492 # Dispatch routes to controller classes. 487 493 # For each class, routes are checked for a match based on their order in the routing list 488 494 # given to Controllers::R. If no routes were given, the dispatcher uses a slash followed 489 495 # by the name of the controller lowercased. 496 # 497 # Controllers are searched in this order: 498 # 499 # # Classes without routes, since they refer to a very specific URL. 500 # # Classes with routes are searched in order of their creation. 501 # 502 # So, define your catch-all controllers last. 490 503 def D(path) 491 constants.each do |c|492 k = const_get(c)493 k.meta_def(:urls){%r!^/#{c.downcase}/?$!}if !k.respond_to? :urls494 case path when k.urls: return k, $~[1..-1] end495 end504 r.map { |k| 505 k.urls.map { |x| 506 return k, $~[1..-1] if path =~ /^#{x}\/?$/ 507 } 508 } 496 509 [NotFound, [path]] 510 end 511 512 # The route maker, this is called by Camping internally, you shouldn't need to call it. 513 # 514 # Still, it's worth know what this method does. Since Ruby doesn't keep track of class 515 # creation order, we're keeping an internal list of the controllers which inherit from R(). 516 # This method goes through and adds all the remaining routes to the beginning of the list 517 # and ensures all the controllers have the right mixins. 518 # 519 # Anyway, if you are calling the URI dispatcher from outside of a Camping server, you'll 520 # definitely need to call this at least once to set things up. 521 def M 522 def M; end 523 constants.map { |c| 524 k=const_get(c) 525 k.send:include,C,Base,Models 526 r[0,0]=k if !r.include?k 527 k.meta_def(:urls){["/#{c.downcase}"]}if !k.respond_to?:urls 528 } 497 529 end 498 530 end … … 552 584 end 553 585 end 586 self 554 587 end 555 588 … … 567 600 # 568 601 def goes(m) 569 eval(S.gsub(/Camping/,m.to_s),TOPLEVEL_BINDING) 570 Apps << const_get(m) 602 eval S.gsub(/Camping/,m.to_s).gsub("A\pps = []","Cam\ping::Apps<<self"), TOPLEVEL_BINDING 571 603 end 572 604 … … 609 641 # Parses a string of cookies from the <tt>Cookie</tt> header. 610 642 def kp(s); c = qs_parse(s, ';,'); end 611 612 def method_missing(m, c, *a)613 k = X.const_get(c)614 k.send :include, C, Base, Models615 k.allocate.method(m, *a)616 end617 643 618 644 # Fields a request through Camping. For traditional CGI applications, the method can be … … 647 673 # 648 674 def run(r=$stdin,e=ENV) 649 k, a = Controllers.D un("/#{e['PATH_INFO']}".gsub(%r!/+!,'/')) 650 i(k).new(r,e,(m=e['REQUEST_METHOD']||"GET")).service(*a) 651 rescue Exception => x 652 i(Controllers::ServerError).new(r,e,'get').service(k,m,x) 653 end 654 675 X.M 676 k,a=X.D un("/#{e['PATH_INFO']}".gsub(/\/+/,'/')) 677 k.new(r,e,(m=e['REQUEST_METHOD']||"GET")).service *a 678 rescue Exception=>x 679 X::ServerError.new(r,e,'get').service(k,m,x) 680 end 681 682 # The Camping scriptable dispatcher. Any unhandled method call to the app module will 683 # be sent to a controller class, specified as an argument. 684 # 685 # Blog.get(:Index) 686 # #=> #<Blog::Controllers::Index ... > 687 # 688 # The controller object contains all the @cookies, @body, @headers, etc. formulated by 689 # the response. 690 # 691 # You can also feed environment variables and query variables as a hash, the final 692 # argument. 693 # 694 # Blog.post(:Login, :input => {'username' => 'admin', 'password' => 'camping'}) 695 # #=> #<Blog::Controllers::Login @user=... > 696 # 697 # Blog.get(:Info, :env => {:HTTP_HOST => 'wagon'}) 698 # #=> #<Blog::Controllers::Info @env={'HTTP_HOST'=>'wagon'} ...> 699 # 655 700 def method_missing(m, c, *a) 656 k = Controllers.const_get(c) 657 i(k).new(nil,H['HTTP_HOST','','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s).service(*a) 701 X.M 702 k = X.const_get(c).new(StringIO.new, 703 H['HTTP_HOST','','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s) 704 H.new(a.pop).each { |e,f| k.send("#{e}=",f) } if Hash === a[-1] 705 k.service *a 658 706 end 659 707 end … … 685 733 # Models cannot be referred to in Views at this time. 686 734 module Models 687 A = ActiveRecord 688 # Base is an alias for ActiveRecord::Base. The big warning I'm going to give you 689 # about this: *Base overloads table_name_prefix.* This means that if you have a 690 # model class Blog::Models::Post, it's table name will be <tt>blog_posts</tt>. 691 Base = A::Base 692 693 # The default prefix for Camping model classes is the topmost module name lowercase 694 # and followed with an underscore. 695 # 696 # Tepee::Models::Page.table_name_prefix 697 # #=> "tepee_pages" 698 # 699 def Base.table_name_prefix 700 "#{name[/\w+/]}_".downcase.sub(/^(#{A}|camping)_/i,'') 701 end 735 autoload:Base,'camping/db' 702 736 end 703 737 … … 723 757 end 724 758 end 759 760 autoload:ActiveRecord,'camping/db' -
trunk/lib/camping.rb
r133 r134 24 24 @k.dup,q.dup end;def service *a;@body=send(@method,*a)if respond_to?@method 25 25 @headers["Set-Cookie"]=@cookies.map{|k,v|"#{k}=#{C.escape(v)}; path=#{self/"/"}\ 26 " if v!=@k[k]}-[nil];self end;def to_s;"Status: #{@status}#{Z+@headers.map{ 27 |k,v|[*v].map{|x|"#{k}: #{x}"}}*Z+Z*2+@body}" end;def markaby;Mab.new({},self) 28 end end;X=module Controllers 29 @r=[];class<<self;def r;@r;end;def R *u;r=@r;Class.new{meta_def(:urls){u} 30 meta_def(:inherited){|x|r<<x}}end;def M;constants.map{|c|k=const_get(c);k. 31 send:include,C,Base,Models;if !r.include?k;k.meta_def(:urls){["/#{c.downcase}"]} 32 r[0,0]=k;end}end;def D p;r.map{|k|k.urls.map{|x|return k,$~[1..-1]if p=~/^#{x}\/?$/} 33 };[NotFound, [p]]end end;class NotFound<R();def get p;r(404,Mab.new{h1 P 34 h2 p+" not found"}) end end;class ServerError<R();def get k,m,e;r(500, 35 Mab.new{h1 P;h2 "#{k}.#{m}";h3 "#{e.class} #{e.message}:";ul{ 36 e.backtrace.each{|bt|li(bt)}}}.to_s)end end;self;end;class<<self;def goes m 37 eval S.gsub(/Camping/,m.to_s).gsub("A\pps=[]","Cam\ping::Apps<<self"),TOPLEVEL_BINDING;end 38 def escape(s)s.to_s.gsub(/[^ \w.-]+/n){'%'+($&.unpack('H2'*$&.size)*'%').upcase}.tr(' ','+')end 39 def un(s)s.tr('+',' ').gsub(/%([\da-f]{2})/in){[$1].pack('H*')} end 40 def qs_parse q,d='&;';m=proc{|_,o,n|o.u(n,&m)rescue([*o 41 ]<<n)};q.to_s.split(/[#{d}] */n).inject(H[]){|h,p|k,v=un(p).split('=',2) 42 h.u k.split(/[\]\[]+/).reverse.inject(v){|x,i|H[i,x]},&m}end;def kp(s);c= 43 qs_parse(s,';,')end;def run r=$stdin,e=ENV;X.M;k,a=X.D un("/#{e['PATH_INFO'] 44 }".gsub(/\/+/,'/'));k.new(r,e,(m=e['REQUEST_METHOD' 45 ]||"GET")).service *a;rescue Exception=>x;X::ServerError.new(r,e,'get').service( 46 k,m,x)end;def method_missing m,c,*a;k=X.const_get c;k.new('', 47 H['HTTP_HOST','','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s).service *a;end 48 end;module Views;include X,Helpers end;module Models;autoload(:Base,'camping/db') 49 end;class Mab<Markaby::Builder;include \ 26 " if v!=@k[k]}-[nil];self end;def to_s;"Status: #{@status}#{Z+@headers.map{|k,v| 27 [*v].map{|x|"#{k}: #{x}"}}*Z+Z*2+@body}" end;def markaby;Mab.new({},self)end;end 28 X=module Controllers;@r=[];class<<self;def r;@r;end;def R *u;r=@r;Class.new{ 29 meta_def(:urls){u};meta_def(:inherited){|x|r<<x}}end;def M;def M;end;constants.map{|c| 30 k=const_get(c);k.send:include,C,Base,Models;r[0,0]=k if !r.include?k;k.meta_def( 31 :urls){["/#{c.downcase}"]}if !k.respond_to?:urls}end;def D p;r.map{|k|k.urls. 32 map{|x|return k,$~[1..-1]if p=~/^#{x}\/?$/}};[NotFound, [p]]end end;class 33 NotFound<R();def get p;r(404,Mab.new{h1 P;h2 p+" not found"}) end end;class 34 ServerError<R();def get k,m,e;r(500,Mab.new{h1 P;h2 "#{k}.#{m}";h3 "#{e.class 35 } #{e.message}:";ul{e.backtrace.each{|bt|li(bt)}}}.to_s)end end;self;end;class<< 36 self;def goes m;eval S.gsub(/Camping/,m.to_s).gsub("A\pps=[]","Cam\ping::Apps<<\ 37 self"),TOPLEVEL_BINDING;end;def escape(s)s.to_s.gsub(/[^ \w.-]+/n){'%'+($&. 38 unpack('H2'*$&.size)*'%').upcase}.tr(' ','+')end;def un(s)s.tr('+',' ').gsub( 39 /%([\da-f]{2})/in){[$1].pack('H*')}end;def qs_parse q,d='&;';m=proc{|_,o,n|o.u( 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=qs_parse(s,';,')end;def run r=$stdin,e=ENV;X.M;k,a=X.D un("/#{e[ 43 'PATH_INFO']}".gsub(/\/+/,'/'));k.new(r,e,(m=e['REQUEST_METHOD']||"GET")). 44 service *a;rescue Exception=>x;X::ServerError.new(r,e,'get').service(k,m,x)end 45 def method_missing m,c,*a;X.M;k=X.const_get(c).new(StringIO.new,H['HTTP_HOST', 46 '','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s);H.new(a.pop).each{|e,f|k.send( 47 "#{e}=",f)}if Hash===a[-1];k.service *a;end;end;module Views;include X,Helpers 48 end;module Models;autoload:Base,'camping/db';end;class Mab<Markaby::Builder;include \ 50 49 Views;def tag! *g,&b;h=g[-1];[:href,:action,:src].map{|a|(h[a]=self/h[a])rescue 51 50 0};super end end;H=HashWithIndifferentAccess;class H;def method_missing m,*a 52 51 m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m]:raise(NoMethodError,"#{m}")end 53 alias_method:u,:regular_update;end end;autoload (:ActiveRecord,'camping/db')52 alias_method:u,:regular_update;end end;autoload:ActiveRecord,'camping/db' -
trunk/lib/camping/db.rb
r133 r134 8 8 module Camping::Models 9 9 A = ActiveRecord 10 # Base is an alias for ActiveRecord::Base. The big warning I'm going to give you 11 # about this: *Base overloads table_name_prefix.* This means that if you have a 12 # model class Blog::Models::Post, it's table name will be <tt>blog_posts</tt>. 13 # 14 # ActiveRecord is not loaded if you never reference this class. The minute you 15 # use the ActiveRecord or Camping::Models::Base class, then the ActiveRecord library 16 # is loaded. 10 17 Base = A::Base 18 19 # The default prefix for Camping model classes is the topmost module name lowercase 20 # and followed with an underscore. 21 # 22 # Tepee::Models::Page.table_name_prefix 23 # #=> "tepee_pages" 24 # 11 25 def Base.table_name_prefix 12 26 "#{name[/\w+/]}_".downcase.sub(/^(#{A}|camping)_/i,'') 13 27 end 14 28 end 15 Camping::S.sub! "autoload (:Base,'camping/db')", "Base=ActiveRecord::Base"29 Camping::S.sub! "autoload:Base,'camping/db'", "Base=ActiveRecord::Base" 16 30 Camping::Apps.each do |app| 17 31 app::Models::Base = ActiveRecord::Base -
trunk/lib/camping/reloader.rb
r131 r134 60 60 @klass = Object.const_get(Object.constants.grep(/^#{title}$/i)[0]) rescue nil 61 61 unless @klass and @klass.const_defined? :C 62 puts "!! trouble loading #{title}: not a Camping app "62 puts "!! trouble loading #{title}: not a Camping app, no #{title.capitalize} module found" 63 63 @klass = nil 64 64 return
