Camping is a Microframework
Camping is a Microframework
First, as a mural. For friends here who like to pick at the weave.
%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)}}})end end;class<<self;def R(*urls);Class.new(R){meta_def(:inherited){|c|
c.meta_def(:urls){urls}}};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

gem install camping --source http://code.whytheluckystiff.net/
- Installs the regular camping.rb.
- … and dependencies
activerecord,markaby and metaid.
- For an example, see the blog.
Update:

Update: Field Office Outpost Bravo Bravo reports.

Update: Ranger Douglas has sent word.

Many rangers, myself included, returned from their morning jaunt to find this tiny yarn insignia fastened to their ranger doors without explanation. It seems peaceful, like something Camping would do, but you never know. Radicals often portray future animal executions in the simplest of strokes.
Camping has not had the time or inclination to rate and classify this message, so it is being displayed in its true, unfiltered form.
Update: Reader Justin Heat sent in this awful photo.

He says, “At my aunt’s viewing. Snapped a pic for you with my Treo 650.”
Off-topic. And NOT funny. Let’s start focusing on getting through this day before we talk about this kind of stuff. For crying out loud.
Update: What luck, this month’s Boy’s Life has a feature story for PDFsubscribers only. Very in-depth. An excerpt:


Update: from #camping.
20:11 <goalie> can camping do e-commerce?
20:11 <_why> no, use rails
20:11 <RangerCliff> heh wtf?
20:13 <goalie> it's just a small store, we only have two products
20:13 <goalie> root beer lip balm and cream soda lip balm
20:13 * _why contemplates
20:14 <goalie> mail-order only
20:14 <_why> now you're talking!!
Update: 2006 uniforms are in.

Update:

Update: You may now clip the following article from your handbooks, the Camping Phys Ed staff has ejected it from the curriculum!

Update:
If you’ve come to this site looking for the Dog Scouts, or if you’ve never heard of Dog Scouts and would like to sign your dog (or dogs) up for Dog Scouts, please click on the image of the Geocaching 3 Merit Badge for Dogs (to your right, my left) and you will be whisked away to the correct site.
If you are looking for Doggles, they can be foundhere.
Matthew Cartwright from Aberdeen, WA, writes in:

My nine-year-old daughter has been captivated with Camping over the last day or so. We even got her a little tackle box to put all her supplies in. Her excitement really came to a crescendo when she read your story about Ranger Cliff hanging out inIRC. She’s been non-stop Ranger Cliff all morning.
Anyway, she’s now filming a stop-motion film about the wedding of Ranger Cliff, I’m including a still frame I just snapped over her shoulder. Ranger Cliff is the little, rotund, dark fellow.
Great work, Katy! You’ve nailed the Ranger. His balance is abyssmal.
Update: Photos are piling up on Flickr now that everyone’s back from CampingCamp, held last night at Ranger Mayhew’s house in Minneapolis.Jealous.
Alex Chanticlair took the following photo and adds:

Troops 142 and 146 were in attendance. Many a glowstick was had. I know it looks alot like a rave, but believe me, it’s a very safe, very loving environment, sort of like a rave, but with root beer and cream soda lip balms instead of Roofies or Vitamin K. Actually, Roofies are still pretty common at conventions, but usually those people are under the table and don’t pollute the general mood of self-photography.
Update: 100 bytes have been shaved from Camping 1.1. Update through gems or wherever.
Two steps to get Camping working under Lighttpd+FastCGI. At the bottom of your application script (blog.rb):
if __FILE__ == $0
require 'fcgi'
Dir.chdir(File.dirname(__FILE__))
Camping::Models::Base.establish_connection
:adapter => 'sqlite3', :database => 'blog3.db'
Camping::Models::Base.logger = Logger.new('camping.log')
FCGI.each do |req|
ENV.replace req.env
Camping.run(req.in, req.out)
req.finish
end
end
A suitable lighttpd.conf would look like:
server.port = 3044
server.bind = "127.0.0.1"
server.modules = ( "mod_fastcgi" )
server.document-root = "/var/camping/blog/"
server.errorlog = "/var/camping/blog/error.log"
#### fastcgi module
fastcgi.server = ( "/" => (
"localhost" => (
"socket" => "/tmp/camping-blog.socket",
"bin-path" => "/var/camping/blog/blog.rb",
"check-local" => "disable",
"max-procs" => 1 ) ) )
The check-local directive is vital to make sure lighttpd passes all the requests into blog.rb without throwing a 404.
Update:

Update: Reader Bill Atley writes in:
This afternoon I was at my grandfather’s house (I’m his primary caretaker) and midway through a corn feeding, I look over and this little mouse has taken the stage on a bit of carpet over by the television, no idea where he got the little mike, but he started belting away—Walk This Way, Sweet Child of Mine—classic stuff! I swear, it has to be related!

Thanks, Bill. It has to be related.
Hot tip! Give the new symmetrical R is for Routing method a try, it’s in trunk or the 1.1.18 gem on http://code.whytheluckystiff.net/ (still at 3.2k!)
Pave routes with Controllers::R:
module Camping::Controllers
class BlogEdit < R '/edit/(\d+)'; end
edn
Find a Route backwards inside controller and view methods with Helpers::R:
def Camping::Views.all_posts
for post in @posts
a :href => R(BlogEdit, post)
end
end
Also works in redirects:
redirect R(BlogEdit, 2)
One breakage overnight: if a controller and a model are named identically, you’ll need to refer to the model as Models::Blog inside the controller. See the blogging app.
Update: The Florida Friends of Camping want to send out an alert to the vast throngs of bedraggled and disenfranchised coders who don’t fathom Camping, don’t care about it and are hurt that it is even trying to do anything.

Many dogs are out of work and remain unemployed for years on end. Their prospects are weak and many humans lack the patience to help the canine hunt through Craigslist. (The mouse peripheral was specifically designed without paws in mind.) Yet, many of these dogs are our best asset when faced with that initial first stab through Camping. Call us! Our dogs are lined upright now, outside the facility, with their honorary green scarves blowing in the wind, ready to embark on a walking tour of up to 4k. It’s your enjoyment that’s at stake here!
Update:
