| CODENOTIFIER | HelpYou are not signed inSign in |
Project: Camping
Revision: 235
Author: zimbatm
Date: 02 Oct 2007 01:49:34
Diff at Trac: http://code.whytheluckystiff.net/camping/changeset/235
Changes:Removed Markaby as a direct dependency. As AR, it is loaded on demand.
Files:| ... | ...@@ -0,0 +1,26 @@ | |
| 1 | class MissingLibrary < Exception #:nodoc: all | |
| 2 | end | |
| 3 | begin | |
| 4 | require 'markaby' | |
| 5 | rescue LoadError => e | |
| 6 | raise MissingLibrary, "Markaby could not be loaded (is it installed?): #{e.message}" | |
| 7 | end | |
| 8 | ||
| 9 | $MAB_CODE = %{ | |
| 10 | # The Mab class wraps Markaby, allowing it to run methods from Camping::Views | |
| 11 | # and also to replace :href, :action and :src attributes in tags by prefixing the root | |
| 12 | # path. | |
| 13 | class Mab < Markaby::Builder | |
| 14 | include Views | |
| 15 | def tag!(*g,&b) | |
| 16 | h=g[-1] | |
| 17 | [:href,:action,:src].map{|a|(h[a]=self/h[a])rescue 0} | |
| 18 | super | |
| 19 | end | |
| 20 | end | |
| 21 | } | |
| 22 | ||
| 23 | Camping::S.sub! /autoload\s*:Mab\s*,\s*['"]camping\/mab['"]/, $MAB_CODE | |
| 24 | Object.constants.map{|c|Object.const_get(c)}.each do |c| | |
| 25 | c.module_eval $MAB_CODE if c.respond_to?(:goes) | |
| 26 | end |
| ... | ...@@ -1,21 +1,23 @@ | |
| 1 | %w[markaby tempfile uri].map{|l|require l};module Camping;C=self | |
| 2 | S=IO.read(__FILE__)rescue nil;P="Cam\ping Problem!";class H<Hash | |
| 3 | def method_missing m,*a;m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m.to_s]:super | |
| 4 | end;alias u merge!;end;module Helpers def R c,*g;p,h=/\(.+?\)/,g. | |
| 5 | grep(Hash);(g-=h).inject(c.urls.find{|x|x.scan(p).size==g.size}.dup){|s,a|s. | |
| 6 | sub p,C.escape((a[a.class.primary_key]rescue a))}+(h.any?? "?"+h[0]. | |
| 7 | map{|x|x.map{|z|C.escape z}*"="}*"&": "")end;def /(p);p[/^\//]?@root+p:p | |
| 8 | end;def URL c='/',*a;c=R(c,*a)if c.respond_to?:urls;c=self/c;c="//"+ | |
| 9 | @env.HTTP_HOST+c if c[/^\//];URI(c) end end;module Base;attr_accessor:input, | |
| 10 | :cookies,:env,:headers,:body,:status,:root;Z="\r\n";def method_missing*a,&b;a. | |
| 11 | shift if a[0]==:render;m=Mab.new({},self);s=m.capture{send(*a,&b)};s=m.capture{ | |
| 12 | send(:layout){s}}if /^_/!~a[0].to_s and m.respond_to?:layout;s end;def | |
| 13 | redirect*a;r 302,'','Location'=>URL(*a)end;def r s,b,h={};@status=s;headers. | |
| 14 | merge!h;@body=b end;def to_a;[status,body,headers]end;def initialize r,e,m; | |
| 1 | %w[tempfile uri].map{|l|require l};class Object;def meta_def m,&b | |
| 2 | (class<<self;self end).instance_eval{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!;end | |
| 6 | module Helpers def R c,*g;p,h=/\(.+?\)/,g.grep(Hash) | |
| 7 | (g-=h).inject(c.urls.find{|x|x.scan(p).size==g.size}.dup){|s,a|s.sub p,C. | |
| 8 | escape((a[a.class.primary_key]rescue a))}+(h.any?? "?"+h[0].map{|x|x.map{|z|C. | |
| 9 | escape z}*"="}*"&": "")end;def /(p);p[/^\//]?@root+p:p;end;def URL c='/',*a | |
| 10 | c=R(c,*a)if c.respond_to?:urls;c=self/c;c="//"+@env.HTTP_HOST+c if c[/^\//] | |
| 11 | URI(c) end end;module Base;attr_accessor:input,:cookies,:env,:headers,:body, | |
| 12 | :status,:root;Z="\r\n";def method_missing*a,&b;a.shift if a[0]==:render | |
| 13 | m=Mab.new({},self);s=m.capture{send(*a,&b)};s=m.capture{send(:layout){s}}if | |
| 14 | /^_/!~a[0].to_s and m.respond_to?:layout;s end;def redirect*a | |
| 15 | r 302,'','Location'=>URL(*a)end;def r s,b,h={};@status=s;headers.merge!h | |
| 16 | @body=b end;def to_a;[status,body,headers]end;def initialize r,e,m | |
| 15 | 17 | @status,@method,@env,@headers,@root=200,m.downcase,e,{ |
| 16 | 18 | 'Content-Type'=>"text/html"},e.SCRIPT_NAME.sub(/\/$/,'');@k=C.kp e.HTTP_COOKIE |
| 17 | q=C.qsp e.QUERY_STRING;@in=r | |
| 18 | case e.CONTENT_TYPE when %r|\Amultipart/form-.*boundary=\"?([^\";,]+)|n | |
| 19 | q=C.qsp e.QUERY_STRING;@in=r;case e.CONTENT_TYPE | |
| 20 | when %r|\Amultipart/form-.*boundary=\"?([^\";,]+)|n | |
| 19 | 21 | b=/(?:\r?\n|\A)#{Regexp::quote"--#$1"}(?:--)?\r$/;until |
| 20 | 22 | @in.eof?;fh=H[];for l in@in;case l;when Z;break;when/^Content-D.+?: form-data;/ |
| 21 | 23 | fh.u H[*$'.scan(/(?:\s(\w+)="([^"]+)")/).flatten];when |
| ... | ...@@ -24,8 +26,8 @@ | |
| 24 | 26 | l=@in.read(s*2);while l;if(k<<l)=~b;o<<$`.chomp;@in.seek(-$'.size,IO::SEEK_CUR) |
| 25 | 27 | break;end;o<<k.slice!(0...s);l=@in.read(s);end;C.qsp(fn,'&;',fh,q)if fn;fh. |
| 26 | 28 | tempfile.rewind if fh.is_a?H;end;when "application/x-www-form-urlencoded" |
| 27 | q.u C.qsp(@in.read)end;@cookies,@input=@k.dup,q.dup end | |
| 28 | def service*a;@body=send(@method,*a)if respond_to?@method | |
| 29 | q.u C.qsp(@in.read)end;@cookies,@input=@k.dup,q.dup end;def service*a | |
| 30 | @body=send(@method,*a)if respond_to?@method | |
| 29 | 31 | headers["Set-Cookie"]=cookies.map{|k,v|"#{k}=#{C.escape v}; path=#{self/'/'}"if |
| 30 | 32 | v!=@k[k]}-[nil];self end;def to_s;"Status: #@status#{Z+headers.map{|k,v|[*v]. |
| 31 | 33 | map{|x|[k,v]*": "}}*Z+Z}#@body"end;end;X=module Controllers;@r=[];class<< |
| ... | ...@@ -34,21 +36,20 @@ | |
| 34 | 36 | }\/?$/}};[NotFound,[p]]end;def M;def M;end;constants.map{|c|k=const_get(c);k. |
| 35 | 37 | send:include,C,Base,Helpers,Models;r[0,0]=k if !r.include?k;k.meta_def(:urls){[ |
| 36 | 38 | "/#{c.downcase}"]}if !k.respond_to?:urls}end;end;class NotFound<R();def get p |
| 37 | r(404,Mab.new{h1 P;h2 p+" not found"})end end;class ServerError<R();def get k, | |
| 38 | m,e;r(500,Mab.new{h1 P;h2"#{k}.#{m}";h3"#{e.class} #{e.message}:";ul{e. | |
| 39 | backtrace.each{|bt|li bt}}})end end;self;end;class<<self;def goes m;eval S. | |
| 40 | gsub(/Camping/,m.to_s),TOPLEVEL_BINDING;end;def escape s;s.to_s.gsub( | |
| 41 | /[^ \w.-]+/n){'%'+($&.unpack('H2'*$&.size)*'%').upcase}.tr(' ','+')end;def un s | |
| 42 | s.tr('+',' ').gsub(/%([\da-f]{2})/in){[$1].pack('H*')}end;def qsp q,d='&;',y= | |
| 43 | nil,z=H[];m=proc{|_,o,n|o.u(n,&m)rescue([*o]<<n)};(q.to_s.split(/[#{d}]+ */n)-['']). | |
| 44 | inject((b,z=z,H[])[0]){|h,p|k,v=un(p).split('=',2);h.u k.split(/[\]\[]+/). | |
| 45 | reverse.inject(y||v){|x,i|H[i,x]},&m}end;def kp s;c=qsp(s,';,')end;def | |
| 46 | run r=$stdin,e=ENV;X.M;e=H[e.to_hash] | |
| 39 | r(404,"<h1>#{P}</h1><h2>#{p} not found</h2>")end end;class ServerError<R() | |
| 40 | def get k,m,e;r(500,"<h1>#{P}</h1><h2>#{k}.#{m}</h2><h3>#{e.class} #{e.message | |
| 41 | }:<ul>#{e.backtrace.map{ |b| "<li>#{b}</li>" } }")end end;self;end;class<<self | |
| 42 | def goes m;eval S.gsub(/Camping/,m.to_s),TOPLEVEL_BINDING;end;def escape s | |
| 43 | s.to_s.gsub(/[^ \w.-]+/n){'%'+($&.unpack('H2'*$&.size)*'%').upcase}.tr(' ','+') | |
| 44 | end;def un s;s.tr('+',' ').gsub(/%([\da-f]{2})/in){[$1].pack('H*')}end | |
| 45 | def qsp q,d='&;',y=nil,z=H[];m=proc{|_,o,n|o.u(n,&m)rescue([*o]<<n)} | |
| 46 | (q.to_s.split(/[#{d}]+ */n)-['']).inject((b,z=z,H[])[0]){|h,p|k,v=un(p). | |
| 47 | split('=',2);h.u k.split(/[\]\[]+/).reverse.inject(y||v){|x,i|H[i,x]},&m}end | |
| 48 | def kp s;c=qsp(s,';,')end;def run r=$stdin,e=ENV;X.M;e=H[e.to_hash] | |
| 47 | 49 | k,a=X.D e.PATH_INFO=un("/#{e.PATH_INFO}".gsub(/\/+/,'/'));k.new( |
| 48 | 50 | r,e,(m=e.REQUEST_METHOD||"GET")).Y.service(*a);rescue=>x;X::ServerError.new( |
| 49 | 51 | r,e,'get').service(k,m,x)end;def method_missing m,c,*a;X.M;k=X.const_get(c). |
| 50 | 52 | new(StringIO.new,H['HTTP_HOST','','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s);H. |
| 51 | 53 | new(a.pop).each{|e,f|k.send("#{e}=",f)}if Hash===a[-1];k.service(*a);end;end |
| 52 | 54 | module Views;include X,Helpers;end;module Models;autoload:Base,'camping/db';def |
| 53 | Y;self;end;end;class Mab<Markaby::Builder;include Views;def tag!*g,&b;h=g[-1] | |
| 54 | [:href,:action,:src].map{|a|(h[a]=self/h[a])rescue 0};super end end end | |
| 55 | Y;self;end;end;autoload:Mab,'camping/mab'end |
| ... | ...@@ -28,7 +28,13 @@ | |
| 28 | 28 | # http://rubyforge.org/projects/mongrel Mongrel comes with examples |
| 29 | 29 | # in its <tt>examples/camping</tt> directory. |
| 30 | 30 | # |
| 31 | %w[markaby tempfile uri].map { |l| require l } | |
| 31 | %w[tempfile uri].map { |l| require l } | |
| 32 | ||
| 33 | class Object | |
| 34 | def meta_def(m,&b) | |
| 35 | (class<<self;self end).instance_eval{define_method(m,&b)} | |
| 36 | end | |
| 37 | end | |
| 32 | 38 | |
| 33 | 39 | # == Camping |
| 34 | 40 | # |
| ... | ...@@ -528,7 +534,7 @@ | |
| 528 | 534 | # |
| 529 | 535 | class NotFound < R() |
| 530 | 536 | def get(p) |
| 531 | r(404, Mab.new{h1 P;h2 p + " not found"}) | |
| 537 | r(404, "<h1>#{P}</h1><h2>#{p} not found</h2>") | |
| 532 | 538 | end |
| 533 | 539 | end |
| 534 | 540 | |
| ... | ...@@ -557,12 +563,8 @@ | |
| 557 | 563 | # |
| 558 | 564 | class ServerError < R() |
| 559 | 565 | def get(k,m,e) |
| 560 | r(500, Mab.new { | |
| 561 | h1(P) | |
| 562 | h2 "#{k}.#{m}" | |
| 563 | h3 "#{e.class} #{e.message}:" | |
| 564 | ul { e.backtrace.each { |bt| li bt } } | |
| 565 | }) | |
| 566 | r(500, "<h1>#{P}</h1><h2>#{k}.#{m}</h2><h3>#{e.class} #{e.message | |
| 567 | }:<ul>#{e.backtrace.map{ |b| "<li>#{b}</li>" } }") | |
| 566 | 568 | end |
| 567 | 569 | end |
| 568 | 570 | |
| ... | ...@@ -724,16 +726,6 @@ | |
| 724 | 726 | def Y;self;end |
| 725 | 727 | end |
| 726 | 728 | |
| 727 | # The Mab class wraps Markaby, allowing it to run methods from Camping::Views | |
| 728 | # and also to replace :href, :action and :src attributes in tags by prefixing the root | |
| 729 | # path. | |
| 730 | class Mab < Markaby::Builder | |
| 731 | include Views | |
| 732 | def tag!(*g,&b) | |
| 733 | h=g[-1] | |
| 734 | [:href,:action,:src].map{|a|(h[a]=self/h[a])rescue 0} | |
| 735 | super | |
| 736 | end | |
| 737 | end | |
| 729 | autoload :Mab, 'camping/mab' | |
| 738 | 730 | end |
| 739 | 731 |
| ... | ...@@ -1,5 +1,4 @@ | |
| 1 | 1 | require 'camping/reloader' |
| 2 | require 'markaby' | |
| 3 | 2 | |
| 4 | 3 | module Camping::Server |
| 5 | 4 | class Base < Hash |
| ... | ...@@ -55,38 +54,32 @@ | |
| 55 | 54 | def index_page |
| 56 | 55 | welcome = "You are Camping" |
| 57 | 56 | apps = self |
| 58 | b = Markaby::Builder.new({}, {}) | |
| 59 | b = b.instance_eval do | |
| 60 | html do | |
| 61 | head do | |
| 62 | title welcome | |
| 63 | style <<-END, :type => 'text/css' | |
| 64 | body { | |
| 65 | font-family: verdana, arial, sans-serif; | |
| 66 | padding: 10px 40px; | |
| 67 | margin: 0; | |
| 68 | } | |
| 69 | h1, h2, h3, h4, h5, h6 { | |
| 70 | font-family: utopia, georgia, serif; | |
| 71 | } | |
| 72 | END | |
| 73 | end | |
| 74 | body do | |
| 75 | h1 welcome | |
| 76 | p %{Good day. These are the Camping apps you've mounted.} | |
| 77 | ul do | |
| 78 | apps.values.each do |app| | |
| 79 | next unless app.klass | |
| 80 | li do | |
| 81 | h3(:style => "display: inline") { a app.klass.name, :href => "/#{app.mount}" } | |
| 82 | small { text " / " ; a "View Source", :href => "/code/#{app.mount}" } | |
| 83 | end | |
| 84 | end | |
| 85 | end | |
| 86 | end | |
| 87 | end | |
| 88 | end | |
| 89 | b.to_s | |
| 57 | <<-HTML | |
| 58 | <html> | |
| 59 | <head> | |
| 60 | <title>#{welcome}</title> | |
| 61 | <style type="text/css"> | |
| 62 | body { | |
| 63 | font-family: verdana, arial, sans-serif; | |
| 64 | padding: 10px 40px; | |
| 65 | margin: 0; | |
| 66 | } | |
| 67 | h1, h2, h3, h4, h5, h6 { | |
| 68 | font-family: utopia, georgia, serif; | |
| 69 | } | |
| 70 | </style> | |
| 71 | </head> | |
| 72 | <body> | |
| 73 | <h1>#{welcome}</h1> | |
| 74 | <p>Good day. These are the Camping apps you've mounted.</p> | |
| 75 | <ul> | |
| 76 | #{apps.values.select{|app|app.klass}.map do |app| | |
| 77 | "<li><h3 style=\"display: inline\"><a href=\"/#{app.mount}\">#{app.klass.name}</a></h3><small> / <a href=\"/code/#{app.mount}\">View source</a></small></li>" | |
| 78 | end.join("\n")} | |
| 79 | </ul> | |
| 80 | </body> | |
| 81 | </html> | |
| 82 | HTML | |
| 90 | 83 | end |
| 91 | 84 | |
| 92 | 85 | def each(&b) |