SlideShare a Scribd company logo
1 of 36
Download to read offline
Camping,
   a Microframework
www.rug-b.com/wiki/show/schmidt
           2007-10-04
_why
_why
Shoes

Hackety Hack

Hpricot

Sandbox

RedCloth

Syck - Ruby‘s YAML parser
_why
                              Why‘s
Shoes
                            (Poignant)
Hackety Hack
                             Guide to
                              Ruby
Hpricot

Sandbox

RedCloth

Syck - Ruby‘s YAML parser
code.whytheluckystiff.net/camping/
%w[active_support markaby tempfile uri].map{|l|require l}
          module Camping;Apps=[];C=self;S=IO.read(__FILE__).sub(/S=I.+$/,'')
        P=quot;Camping Problem!quot;;module Helpers;def R c,*g;p=/(.+?)/;g.inject(c.
             urls.find{|x|x.scan(p).size==g.size}.dup){|s,a|s.sub p,C.escape((a[
              a.class.primary_key]rescue a))}end;def URL c='/',*a;c=R(c,*a)if c.
respond_to?:urls;c=self/c;c=quot;//quot;+@env.HTTP_HOST+c if c[/^//];URI(c)end;def/p
     p[/^//]?@root+p : p end;def errors_for o;ul.errors{o.errors.each_full{|x|li x}
        }if o.errors.any?end end;module Base;include Helpers;attr_accessor:input,
             :cookies,:env,:headers,:body,:status,:root;def method_missing*a,&b
a.shift if a[0]==:render;m=Mab.new({},self);s=m.capture{send(*a,&b)};s=m.layout{s}if
  /^_/!~a[0].to_s and m.respond_to?:layout;s end;def r s,b,h={};@status=s;@headers.
     merge!h;@body=b end;def redirect*a;r 302,'','Location'=>URL(*a)end;Z=quot;rnquot;
   def initialize r,e,m;e=H[e.to_hash];@status,@method,@env,@headers,@root=200,m.
        downcase,e,{'Content-Type'=>quot;text/htmlquot;},e.SCRIPT_NAME.sub(//$/,'')
            @k=C.kp e.HTTP_COOKIE;q=C.qs_parse e.QUERY_STRING;@in=r
    if%r|Amultipart/form-.*boundary=quot;?([^quot;;,]+)|n.match e.CONTENT_TYPE
b=/(?:r?n|A)#{Regexp::quote(quot;--#$1quot;)}(?:--)?r$/;until@in.eof?;fh=H[];for l in@in
             case l;when Z;break;when/^Content-D.+?: form-data;/;fh.u H[*$'.
   scan(/(?:s(w+)=quot;([^quot;]+)quot;)/).flatten];when/^Content-Type: (.+?)(r$|Z)/m;fh[
 :type]=$1;end;end;fn=fh[:name];o=if fh[:filename];o=fh[:tempfile]=Tempfile.new(:C)
  o.binmode;else;fh=quot;quot;end;while l=@in.read(16384);if l=~b;o<<$`.chomp;@in.seek(-$'.
                                                                                                X=module Controllers;@r=[];class<<self;def r;@r;end;def R*u;r=@r;Class.new{
      size,IO::SEEK_CUR);break;end;o<<l;end;q[fn]=fh if fn;fh[:tempfile].rewind if
                                                                                       meta_def(:urls){u};meta_def(:inherited){|x|r<<x}}end;def M;def M;end;constants.map{|c|
fh.is_a?H;end;elsif@method==quot;postquot;;q.u C.qs_parse(@in.read)end;@cookies,@input=
                                                                                              k=const_get(c);k.send:include,C,Base,Models;r[0,0]=k if !r.include?k;k.meta_def(
  @k.dup,q.dup end;def service*a;@body=send(@method,*a)if respond_to?@method
                                                                                                 :urls){[quot;/#{c.downcase}quot;]}if !k.respond_to?:urls}end;def D p;r.map{|k|k.urls.
  @headers[quot;Set-Cookiequot;]=@cookies.map{|k,v|quot;#{k}=#{C.escape(v)}; path=#{self/'/'
                                                                                                  map{|x|return k,$~[1..-1]if p=~/^#{x}/?$/}};[NotFound,[p]]end end;class
    }quot;if v!=@k[k]}-[nil];self end;def to_s;quot;Status: #{@status}#{Z+@headers.map{|k,v|
                                                                                               NotFound<R();def get p;r(404,Mab.new{h1 P;h2 p+quot; not foundquot;})end end;class
                      [*v].map{|x|quot;#{k}: #{x}quot;}}*Z+Z*2+@body}quot;end;end
                                                                                                 ServerError<R();def get k,m,e;r(500,Mab.new{h1 P;h2quot;#{k}.#{m}quot;;h3quot;#{e.class
                                                                                                } #{e.message}:quot;;ul{e.backtrace.each{|bt|li(bt)}}}.to_s)end end;self;end;class<<
                                                                                        self;def goes m;eval S.gsub(/Camping/,m.to_s).gsub(quot;Apps=[]quot;,quot;Camping::Apps<<
                                                                                             selfquot;),TOPLEVEL_BINDING;end;def escape s;s.to_s.gsub(/[^ w.-]+/n){'%'+($&.
                                                                                                     unpack('H2'*$&.size)*'%').upcase}.tr(' ','+')end;def un s;s.tr('+',' ').gsub(
                                                                                              /%([da-f]{2})/in){[$1].pack('H*')}end;def qs_parse q,d='&;';m=proc{|_,o,n|o.u(
                                                                                                  n,&m)rescue([*o]<<n)};q.to_s.split(/[#{d}] */n).inject(H[]){|h,p|k,v=un(p).
                                                                                                   split('=',2);h.u k.split(/[][]+/).reverse.inject(v){|x,i|H[i,x]},&m}end;def
                                                                                                   kp s;c=qs_parse(s,';,')end;def run r=$stdin,e=ENV;X.M;k,a=X.D un(quot;/#{e[
                                                                                         'PATH_INFO']}quot;.gsub(//+/,'/'));k.new(r,e,(m=e['REQUEST_METHOD']||quot;GETquot;)).Y.
                                                                                                service *a;rescue Exception=>x;X::ServerError.new(r,e,'get').service(k,m,x)end
                                                                                         def method_missing m,c,*a;X.M;k=X.const_get(c).new(StringIO.new,H['HTTP_HOST',
                                                                                             '','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s);H.new(a.pop).each{|e,f|k.send(
                                                                                              quot;#{e}=quot;,f)}if Hash===a[-1];k.service *a;end;end;module Views;include X,Helpers
                                                                                                   end;module Models;autoload:Base,'camping/db';def Y;self;end;end;class
                                                                                                                               Mab<Markaby::Builder
                                                                                          include Views;def tag!*g,&b;h=g[-1];[:href,:action,:src].map{|a|(h[a]=self/h[a])rescue
                                                                                            0};super end end;H=HashWithIndifferentAccess;class H;def method_missing m,*a
                                                                                                  m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m]:raise(NoMethodError,quot;#{m}quot;)end
                                                                                                                      alias_method:u,:regular_update;end end
%w[active_support markaby tempfile uri].map{|l|require l}
          module Camping;Apps=[];C=self;S=IO.read(__FILE__).sub(/S=I.+$/,'')
        P=quot;Camping Problem!quot;;module Helpers;def R c,*g;p=/(.+?)/;g.inject(c.
             urls.find{|x|x.scan(p).size==g.size}.dup){|s,a|s.sub p,C.escape((a[
              a.class.primary_key]rescue a))}end;def URL c='/',*a;c=R(c,*a)if c.
respond_to?:urls;c=self/c;c=quot;//quot;+@env.HTTP_HOST+c if c[/^//];URI(c)end;def/p
     p[/^//]?@root+p : p end;def errors_for o;ul.errors{o.errors.each_full{|x|li x}
        }if o.errors.any?end end;module Base;include Helpers;attr_accessor:input,
             :cookies,:env,:headers,:body,:status,:root;def method_missing*a,&b
a.shift if a[0]==:render;m=Mab.new({},self);s=m.capture{send(*a,&b)};s=m.layout{s}if
  /^_/!~a[0].to_s and m.respond_to?:layout;s end;def r s,b,h={};@status=s;@headers.
     merge!h;@body=b end;def redirect*a;r 302,'','Location'=>URL(*a)end;Z=quot;rnquot;
   def initialize r,e,m;e=H[e.to_hash];@status,@method,@env,@headers,@root=200,m.
        downcase,e,{'Content-Type'=>quot;text/htmlquot;},e.SCRIPT_NAME.sub(//$/,'')
            @k=C.kp e.HTTP_COOKIE;q=C.qs_parse e.QUERY_STRING;@in=r
    if%r|Amultipart/form-.*boundary=quot;?([^quot;;,]+)|n.match e.CONTENT_TYPE
b=/(?:r?n|A)#{Regexp::quote(quot;--#$1quot;)}(?:--)?r$/;until@in.eof?;fh=H[];for l in@in
             case l;when Z;break;when/^Content-D.+?: form-data;/;fh.u H[*$'.
   scan(/(?:s(w+)=quot;([^quot;]+)quot;)/).flatten];when/^Content-Type: (.+?)(r$|Z)/m;fh[
 :type]=$1;end;end;fn=fh[:name];o=if fh[:filename];o=fh[:tempfile]=Tempfile.new(:C)




                    3.909 Bytes
  o.binmode;else;fh=quot;quot;end;while l=@in.read(16384);if l=~b;o<<$`.chomp;@in.seek(-$'.
                                                                                                X=module Controllers;@r=[];class<<self;def r;@r;end;def R*u;r=@r;Class.new{
      size,IO::SEEK_CUR);break;end;o<<l;end;q[fn]=fh if fn;fh[:tempfile].rewind if
                                                                                       meta_def(:urls){u};meta_def(:inherited){|x|r<<x}}end;def M;def M;end;constants.map{|c|
fh.is_a?H;end;elsif@method==quot;postquot;;q.u C.qs_parse(@in.read)end;@cookies,@input=
                                                                                              k=const_get(c);k.send:include,C,Base,Models;r[0,0]=k if !r.include?k;k.meta_def(
  @k.dup,q.dup end;def service*a;@body=send(@method,*a)if respond_to?@method
                                                                                                 :urls){[quot;/#{c.downcase}quot;]}if !k.respond_to?:urls}end;def D p;r.map{|k|k.urls.
  @headers[quot;Set-Cookiequot;]=@cookies.map{|k,v|quot;#{k}=#{C.escape(v)}; path=#{self/'/'
                                                                                                  map{|x|return k,$~[1..-1]if p=~/^#{x}/?$/}};[NotFound,[p]]end end;class
    }quot;if v!=@k[k]}-[nil];self end;def to_s;quot;Status: #{@status}#{Z+@headers.map{|k,v|
                                                                                               NotFound<R();def get p;r(404,Mab.new{h1 P;h2 p+quot; not foundquot;})end end;class
                      [*v].map{|x|quot;#{k}: #{x}quot;}}*Z+Z*2+@body}quot;end;end
                                                                                                 ServerError<R();def get k,m,e;r(500,Mab.new{h1 P;h2quot;#{k}.#{m}quot;;h3quot;#{e.class
                                                                                                } #{e.message}:quot;;ul{e.backtrace.each{|bt|li(bt)}}}.to_s)end end;self;end;class<<
                                                                                        self;def goes m;eval S.gsub(/Camping/,m.to_s).gsub(quot;Apps=[]quot;,quot;Camping::Apps<<
                                                                                             selfquot;),TOPLEVEL_BINDING;end;def escape s;s.to_s.gsub(/[^ w.-]+/n){'%'+($&.
                                                                                                     unpack('H2'*$&.size)*'%').upcase}.tr(' ','+')end;def un s;s.tr('+',' ').gsub(
                                                                                              /%([da-f]{2})/in){[$1].pack('H*')}end;def qs_parse q,d='&;';m=proc{|_,o,n|o.u(
                                                                                                  n,&m)rescue([*o]<<n)};q.to_s.split(/[#{d}] */n).inject(H[]){|h,p|k,v=un(p).
                                                                                                   split('=',2);h.u k.split(/[][]+/).reverse.inject(v){|x,i|H[i,x]},&m}end;def
                                                                                                   kp s;c=qs_parse(s,';,')end;def run r=$stdin,e=ENV;X.M;k,a=X.D un(quot;/#{e[
                                                                                         'PATH_INFO']}quot;.gsub(//+/,'/'));k.new(r,e,(m=e['REQUEST_METHOD']||quot;GETquot;)).Y.
                                                                                                service *a;rescue Exception=>x;X::ServerError.new(r,e,'get').service(k,m,x)end
                                                                                         def method_missing m,c,*a;X.M;k=X.const_get(c).new(StringIO.new,H['HTTP_HOST',
                                                                                             '','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s);H.new(a.pop).each{|e,f|k.send(
                                                                                              quot;#{e}=quot;,f)}if Hash===a[-1];k.service *a;end;end;module Views;include X,Helpers
                                                                                                   end;module Models;autoload:Base,'camping/db';def Y;self;end;end;class
                                                                                                                               Mab<Markaby::Builder
                                                                                          include Views;def tag!*g,&b;h=g[-1];[:href,:action,:src].map{|a|(h[a]=self/h[a])rescue
                                                                                            0};super end end;H=HashWithIndifferentAccess;class H;def method_missing m,*a
                                                                                                  m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m]:raise(NoMethodError,quot;#{m}quot;)end
                                                                                                                      alias_method:u,:regular_update;end end
%w[active_support markaby tempfile uri].map{|l|require l}
          module Camping;Apps=[];C=self;S=IO.read(__FILE__).sub(/S=I.+$/,'')
        P=quot;Camping Problem!quot;;module Helpers;def R c,*g;p=/(.+?)/;g.inject(c.
             urls.find{|x|x.scan(p).size==g.size}.dup){|s,a|s.sub p,C.escape((a[
              a.class.primary_key]rescue a))}end;def URL c='/',*a;c=R(c,*a)if c.
respond_to?:urls;c=self/c;c=quot;//quot;+@env.HTTP_HOST+c if c[/^//];URI(c)end;def/p
     p[/^//]?@root+p : p end;def errors_for o;ul.errors{o.errors.each_full{|x|li x}

                                                                                                        not sure, if this is
        }if o.errors.any?end end;module Base;include Helpers;attr_accessor:input,
             :cookies,:env,:headers,:body,:status,:root;def method_missing*a,&b
a.shift if a[0]==:render;m=Mab.new({},self);s=m.capture{send(*a,&b)};s=m.layout{s}if


                                                                                                        a bug or a feature
  /^_/!~a[0].to_s and m.respond_to?:layout;s end;def r s,b,h={};@status=s;@headers.
     merge!h;@body=b end;def redirect*a;r 302,'','Location'=>URL(*a)end;Z=quot;rnquot;
   def initialize r,e,m;e=H[e.to_hash];@status,@method,@env,@headers,@root=200,m.
        downcase,e,{'Content-Type'=>quot;text/htmlquot;},e.SCRIPT_NAME.sub(//$/,'')
            @k=C.kp e.HTTP_COOKIE;q=C.qs_parse e.QUERY_STRING;@in=r
    if%r|Amultipart/form-.*boundary=quot;?([^quot;;,]+)|n.match e.CONTENT_TYPE
b=/(?:r?n|A)#{Regexp::quote(quot;--#$1quot;)}(?:--)?r$/;until@in.eof?;fh=H[];for l in@in
             case l;when Z;break;when/^Content-D.+?: form-data;/;fh.u H[*$'.
   scan(/(?:s(w+)=quot;([^quot;]+)quot;)/).flatten];when/^Content-Type: (.+?)(r$|Z)/m;fh[
 :type]=$1;end;end;fn=fh[:name];o=if fh[:filename];o=fh[:tempfile]=Tempfile.new(:C)




                    3.909 Bytes
  o.binmode;else;fh=quot;quot;end;while l=@in.read(16384);if l=~b;o<<$`.chomp;@in.seek(-$'.
                                                                                                X=module Controllers;@r=[];class<<self;def r;@r;end;def R*u;r=@r;Class.new{
      size,IO::SEEK_CUR);break;end;o<<l;end;q[fn]=fh if fn;fh[:tempfile].rewind if
                                                                                       meta_def(:urls){u};meta_def(:inherited){|x|r<<x}}end;def M;def M;end;constants.map{|c|
fh.is_a?H;end;elsif@method==quot;postquot;;q.u C.qs_parse(@in.read)end;@cookies,@input=
                                                                                              k=const_get(c);k.send:include,C,Base,Models;r[0,0]=k if !r.include?k;k.meta_def(
  @k.dup,q.dup end;def service*a;@body=send(@method,*a)if respond_to?@method
                                                                                                 :urls){[quot;/#{c.downcase}quot;]}if !k.respond_to?:urls}end;def D p;r.map{|k|k.urls.
  @headers[quot;Set-Cookiequot;]=@cookies.map{|k,v|quot;#{k}=#{C.escape(v)}; path=#{self/'/'
                                                                                                  map{|x|return k,$~[1..-1]if p=~/^#{x}/?$/}};[NotFound,[p]]end end;class
    }quot;if v!=@k[k]}-[nil];self end;def to_s;quot;Status: #{@status}#{Z+@headers.map{|k,v|
                                                                                               NotFound<R();def get p;r(404,Mab.new{h1 P;h2 p+quot; not foundquot;})end end;class
                      [*v].map{|x|quot;#{k}: #{x}quot;}}*Z+Z*2+@body}quot;end;end
                                                                                                 ServerError<R();def get k,m,e;r(500,Mab.new{h1 P;h2quot;#{k}.#{m}quot;;h3quot;#{e.class
                                                                                                } #{e.message}:quot;;ul{e.backtrace.each{|bt|li(bt)}}}.to_s)end end;self;end;class<<
                                                                                        self;def goes m;eval S.gsub(/Camping/,m.to_s).gsub(quot;Apps=[]quot;,quot;Camping::Apps<<
                                                                                             selfquot;),TOPLEVEL_BINDING;end;def escape s;s.to_s.gsub(/[^ w.-]+/n){'%'+($&.
                                                                                                     unpack('H2'*$&.size)*'%').upcase}.tr(' ','+')end;def un s;s.tr('+',' ').gsub(
                                                                                              /%([da-f]{2})/in){[$1].pack('H*')}end;def qs_parse q,d='&;';m=proc{|_,o,n|o.u(
                                                                                                  n,&m)rescue([*o]<<n)};q.to_s.split(/[#{d}] */n).inject(H[]){|h,p|k,v=un(p).
                                                                                                   split('=',2);h.u k.split(/[][]+/).reverse.inject(v){|x,i|H[i,x]},&m}end;def
                                                                                                   kp s;c=qs_parse(s,';,')end;def run r=$stdin,e=ENV;X.M;k,a=X.D un(quot;/#{e[
                                                                                         'PATH_INFO']}quot;.gsub(//+/,'/'));k.new(r,e,(m=e['REQUEST_METHOD']||quot;GETquot;)).Y.
                                                                                                service *a;rescue Exception=>x;X::ServerError.new(r,e,'get').service(k,m,x)end
                                                                                         def method_missing m,c,*a;X.M;k=X.const_get(c).new(StringIO.new,H['HTTP_HOST',
                                                                                             '','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s);H.new(a.pop).each{|e,f|k.send(
                                                                                              quot;#{e}=quot;,f)}if Hash===a[-1];k.service *a;end;end;module Views;include X,Helpers
                                                                                                   end;module Models;autoload:Base,'camping/db';def Y;self;end;end;class
                                                                                                                               Mab<Markaby::Builder
                                                                                          include Views;def tag!*g,&b;h=g[-1];[:href,:action,:src].map{|a|(h[a]=self/h[a])rescue
                                                                                            0};super end end;H=HashWithIndifferentAccess;class H;def method_missing m,*a
                                                                                                  m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m]:raise(NoMethodError,quot;#{m}quot;)end
                                                                                                                      alias_method:u,:regular_update;end end
The Basics
Core Components


ActiveRecord for Models

HTTP-style for Controllers

Markaby for Views
Models

Everybody in here knows ActiveRecord

One-Way-Migrations

SQLite-db is default storage

All Camping apps use the same one
Controllers
Caming.goes :HelloWorld

module HelloWorld::Controllers
  class Index < R '/'
    def get
      'Hello, World!'
    end
  end
end
Controllers (contd.)


One method for each verb - get, post, put, ...

Return value of method is send to browser

Configure Routing with R method
Routing
R '/'
  localhost:3301/
R '/foo'
  localhost:3301/foo
R '/bar/(d+)'
  localhost:3301/bar/110
R '/baz', '/baz/(d+)'
  localhost:3301/baz
  localhost:3301/baz/110
Routing (contd.)
class Baz < R '/baz', '/baz/(d+)'
  def get(id = nil)
    id.nil? ? index : show(id)
  end

  ...
end
Views
Markaby - Markup in Ruby

Internal
DSL for
HTML
Views
Markaby - Markup in Ruby
           html do
Internal     head do
               title { quot;The test sitequot; }
DSL for
             end
HTML
             body.example! do
               h1 { quot;The test sitequot; }
               div.content do
                 p quot;show examplequot;
                 img :src => quot;camping.pngquot;
               end
             end
           end
Views
Markaby - Markup in Ruby
           html do
Internal     head do
               title { quot;The test sitequot; }
DSL for
             end
HTML                   id
             body.example! do
               h1 { quot;The test sitequot; }
               div.content do
                 p quot;show examplequot;
                 img :src => quot;camping.pngquot;
               end
             end
           end
Views
Markaby - Markup in Ruby
           html do
Internal     head do
               title { quot;The test sitequot; }
DSL for
             end
HTML                   id
             body.example! do
                           class
               h1 { quot;The test sitequot; }
               div.content do
                 p quot;show examplequot;
                 img :src => quot;camping.pngquot;
               end
             end
           end
Views
Markaby - Markup in Ruby
           html do
Internal     head do
               title { quot;The test sitequot; }
DSL for
             end
HTML                   id
             body.example! do
                           class
               h1 { quot;The test sitequot; }
               div.content do
                                   attributes
                 p quot;show examplequot;
                 img :src => quot;camping.pngquot;
               end
             end
           end
A whole App
A Camping App
    One File
A Camping App
    One File

     models
   migrations
   controllers
     views
   post-ambles
module Foo::Controllers
                                     class Index < R '/'
Camping.goes :Foo                      def get
                                         @user_count = User.count
module Foo::Models                       @foo_count = Foo.count
  class User < Base                      render 'index'
    has_many :bars                     end
  end                                end
  class Bar < Base                 end
    belongs_to :user
  end                              module Foo::Views
                                     def layout
  class CreateFoo < V 1.0              xhtml_strict do
    def self.up                          head do
      create_table :foo_users do           title quot;Foo - a Camping Appquot;
        ...                              end
      end                                body do
      create_table :foo_bars do            self << yield
       ...                               end
      end                              end
    end                              end
    def self.down
      drop_table :foo_users          def index
      drop_table :foo_bars             p do
    end                                  text quot;#{@user_count} users quot;
  end                                    text quot; with#{@bar_count} barsquot;
end                                    end
                                     end
                                   end
camping foo.rb


Put everything in one file

run camping foo.rb

point your browser to http://localhost:3301/
Extending
service(*a)
One single point of entry

Each request passes this method

  May manipulate arguments

  Add behaviour

  ...

Two possible uses
I. Write your own
    extensions
module MyExtension
  def service(*a)
    # do the additional stuff
    super
    # but be sure to call super
    # and keep return values
  end
end

module Foo
  include MyExtension
end
II. Use existing ones

Camping::Session - adds session suport (db)

SleepingBag - allows rails-like REST-controllers

TentSteak - some Markaby goodness

Parasite - include Camping in Rails apps

Mosquito - Tests for Camping
Finding libaries

      They all have
        generic,
   camping-associated
        names !!!
http://rubyforge.org/pipermail/camping-list/
What else?
Usage scenarios
small apps

  Do I really need a full blown rails app for
  this little code?

multiple small apps

  camping servers mount multiple apps at
  once

  they share the db so they could share models
Future
_why does hackety.org

still involved in camping dev

last release 1.5 from 2006-10-03

current dev for 1.6

  bug fix and consolidation release

  get rid of dependencies
Future
_why does hackety.org

still involved in camping dev

last release 1.5 from 2006-10-03

current dev for 1.6

  bug fix and consolidation release

  get rid of dependencies            soon !?!
Preview

  ContextWiki

or Live Example

 SyntaxCaming
???

More Related Content

What's hot

SPL: The Missing Link in Development
SPL: The Missing Link in DevelopmentSPL: The Missing Link in Development
SPL: The Missing Link in Developmentjsmith92
 
Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8XSolve
 
Learning Perl 6
Learning Perl 6 Learning Perl 6
Learning Perl 6 brian d foy
 
Travel management
Travel managementTravel management
Travel management1Parimal2
 
Functional Pe(a)rls version 2
Functional Pe(a)rls version 2Functional Pe(a)rls version 2
Functional Pe(a)rls version 2osfameron
 
Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4Jeff Carouth
 
Learning Perl 6 (NPW 2007)
Learning Perl 6 (NPW 2007)Learning Perl 6 (NPW 2007)
Learning Perl 6 (NPW 2007)brian d foy
 
Static Optimization of PHP bytecode (PHPSC 2017)
Static Optimization of PHP bytecode (PHPSC 2017)Static Optimization of PHP bytecode (PHPSC 2017)
Static Optimization of PHP bytecode (PHPSC 2017)Nikita Popov
 
Text in search queries with examples in Perl 6
Text in search queries with examples in Perl 6Text in search queries with examples in Perl 6
Text in search queries with examples in Perl 6Andrew Shitov
 
Implementing virtual machines in go & c 2018 redux
Implementing virtual machines in go & c 2018 reduxImplementing virtual machines in go & c 2018 redux
Implementing virtual machines in go & c 2018 reduxEleanor McHugh
 
Electrify your code with PHP Generators
Electrify your code with PHP GeneratorsElectrify your code with PHP Generators
Electrify your code with PHP GeneratorsMark Baker
 
RAILWAY RESERWATION PROJECT PROGRAM
RAILWAY RESERWATION PROJECT PROGRAMRAILWAY RESERWATION PROJECT PROGRAM
RAILWAY RESERWATION PROJECT PROGRAMKrishna Raj
 
PHP Conference Asia 2016
PHP Conference Asia 2016PHP Conference Asia 2016
PHP Conference Asia 2016Britta Alex
 
There's more than one way to empty it
There's more than one way to empty itThere's more than one way to empty it
There's more than one way to empty itAndrew Shitov
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsBastian Feder
 

What's hot (20)

SPL: The Missing Link in Development
SPL: The Missing Link in DevelopmentSPL: The Missing Link in Development
SPL: The Missing Link in Development
 
Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8
 
Railwaynew
RailwaynewRailwaynew
Railwaynew
 
Learning Perl 6
Learning Perl 6 Learning Perl 6
Learning Perl 6
 
Travel management
Travel managementTravel management
Travel management
 
Functional Pe(a)rls version 2
Functional Pe(a)rls version 2Functional Pe(a)rls version 2
Functional Pe(a)rls version 2
 
The most exciting features of PHP 7.1
The most exciting features of PHP 7.1The most exciting features of PHP 7.1
The most exciting features of PHP 7.1
 
Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4
 
Learning Perl 6 (NPW 2007)
Learning Perl 6 (NPW 2007)Learning Perl 6 (NPW 2007)
Learning Perl 6 (NPW 2007)
 
Static Optimization of PHP bytecode (PHPSC 2017)
Static Optimization of PHP bytecode (PHPSC 2017)Static Optimization of PHP bytecode (PHPSC 2017)
Static Optimization of PHP bytecode (PHPSC 2017)
 
Opp compile
Opp compileOpp compile
Opp compile
 
Text in search queries with examples in Perl 6
Text in search queries with examples in Perl 6Text in search queries with examples in Perl 6
Text in search queries with examples in Perl 6
 
Implementing virtual machines in go & c 2018 redux
Implementing virtual machines in go & c 2018 reduxImplementing virtual machines in go & c 2018 redux
Implementing virtual machines in go & c 2018 redux
 
Electrify your code with PHP Generators
Electrify your code with PHP GeneratorsElectrify your code with PHP Generators
Electrify your code with PHP Generators
 
RAILWAY RESERWATION PROJECT PROGRAM
RAILWAY RESERWATION PROJECT PROGRAMRAILWAY RESERWATION PROJECT PROGRAM
RAILWAY RESERWATION PROJECT PROGRAM
 
R meets Hadoop
R meets HadoopR meets Hadoop
R meets Hadoop
 
PHP Conference Asia 2016
PHP Conference Asia 2016PHP Conference Asia 2016
PHP Conference Asia 2016
 
There's more than one way to empty it
There's more than one way to empty itThere's more than one way to empty it
There's more than one way to empty it
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown Parts
 
RHadoop の紹介
RHadoop の紹介RHadoop の紹介
RHadoop の紹介
 

Similar to Camping

TypeScript Introduction
TypeScript IntroductionTypeScript Introduction
TypeScript IntroductionDmitry Sheiko
 
perl usage at database applications
perl usage at database applicationsperl usage at database applications
perl usage at database applicationsJoe Jiang
 
Functional programming using underscorejs
Functional programming using underscorejsFunctional programming using underscorejs
Functional programming using underscorejs偉格 高
 
R57shell
R57shellR57shell
R57shellady36
 
How to write code you won't hate tomorrow
How to write code you won't hate tomorrowHow to write code you won't hate tomorrow
How to write code you won't hate tomorrowPete McFarlane
 
FunctionalJS - George Shevtsov
FunctionalJS - George ShevtsovFunctionalJS - George Shevtsov
FunctionalJS - George ShevtsovGeorgiy Shevtsov
 
1. George Shevtsov - Functional JavaScript
1. George Shevtsov - Functional JavaScript1. George Shevtsov - Functional JavaScript
1. George Shevtsov - Functional JavaScriptInnovecs
 
All I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web FrameworkAll I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web FrameworkBen Scofield
 
PHP tips and tricks
PHP tips and tricks PHP tips and tricks
PHP tips and tricks Damien Seguy
 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairMark
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your CodeAbbas Ali
 
Perl Bag of Tricks - Baltimore Perl mongers
Perl Bag of Tricks  -  Baltimore Perl mongersPerl Bag of Tricks  -  Baltimore Perl mongers
Perl Bag of Tricks - Baltimore Perl mongersbrian d foy
 
And the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportAnd the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportBen Scofield
 
Php tips-and-tricks4128
Php tips-and-tricks4128Php tips-and-tricks4128
Php tips-and-tricks4128PrinceGuru MS
 
An Elephant of a Different Colour: Hack
An Elephant of a Different Colour: HackAn Elephant of a Different Colour: Hack
An Elephant of a Different Colour: HackVic Metcalfe
 
ECMAScript2015
ECMAScript2015ECMAScript2015
ECMAScript2015qmmr
 

Similar to Camping (20)

TypeScript Introduction
TypeScript IntroductionTypeScript Introduction
TypeScript Introduction
 
Functional JS+ ES6.pptx
Functional JS+ ES6.pptxFunctional JS+ ES6.pptx
Functional JS+ ES6.pptx
 
perl usage at database applications
perl usage at database applicationsperl usage at database applications
perl usage at database applications
 
Functional programming using underscorejs
Functional programming using underscorejsFunctional programming using underscorejs
Functional programming using underscorejs
 
R57shell
R57shellR57shell
R57shell
 
How to write code you won't hate tomorrow
How to write code you won't hate tomorrowHow to write code you won't hate tomorrow
How to write code you won't hate tomorrow
 
FunctionalJS - George Shevtsov
FunctionalJS - George ShevtsovFunctionalJS - George Shevtsov
FunctionalJS - George Shevtsov
 
1. George Shevtsov - Functional JavaScript
1. George Shevtsov - Functional JavaScript1. George Shevtsov - Functional JavaScript
1. George Shevtsov - Functional JavaScript
 
All I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web FrameworkAll I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web Framework
 
PHP tips and tricks
PHP tips and tricks PHP tips and tricks
PHP tips and tricks
 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love Affair
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your Code
 
Perl Bag of Tricks - Baltimore Perl mongers
Perl Bag of Tricks  -  Baltimore Perl mongersPerl Bag of Tricks  -  Baltimore Perl mongers
Perl Bag of Tricks - Baltimore Perl mongers
 
ES6 is Nigh
ES6 is NighES6 is Nigh
ES6 is Nigh
 
And the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportAnd the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack Support
 
Php tips-and-tricks4128
Php tips-and-tricks4128Php tips-and-tricks4128
Php tips-and-tricks4128
 
An Elephant of a Different Colour: Hack
An Elephant of a Different Colour: HackAn Elephant of a Different Colour: Hack
An Elephant of a Different Colour: Hack
 
Javascript
JavascriptJavascript
Javascript
 
Functional programming with php7
Functional programming with php7Functional programming with php7
Functional programming with php7
 
ECMAScript2015
ECMAScript2015ECMAScript2015
ECMAScript2015
 

Recently uploaded

MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MIND CTI
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsNanddeep Nachan
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDropbox
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfOverkill Security
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
A Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source MilvusA Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source MilvusZilliz
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native ApplicationsWSO2
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FMESafe Software
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProduct Anonymous
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024The Digital Insurer
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodJuan lago vázquez
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 

Recently uploaded (20)

MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
A Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source MilvusA Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source Milvus
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 

Camping

  • 1. Camping, a Microframework www.rug-b.com/wiki/show/schmidt 2007-10-04
  • 4. _why Why‘s Shoes (Poignant) Hackety Hack Guide to Ruby Hpricot Sandbox RedCloth Syck - Ruby‘s YAML parser
  • 6. %w[active_support markaby tempfile uri].map{|l|require l} module Camping;Apps=[];C=self;S=IO.read(__FILE__).sub(/S=I.+$/,'') P=quot;Camping Problem!quot;;module Helpers;def R c,*g;p=/(.+?)/;g.inject(c. urls.find{|x|x.scan(p).size==g.size}.dup){|s,a|s.sub p,C.escape((a[ a.class.primary_key]rescue a))}end;def URL c='/',*a;c=R(c,*a)if c. respond_to?:urls;c=self/c;c=quot;//quot;+@env.HTTP_HOST+c if c[/^//];URI(c)end;def/p p[/^//]?@root+p : p end;def errors_for o;ul.errors{o.errors.each_full{|x|li x} }if o.errors.any?end end;module Base;include Helpers;attr_accessor:input, :cookies,:env,:headers,:body,:status,:root;def method_missing*a,&b a.shift if a[0]==:render;m=Mab.new({},self);s=m.capture{send(*a,&b)};s=m.layout{s}if /^_/!~a[0].to_s and m.respond_to?:layout;s end;def r s,b,h={};@status=s;@headers. merge!h;@body=b end;def redirect*a;r 302,'','Location'=>URL(*a)end;Z=quot;rnquot; def initialize r,e,m;e=H[e.to_hash];@status,@method,@env,@headers,@root=200,m. downcase,e,{'Content-Type'=>quot;text/htmlquot;},e.SCRIPT_NAME.sub(//$/,'') @k=C.kp e.HTTP_COOKIE;q=C.qs_parse e.QUERY_STRING;@in=r if%r|Amultipart/form-.*boundary=quot;?([^quot;;,]+)|n.match e.CONTENT_TYPE b=/(?:r?n|A)#{Regexp::quote(quot;--#$1quot;)}(?:--)?r$/;until@in.eof?;fh=H[];for l in@in case l;when Z;break;when/^Content-D.+?: form-data;/;fh.u H[*$'. scan(/(?:s(w+)=quot;([^quot;]+)quot;)/).flatten];when/^Content-Type: (.+?)(r$|Z)/m;fh[ :type]=$1;end;end;fn=fh[:name];o=if fh[:filename];o=fh[:tempfile]=Tempfile.new(:C) o.binmode;else;fh=quot;quot;end;while l=@in.read(16384);if l=~b;o<<$`.chomp;@in.seek(-$'. X=module Controllers;@r=[];class<<self;def r;@r;end;def R*u;r=@r;Class.new{ size,IO::SEEK_CUR);break;end;o<<l;end;q[fn]=fh if fn;fh[:tempfile].rewind if meta_def(:urls){u};meta_def(:inherited){|x|r<<x}}end;def M;def M;end;constants.map{|c| fh.is_a?H;end;elsif@method==quot;postquot;;q.u C.qs_parse(@in.read)end;@cookies,@input= k=const_get(c);k.send:include,C,Base,Models;r[0,0]=k if !r.include?k;k.meta_def( @k.dup,q.dup end;def service*a;@body=send(@method,*a)if respond_to?@method :urls){[quot;/#{c.downcase}quot;]}if !k.respond_to?:urls}end;def D p;r.map{|k|k.urls. @headers[quot;Set-Cookiequot;]=@cookies.map{|k,v|quot;#{k}=#{C.escape(v)}; path=#{self/'/' map{|x|return k,$~[1..-1]if p=~/^#{x}/?$/}};[NotFound,[p]]end end;class }quot;if v!=@k[k]}-[nil];self end;def to_s;quot;Status: #{@status}#{Z+@headers.map{|k,v| NotFound<R();def get p;r(404,Mab.new{h1 P;h2 p+quot; not foundquot;})end end;class [*v].map{|x|quot;#{k}: #{x}quot;}}*Z+Z*2+@body}quot;end;end ServerError<R();def get k,m,e;r(500,Mab.new{h1 P;h2quot;#{k}.#{m}quot;;h3quot;#{e.class } #{e.message}:quot;;ul{e.backtrace.each{|bt|li(bt)}}}.to_s)end end;self;end;class<< self;def goes m;eval S.gsub(/Camping/,m.to_s).gsub(quot;Apps=[]quot;,quot;Camping::Apps<< selfquot;),TOPLEVEL_BINDING;end;def escape s;s.to_s.gsub(/[^ w.-]+/n){'%'+($&. unpack('H2'*$&.size)*'%').upcase}.tr(' ','+')end;def un s;s.tr('+',' ').gsub( /%([da-f]{2})/in){[$1].pack('H*')}end;def qs_parse q,d='&;';m=proc{|_,o,n|o.u( n,&m)rescue([*o]<<n)};q.to_s.split(/[#{d}] */n).inject(H[]){|h,p|k,v=un(p). split('=',2);h.u k.split(/[][]+/).reverse.inject(v){|x,i|H[i,x]},&m}end;def kp s;c=qs_parse(s,';,')end;def run r=$stdin,e=ENV;X.M;k,a=X.D un(quot;/#{e[ 'PATH_INFO']}quot;.gsub(//+/,'/'));k.new(r,e,(m=e['REQUEST_METHOD']||quot;GETquot;)).Y. service *a;rescue Exception=>x;X::ServerError.new(r,e,'get').service(k,m,x)end def method_missing m,c,*a;X.M;k=X.const_get(c).new(StringIO.new,H['HTTP_HOST', '','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s);H.new(a.pop).each{|e,f|k.send( quot;#{e}=quot;,f)}if Hash===a[-1];k.service *a;end;end;module Views;include X,Helpers end;module Models;autoload:Base,'camping/db';def Y;self;end;end;class Mab<Markaby::Builder include Views;def tag!*g,&b;h=g[-1];[:href,:action,:src].map{|a|(h[a]=self/h[a])rescue 0};super end end;H=HashWithIndifferentAccess;class H;def method_missing m,*a m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m]:raise(NoMethodError,quot;#{m}quot;)end alias_method:u,:regular_update;end end
  • 7. %w[active_support markaby tempfile uri].map{|l|require l} module Camping;Apps=[];C=self;S=IO.read(__FILE__).sub(/S=I.+$/,'') P=quot;Camping Problem!quot;;module Helpers;def R c,*g;p=/(.+?)/;g.inject(c. urls.find{|x|x.scan(p).size==g.size}.dup){|s,a|s.sub p,C.escape((a[ a.class.primary_key]rescue a))}end;def URL c='/',*a;c=R(c,*a)if c. respond_to?:urls;c=self/c;c=quot;//quot;+@env.HTTP_HOST+c if c[/^//];URI(c)end;def/p p[/^//]?@root+p : p end;def errors_for o;ul.errors{o.errors.each_full{|x|li x} }if o.errors.any?end end;module Base;include Helpers;attr_accessor:input, :cookies,:env,:headers,:body,:status,:root;def method_missing*a,&b a.shift if a[0]==:render;m=Mab.new({},self);s=m.capture{send(*a,&b)};s=m.layout{s}if /^_/!~a[0].to_s and m.respond_to?:layout;s end;def r s,b,h={};@status=s;@headers. merge!h;@body=b end;def redirect*a;r 302,'','Location'=>URL(*a)end;Z=quot;rnquot; def initialize r,e,m;e=H[e.to_hash];@status,@method,@env,@headers,@root=200,m. downcase,e,{'Content-Type'=>quot;text/htmlquot;},e.SCRIPT_NAME.sub(//$/,'') @k=C.kp e.HTTP_COOKIE;q=C.qs_parse e.QUERY_STRING;@in=r if%r|Amultipart/form-.*boundary=quot;?([^quot;;,]+)|n.match e.CONTENT_TYPE b=/(?:r?n|A)#{Regexp::quote(quot;--#$1quot;)}(?:--)?r$/;until@in.eof?;fh=H[];for l in@in case l;when Z;break;when/^Content-D.+?: form-data;/;fh.u H[*$'. scan(/(?:s(w+)=quot;([^quot;]+)quot;)/).flatten];when/^Content-Type: (.+?)(r$|Z)/m;fh[ :type]=$1;end;end;fn=fh[:name];o=if fh[:filename];o=fh[:tempfile]=Tempfile.new(:C) 3.909 Bytes o.binmode;else;fh=quot;quot;end;while l=@in.read(16384);if l=~b;o<<$`.chomp;@in.seek(-$'. X=module Controllers;@r=[];class<<self;def r;@r;end;def R*u;r=@r;Class.new{ size,IO::SEEK_CUR);break;end;o<<l;end;q[fn]=fh if fn;fh[:tempfile].rewind if meta_def(:urls){u};meta_def(:inherited){|x|r<<x}}end;def M;def M;end;constants.map{|c| fh.is_a?H;end;elsif@method==quot;postquot;;q.u C.qs_parse(@in.read)end;@cookies,@input= k=const_get(c);k.send:include,C,Base,Models;r[0,0]=k if !r.include?k;k.meta_def( @k.dup,q.dup end;def service*a;@body=send(@method,*a)if respond_to?@method :urls){[quot;/#{c.downcase}quot;]}if !k.respond_to?:urls}end;def D p;r.map{|k|k.urls. @headers[quot;Set-Cookiequot;]=@cookies.map{|k,v|quot;#{k}=#{C.escape(v)}; path=#{self/'/' map{|x|return k,$~[1..-1]if p=~/^#{x}/?$/}};[NotFound,[p]]end end;class }quot;if v!=@k[k]}-[nil];self end;def to_s;quot;Status: #{@status}#{Z+@headers.map{|k,v| NotFound<R();def get p;r(404,Mab.new{h1 P;h2 p+quot; not foundquot;})end end;class [*v].map{|x|quot;#{k}: #{x}quot;}}*Z+Z*2+@body}quot;end;end ServerError<R();def get k,m,e;r(500,Mab.new{h1 P;h2quot;#{k}.#{m}quot;;h3quot;#{e.class } #{e.message}:quot;;ul{e.backtrace.each{|bt|li(bt)}}}.to_s)end end;self;end;class<< self;def goes m;eval S.gsub(/Camping/,m.to_s).gsub(quot;Apps=[]quot;,quot;Camping::Apps<< selfquot;),TOPLEVEL_BINDING;end;def escape s;s.to_s.gsub(/[^ w.-]+/n){'%'+($&. unpack('H2'*$&.size)*'%').upcase}.tr(' ','+')end;def un s;s.tr('+',' ').gsub( /%([da-f]{2})/in){[$1].pack('H*')}end;def qs_parse q,d='&;';m=proc{|_,o,n|o.u( n,&m)rescue([*o]<<n)};q.to_s.split(/[#{d}] */n).inject(H[]){|h,p|k,v=un(p). split('=',2);h.u k.split(/[][]+/).reverse.inject(v){|x,i|H[i,x]},&m}end;def kp s;c=qs_parse(s,';,')end;def run r=$stdin,e=ENV;X.M;k,a=X.D un(quot;/#{e[ 'PATH_INFO']}quot;.gsub(//+/,'/'));k.new(r,e,(m=e['REQUEST_METHOD']||quot;GETquot;)).Y. service *a;rescue Exception=>x;X::ServerError.new(r,e,'get').service(k,m,x)end def method_missing m,c,*a;X.M;k=X.const_get(c).new(StringIO.new,H['HTTP_HOST', '','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s);H.new(a.pop).each{|e,f|k.send( quot;#{e}=quot;,f)}if Hash===a[-1];k.service *a;end;end;module Views;include X,Helpers end;module Models;autoload:Base,'camping/db';def Y;self;end;end;class Mab<Markaby::Builder include Views;def tag!*g,&b;h=g[-1];[:href,:action,:src].map{|a|(h[a]=self/h[a])rescue 0};super end end;H=HashWithIndifferentAccess;class H;def method_missing m,*a m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m]:raise(NoMethodError,quot;#{m}quot;)end alias_method:u,:regular_update;end end
  • 8. %w[active_support markaby tempfile uri].map{|l|require l} module Camping;Apps=[];C=self;S=IO.read(__FILE__).sub(/S=I.+$/,'') P=quot;Camping Problem!quot;;module Helpers;def R c,*g;p=/(.+?)/;g.inject(c. urls.find{|x|x.scan(p).size==g.size}.dup){|s,a|s.sub p,C.escape((a[ a.class.primary_key]rescue a))}end;def URL c='/',*a;c=R(c,*a)if c. respond_to?:urls;c=self/c;c=quot;//quot;+@env.HTTP_HOST+c if c[/^//];URI(c)end;def/p p[/^//]?@root+p : p end;def errors_for o;ul.errors{o.errors.each_full{|x|li x} not sure, if this is }if o.errors.any?end end;module Base;include Helpers;attr_accessor:input, :cookies,:env,:headers,:body,:status,:root;def method_missing*a,&b a.shift if a[0]==:render;m=Mab.new({},self);s=m.capture{send(*a,&b)};s=m.layout{s}if a bug or a feature /^_/!~a[0].to_s and m.respond_to?:layout;s end;def r s,b,h={};@status=s;@headers. merge!h;@body=b end;def redirect*a;r 302,'','Location'=>URL(*a)end;Z=quot;rnquot; def initialize r,e,m;e=H[e.to_hash];@status,@method,@env,@headers,@root=200,m. downcase,e,{'Content-Type'=>quot;text/htmlquot;},e.SCRIPT_NAME.sub(//$/,'') @k=C.kp e.HTTP_COOKIE;q=C.qs_parse e.QUERY_STRING;@in=r if%r|Amultipart/form-.*boundary=quot;?([^quot;;,]+)|n.match e.CONTENT_TYPE b=/(?:r?n|A)#{Regexp::quote(quot;--#$1quot;)}(?:--)?r$/;until@in.eof?;fh=H[];for l in@in case l;when Z;break;when/^Content-D.+?: form-data;/;fh.u H[*$'. scan(/(?:s(w+)=quot;([^quot;]+)quot;)/).flatten];when/^Content-Type: (.+?)(r$|Z)/m;fh[ :type]=$1;end;end;fn=fh[:name];o=if fh[:filename];o=fh[:tempfile]=Tempfile.new(:C) 3.909 Bytes o.binmode;else;fh=quot;quot;end;while l=@in.read(16384);if l=~b;o<<$`.chomp;@in.seek(-$'. X=module Controllers;@r=[];class<<self;def r;@r;end;def R*u;r=@r;Class.new{ size,IO::SEEK_CUR);break;end;o<<l;end;q[fn]=fh if fn;fh[:tempfile].rewind if meta_def(:urls){u};meta_def(:inherited){|x|r<<x}}end;def M;def M;end;constants.map{|c| fh.is_a?H;end;elsif@method==quot;postquot;;q.u C.qs_parse(@in.read)end;@cookies,@input= k=const_get(c);k.send:include,C,Base,Models;r[0,0]=k if !r.include?k;k.meta_def( @k.dup,q.dup end;def service*a;@body=send(@method,*a)if respond_to?@method :urls){[quot;/#{c.downcase}quot;]}if !k.respond_to?:urls}end;def D p;r.map{|k|k.urls. @headers[quot;Set-Cookiequot;]=@cookies.map{|k,v|quot;#{k}=#{C.escape(v)}; path=#{self/'/' map{|x|return k,$~[1..-1]if p=~/^#{x}/?$/}};[NotFound,[p]]end end;class }quot;if v!=@k[k]}-[nil];self end;def to_s;quot;Status: #{@status}#{Z+@headers.map{|k,v| NotFound<R();def get p;r(404,Mab.new{h1 P;h2 p+quot; not foundquot;})end end;class [*v].map{|x|quot;#{k}: #{x}quot;}}*Z+Z*2+@body}quot;end;end ServerError<R();def get k,m,e;r(500,Mab.new{h1 P;h2quot;#{k}.#{m}quot;;h3quot;#{e.class } #{e.message}:quot;;ul{e.backtrace.each{|bt|li(bt)}}}.to_s)end end;self;end;class<< self;def goes m;eval S.gsub(/Camping/,m.to_s).gsub(quot;Apps=[]quot;,quot;Camping::Apps<< selfquot;),TOPLEVEL_BINDING;end;def escape s;s.to_s.gsub(/[^ w.-]+/n){'%'+($&. unpack('H2'*$&.size)*'%').upcase}.tr(' ','+')end;def un s;s.tr('+',' ').gsub( /%([da-f]{2})/in){[$1].pack('H*')}end;def qs_parse q,d='&;';m=proc{|_,o,n|o.u( n,&m)rescue([*o]<<n)};q.to_s.split(/[#{d}] */n).inject(H[]){|h,p|k,v=un(p). split('=',2);h.u k.split(/[][]+/).reverse.inject(v){|x,i|H[i,x]},&m}end;def kp s;c=qs_parse(s,';,')end;def run r=$stdin,e=ENV;X.M;k,a=X.D un(quot;/#{e[ 'PATH_INFO']}quot;.gsub(//+/,'/'));k.new(r,e,(m=e['REQUEST_METHOD']||quot;GETquot;)).Y. service *a;rescue Exception=>x;X::ServerError.new(r,e,'get').service(k,m,x)end def method_missing m,c,*a;X.M;k=X.const_get(c).new(StringIO.new,H['HTTP_HOST', '','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s);H.new(a.pop).each{|e,f|k.send( quot;#{e}=quot;,f)}if Hash===a[-1];k.service *a;end;end;module Views;include X,Helpers end;module Models;autoload:Base,'camping/db';def Y;self;end;end;class Mab<Markaby::Builder include Views;def tag!*g,&b;h=g[-1];[:href,:action,:src].map{|a|(h[a]=self/h[a])rescue 0};super end end;H=HashWithIndifferentAccess;class H;def method_missing m,*a m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m]:raise(NoMethodError,quot;#{m}quot;)end alias_method:u,:regular_update;end end
  • 10. Core Components ActiveRecord for Models HTTP-style for Controllers Markaby for Views
  • 11. Models Everybody in here knows ActiveRecord One-Way-Migrations SQLite-db is default storage All Camping apps use the same one
  • 12. Controllers Caming.goes :HelloWorld module HelloWorld::Controllers class Index < R '/' def get 'Hello, World!' end end end
  • 13. Controllers (contd.) One method for each verb - get, post, put, ... Return value of method is send to browser Configure Routing with R method
  • 14. Routing R '/' localhost:3301/ R '/foo' localhost:3301/foo R '/bar/(d+)' localhost:3301/bar/110 R '/baz', '/baz/(d+)' localhost:3301/baz localhost:3301/baz/110
  • 15. Routing (contd.) class Baz < R '/baz', '/baz/(d+)' def get(id = nil) id.nil? ? index : show(id) end ... end
  • 16. Views Markaby - Markup in Ruby Internal DSL for HTML
  • 17. Views Markaby - Markup in Ruby html do Internal head do title { quot;The test sitequot; } DSL for end HTML body.example! do h1 { quot;The test sitequot; } div.content do p quot;show examplequot; img :src => quot;camping.pngquot; end end end
  • 18. Views Markaby - Markup in Ruby html do Internal head do title { quot;The test sitequot; } DSL for end HTML id body.example! do h1 { quot;The test sitequot; } div.content do p quot;show examplequot; img :src => quot;camping.pngquot; end end end
  • 19. Views Markaby - Markup in Ruby html do Internal head do title { quot;The test sitequot; } DSL for end HTML id body.example! do class h1 { quot;The test sitequot; } div.content do p quot;show examplequot; img :src => quot;camping.pngquot; end end end
  • 20. Views Markaby - Markup in Ruby html do Internal head do title { quot;The test sitequot; } DSL for end HTML id body.example! do class h1 { quot;The test sitequot; } div.content do attributes p quot;show examplequot; img :src => quot;camping.pngquot; end end end
  • 22. A Camping App One File
  • 23. A Camping App One File models migrations controllers views post-ambles
  • 24. module Foo::Controllers class Index < R '/' Camping.goes :Foo def get @user_count = User.count module Foo::Models @foo_count = Foo.count class User < Base render 'index' has_many :bars end end end class Bar < Base end belongs_to :user end module Foo::Views def layout class CreateFoo < V 1.0 xhtml_strict do def self.up head do create_table :foo_users do title quot;Foo - a Camping Appquot; ... end end body do create_table :foo_bars do self << yield ... end end end end end def self.down drop_table :foo_users def index drop_table :foo_bars p do end text quot;#{@user_count} users quot; end text quot; with#{@bar_count} barsquot; end end end end
  • 25. camping foo.rb Put everything in one file run camping foo.rb point your browser to http://localhost:3301/
  • 27. service(*a) One single point of entry Each request passes this method May manipulate arguments Add behaviour ... Two possible uses
  • 28. I. Write your own extensions module MyExtension def service(*a) # do the additional stuff super # but be sure to call super # and keep return values end end module Foo include MyExtension end
  • 29. II. Use existing ones Camping::Session - adds session suport (db) SleepingBag - allows rails-like REST-controllers TentSteak - some Markaby goodness Parasite - include Camping in Rails apps Mosquito - Tests for Camping
  • 30. Finding libaries They all have generic, camping-associated names !!! http://rubyforge.org/pipermail/camping-list/
  • 32. Usage scenarios small apps Do I really need a full blown rails app for this little code? multiple small apps camping servers mount multiple apps at once they share the db so they could share models
  • 33. Future _why does hackety.org still involved in camping dev last release 1.5 from 2006-10-03 current dev for 1.6 bug fix and consolidation release get rid of dependencies
  • 34. Future _why does hackety.org still involved in camping dev last release 1.5 from 2006-10-03 current dev for 1.6 bug fix and consolidation release get rid of dependencies soon !?!
  • 35. Preview ContextWiki or Live Example SyntaxCaming
  • 36. ???