Camping Microframework Guide


What is Camping?

Camping is a web application framework written in Ruby by "why the lucky stiff" (until version 1.5). Since _why's disappearance (August 19, 2009) from the web it is now maintained by the community.

Camping's complete source code can be viewed on a single page.

%w[rubygems active_record markaby metaid ostruct].each {|lib| require lib} module Camping;C=self;module Models;end;Models::Base=ActiveRecord::Base module Helpers;def R c,*args;p=/\(.+?\)/;args.inject(c.urls.detect{|x|x. scan(p).size==args.size}.dup){|str,a|str.gsub(p,(a.method(a.class.primary_key )[]rescue a).to_s)};end;def / p;File.join(@root,p) end;end;module Controllers module Base;include Helpers;attr_accessor :input,:cookies,:headers,:body, :status,:root;def method_missing(m,*args,&blk);str=m==:render ? markaview( *args,&blk):eval("markaby.#{m}(*args,&blk)");str=markaview(:layout){str }rescue nil;r(200,str.to_s);end;def r(s,b,h={});@status=s;@headers.merge!(h) @body=b;end;def redirect(c,*args);c=R(c,*args)if c.respond_to?:urls;r(302,'', 'Location'=>self/c);end;def service(r,e,m,a);@status,@headers,@root=200,{},e[ 'SCRIPT_NAME'];@cookies=C.cookie_parse(e['HTTP_COOKIE']||e['COOKIE']);cook= @cookies.marshal_dump.dup;if ("POST"==e['REQUEST_METHOD'])and %r|\Amultipart\ /form-data.*boundary=\"?([^\";,]+)\"?|n.match(e['CONTENT_TYPE']);return r(500, "No multipart/form-data supported.")else;@input=C.qs_parse(e['REQUEST_METHOD' ]=="POST"?r.read(e['CONTENT_LENGTH'].to_i):e['QUERY_STRING']);end;@body= method(m.downcase).call(*a);@headers["Set-Cookie"]=@cookies.marshal_dump.map{ |k,v|"#{k}=#{C.escape(v)}; path=/"if v != cook[k]}.compact;self;end;def to_s "Status: #{@status}\n#{{'Content-Type'=>'text/html'}.merge(@headers).map{|k,v| v.to_a.map{|v2|"#{k}: #{v2}"}}.flatten.join("\n")}\n\n#{@body}";end;def \ markaby;Class.new(Markaby::Builder){@root=@root;include Views;def tag!(*g,&b) [:href,:action].each{|a|(g.last[a]=self./(g.last[a]))rescue 0};super end}.new( instance_variables.map{|iv|[iv[1..-1].intern,instance_variable_get(iv)]},{}) end;def markaview(m,*args,&blk);markaby.instance_eval{Views.instance_method(m ).bind(self).call(*args, &blk);self}.to_s;end;end;class R;include Base end class NotFound<R;def get(p);r(404,div{h1("#{C} Problem!")+h2("#{p} not found") });end end;class ServerError<R;def get(k,m,e);r(500,markaby.div{h1 "#{C} Prob\ lem!";h2 "#{k}.#{m}";h3 "#{e.class} #{e.message}:";ul{e.backtrace.each{|bt|li( bt)</pre>)end end;class<<self;def R(*urls);Class.new(R){meta_def(:inherited){|c| c.meta_def(:urls){urls</pre>;end;def D(path);constants.each{|c|k=const_get(c) return k,$~[1..-1] if (k.urls rescue "/#{c.downcase}").find {|x|path=~/^#{x}\ \/?$/}};[NotFound,[path]];end end end;class<<self;def escape(s);s.to_s.gsub( /([^ a-zA-Z0-9_.-]+)/n){'%'+$1.unpack('H2'*$1.size).join('%').upcase}.tr(' ', '+') end;def unescape(s);s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){[$1. delete('%')].pack('H*')} end;def qs_parse(qs,d='&;');OpenStruct.new((qs||''). split(/[#{d}] */n).inject({}){|hsh,p|k,v=p.split('=',2).map{|v|unescape(v)} hsh[k]=v unless v.empty?;hsh}) end;def cookie_parse(s);c=qs_parse(s,';,') end def run(r=$stdin,w=$stdout);w<<begin;k,a=Controllers.D "/#{ENV['PATH_INFO']}". gsub(%r!/+!,'/');m=ENV['REQUEST_METHOD']||"GET";k.class_eval{include C include Controllers::Base;include Models};o=k.new;o.service(r,ENV,m,a);rescue\ =>e;Controllers::ServerError.new.service(r,ENV,"GET",[k,m,e]);end;end;end module Views; include Controllers; include Helpers end;end

It consistently stays under 4kb. The latest version of Camping is available from Github.


Camping Characteristics

Camping stores each new web application in a single file organized in a MVC style.

Camping only requires Rack (0.3 or higher) and (if you want to write HTML) Markaby (0.5 or higher), both available as Rubygems.

Of course, if you plan to use a database, you will need ActiveRecord. And any other resource will require other gems as well.

Camping more recent version includes support for Tilt which means that it now supports ERB and Haml.


Installing Camping

Camping install is just a matter of installing a few gems:

  gem install camping
      

  gem install rack
      

  gem install markaby
      

  gem install activerecord
      

That is it and


      Everyone.goes :Camping
      

Putting Camping into Work

As seen before, Camping uses the MVC architecture. But for our studies purpose, I would like to propose a totally different aproach. Turning everything upside down, we will use the CVM order:

Let's Start from the Beginning...


Our very first Camping application:

Every useful Camping application must have at least:

      require 'camping'
      Camping.goes :Name
      

Where "Name" is the application controller's name. And should, of course, have a controller like this:

      module Name::Controllers
          class Index < R '/'
              def get
                  p "Any Ruby code goes here!"
              end
          end
      end
      

Which generally (not mandatory) uses a class named "Index" to get the job done.

Note the use of the weird raw string syntax "R '/'" for regexes which might contain backslashes. Without the R you'd have to double every backslash.

This is an uncommon Ruby feature, used in Camping to describe the routes which the controller can be accessed on.

So, let's start with a simple Controller:

Save it as "app01.rb" and get it running with the "camping" command:

  camping app01.rb
      

Which will start the server and turn your application on:

Working Camping image

Now you can see the results at "http://localhost:3301/":

Working Camping image


Second step, let's add a View

To show any valuable result as a Web page, You will need Views of course.

Camping "Views" can be divided into two parts:

      module Trainer::Views
          def layout
              html do
                  head do
                      title 'ToR goes Camping'
                  end
                  body do
                      self << yield
                  end
              end
          end

          def view_body
              p "Second step, first View..."
          end
      end
      

To get this View working You will need to render it from the Controller:

      module Trainer::Controllers
          class Index < R'/'
              def get
                  render :view_body
              end
          end
      end
      

Which will bring us this piece of code:

You may save it as "app02.rb" and again get it running with "camping app02.rb".

Which will let You see this result in your Browser:

Working Camping image

And the resulting HTML (thanks to Markaby) is:

Where You can clearly see the layout part and the body part inside that first one.


Baby step 1 - a few improvements

  - More controllers and links

We will see now how to add more controllers and deal with Links in the Views.

In the new "Cool" controller, we have used the regexp "/cool/(\w+)" not only to link the "/cool" URL, but every alphanumeric character after the slash with the "\w+".

We will take those characters (stuff) after the slash and render a corresponding view with:

      def get stuff
          render stuff
      end
      

That being so, if we use one of the view names when specifying the URL, it will be rendered accordingly.

Working Camping image

You can now make your own experiments...


Third step, adding a Model

Storing data is an essential need for any application.

In camping (as in Rails) the model uses, by default, Active Record to have the dirty work done.

Let's have a look at the basic structure of a model-definition in camping: