

A Full Web Service with HTTP caching in 7 lines - lsb
http://slightlynew.blogspot.com/2009/02/full-web-service-with-http-caching-in-7.html

======
m_eiman
Am I the only one who's tired of these "X in Y lines of code!" things? It's
obviously always many more lines of code, just hidden away from view, so meh!

Come back when you've actually done some REAL WORK in insanely tight code, not
just imported this and that lib.

~~~
aditya
Agreed, there's nothing here that couldn't have been done 5 years ago with
perl and CPAN. But, I think this post is relevant for two big reasons:

1\. Sinatra and Rack are amazing lightweight alternatives (especially for web
services) compared to the giantness that is Rails.

2\. Rack::Cache is seriously awesome (<http://tomayko.com/src/rack-cache/>)
and deserves more attention...

~~~
aaronblohowiak
Rack::Cache is neato for development, but you're better served using Varnish
for production -- it acts as a caching reverse proxy to multiple machines, is
insanely fast and battle-tested, it can be configured to serve stale content
if the backend becomes unreachable, it supports ESI, and more!

And yes, Sinatra & Rack are super cool.

------
lsb
I'm using Sinatra et al for my thesis project, for a Master's in Latin, and
minimizing the non-interesting bits of infrastructure helps a lot.

It's interesting to me for the same reason PHP is interesting: it helped me
wrap an HTTP interface over an executable in Ada with its own homegrown
database, and (sociologically) it's a social commentary on the bits of
plumbing we've agreed upon as useful.

------
juliend2
I love Sinatra. I find it very easy to use. I did a little web service that
scraps the usage stats off an ISP page with it and Hpricot. I never had so
much fun with Ruby!

~~~
lsb
Just remember, if you're in Ruby working with strings, you want a local
variable, because MRI leaks memory otherwise. See
<http://groups.google.com/group/god-rb/msg/3f5b13d6d4fa6480>

~~~
aditya
This was fixed in p287 right?

------
ph0rque
The source code is still 55 characters too many for a tweet.

~~~
rfunduk
Did you try getting it to fit? Even 'cheating' a bit by renaming the route and
make the TTL 1 second I couldn't get it closer than -2 :/

I can get to +1 character left if I just make the route '/:n'! :)

~~~
ph0rque
I thought about it, but then I remembered you'd get something like the camping
microframework:

    
    
         %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

~~~
lsb
That was the interesting thing for me: not that you couldn't do it in a
shorter number of characters, but that it was 7 lines of code written for
people to read, and incidentally for machines to execute.

~~~
rfunduk
There are many hundreds more lines in the background of the sinatra example
the OP lists (the 7 lines, that is).

------
jonursenbach
How about we all stop boasting about how many lines we can write something in,
and go back to writing quality software? Who gives a fuck how many lines it
is, only thing we should care about is if it's good or not.

~~~
alecst
Uh, novice here, and although I'm sure we all agree that quality of code >
lack of lines of code, there's no reason to bash clever programming. Who cares
if it's pragmatic -- that's obviously not the motivation.

