require "uri";require "rack";class Object;def meta_def m,&b;(class<<self;self
end).send:define_method,m,&b end end;module Camping;C=self;S=IO.read(__FILE__
)rescue nil;P="<h1>Cam\ping Problem!</h1><h2>%s</h2>";U=Rack::Utils;Apps=[]
class H<Hash;def method_missing m,*a;m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m.
to_s]:super end;undef id,type if ??==63;end;module Helpers;def R c,*g;p,h=
/\(.+?\)/,g.grep(Hash);g-=h;raise"bad route"unless u=c.urls.find{|x|break x if
x.scan(p).size==g.size&&/^#{x}\/?$/=~(x=g.inject(x){|x,a|x.sub p,U.escape((a[
a.class.primary_key]rescue a))})};h.any?? u+"?"+U.build_query(h[0]):u end;def
/ p;p[0]==?/?@root + p : p end;def URL c='/',*a;c=R(c, *a) if c.respond_to?(
:urls);c=self/c;c=@request.url[/.{8,}?(?=\/)/]+c if c[0]==?/;URI c end end
module Base;attr_accessor:env,:request,:root,:input,:cookies,:state,:status,
:headers,:body;def render v,*a,&b;mab(/^_/!~v.to_s){send(v,*a,&b)} end;def
mab l=nil,&b;m=Mab.new({},self);s=m.capture(&b);s=m.capture{layout{s}} if l &&
m.respond_to?(:layout);s end;def r s,b,h={};b,h=h,b if Hash===b;@status=s;
@headers.merge!(h);@body=b;end;def redirect *a;r 302,'','Location'=>URL(*a).
to_s;end;def r404 p;P%"#{p} not found"end;def r500 k,m,e;raise e;end;def r501 m
P%"#{m.upcase} not implemented"end;def to_a;@env['rack.session']=@state;r=Rack::
Response.new(@body,@status,@headers);@cookies.each{|k,v|next if @old_cookies[
k]==v;v={:value=>v,:path=>self/"/"} if String===v;r.set_cookie(k,v)};r.to_a;end
def initialize(env,m) r=@request=Rack::Request.new(@env=env);@root,@input,
@cookies,@state,@headers,@status,@method=r.script_name.sub(/\/$/,''),n(r.params
),H[@old_cookies = r.cookies],H[r.session],{},m=~/r(\d+)/?$1.to_i: 200,m end;def
n h;Hash===h ?h.inject(H[]){|m,(k,v)|m[k]=n(v);m}: h end;def service *a;r=catch(
:halt){send(@method,*a)};@body||=r;self;end;end;module Controllers;@r=[];class<<
self;def r;@r end;def R *u;r=@r;Class.new{meta_def(:urls){u};meta_def(:inherited
){|x|r<<x}}end;def D p,m;p='/'if !p||!p[0];r.map{|k|k.urls.map{|x|return(k.
instance_method(m)rescue nil)?[k,m,*$~[1..-1]]:[I,'r501',m]if p=~/^#{x}\/?$/}}
[I,'r404',p] end;N=H.new{|_,x|x.downcase}.merge! "N"=>'(\d+)',"X"=>'([^/]+)',
"Index"=>'';def M;def M;end;constants.map{|c|k=const_get(c);k.send:include,C,
Base,Helpers,Models;@r=[k]+r if r-[k]==r;k.meta_def(:urls){ [ "/#{c.scan(
/.[^A-Z]*/).map(&N.method(:[]))*'/'}"]}if !k.respond_to?:urls}end end;I=R()
end;X=Controllers;class<<self;def goes m;Apps<<eval(S.gsub(/Camping/,m.to_s),
TOPLEVEL_BINDING) end;def call e;X.M;p=e['PATH_INFO']=U.unescape(e['PATH_INFO'])
k,m,*a=X.D p,e['REQUEST_METHOD'].downcase;k.new(e,m).service(*a).to_a;rescue
r500(:I,k,m,$!,:env=>e).to_a;end;def method_missing m,c,*a;X.M;h=Hash===a[-1]?
a.pop: {};e=H[Rack::MockRequest.env_for('',h.delete(:env)||{})];k=X.const_get(c
).new(e,m.to_s);h.each{|i,v|k.send"#{i}=",v};k.service(*a);end;def use*a,&b;m=a.
shift.new(method(:call),*a,&b);meta_def(:call){|e|m.call(e)}end end;module Views
include X,Helpers end;module Models;autoload:Base,
'camping/ar';end;autoload:Mab,'camping/mab';C end
---
Wow. After 199 commits, 11875 lines of diff and 1284 days, it's an honor to
present a new, freshly baked version of the microframework; now built upon
Rack and weighing only 3072 tiny bytes. (That's exactly 3k!)
gem install camping
Home: http://whywentcamping.com/ (currently only redirects to the docs)
Docs: http://camping.rubyforge.org/
Code/wiki/bugs: http://github.com/camping/camping
Mailing list: http://rubyforge.org/mailman/listinfo/camping-list
~> Wait a second, you're not _why!
Well, no. He went camping, so we decided to commandeer and steer this ship
back on course. I'm merely a scoutmaster; it's the community who's in charge.
Want to have an impact on Camping? Just join the mailing list, and your voice
will be heard.
~> What's new?
Let's start with Rack.
Every Camping app is now a Rack app. This is your config.ru:
require 'blog'
run Blog
If your app requires a middleware, you can inject it inside your app:
module Blog
use Rack::MethodOverride
end
Sessions are now simply a wrapper around env['rack.session'], so it's easy as
pie to `use Other::SessionBackends`.
Ironicly, the methods Rack stole from Camping are no longer in Camping, since
it's shorter to simply call those in Rack :-)
Next up: The book - http://camping.rubyforge.org/book.html
Not really a book, but it should get you started with Camping pretty quick. It's
far from complete, but in the end you should know plenty about both Camping,
Rack, HTTP and other frameworks. Web development is a huge field in Ruby and can
be a little confusing for newcomers. This book should give you a gentle
introduction together with pointers to where you could go for more.
This book isn't written yet. Let me repeat that: This book isn't
written yet. Nothing is set in stone. These are basically just some of mine
ideas - what I wish was there some years ago. But I'm neither the author nor
the reader, so I want YOU to tell me how it really should be. It's open-source
after all.
Being open-source means you are free to do whatever you want. Does that
sentence suck? That paragraph is badly phrased? That chapter doesn't belong
there? Rewrite it! Reorganize the whole book! Just make sure to tell the rest
why it's better.
Being open-source ALSO means you sometimes have to give up your perfect
chapter. Realize it wasn't as good as you thought. It's tough, but that's how
it's going to be.
Okay, now: Simpler routes
The most used line is Camping apps has to be:
class Index < R '/'
So _why commited and now you can write:
class Index # matches /
class Posts # matches /posts
class PostX # matches /post/([^/]+)
class UserN # matches /user/(\d+)
~> Interesting. Is 1.5 still supported? What should I do to upgrade my app?
No more development will be done for 1.5, so we recommend that you upgrade
your app to 2.0. Rack::Adapter::Camping will be removed in the next release of
Rack, so if you're using it you should absolutely switch to Camping 2.0.
Plese read http://camping.rubyforge.org/book/51_upgrading.html#from-15-to-20
for upgrade notes and if you still have any problems, please let us know and
we'll update it.
~> What will we see 2.1?
That's up to you! What would you like to see? You don't necessarily have to
implement it yourself, just tell the rest on the mailing list and maybe
someone else helps out.
Sean Busbey is trying to make migrations suck less, and I want to make it even
more Rackified. And we'll all be smashing bugs.
~> Help, I'm stuck! Where should I go?
The mailing list: http://rubyforge.org/mailman/listinfo/camping-list
You can also try #camping on irc.freenode.net:
http://java.freenode.net/?channel=camping
// Magnus Holm