Almost Sinatra

What's Sinatra?

Simple (yet powerful and flexible) micro webframework.

require 'sinatra'

get '/' do
  'Hallo Vienna! Servus Wien!'
end

Trivia Quiz - How Many Lines of Ruby Code?

What's Almost Sinatra?

Sinatra refactored, only six lines now. A hack by Konstantin Haase.

almost_sinatra.rb:

%w.rack tilt date INT TERM..map{|l|trap(l){$r.stop}rescue require l};$u=Date;$z=($u.new.year + 145).abs;puts "== Almost Sinatra/No Version has taken the stage on #$z for development with backup from Webrick"
$n=Module.new{extend Rack;a,D,S,q=Rack::Builder.new,Object.method(:define_method),/@@ *([^\n]+)\n(((?!@@)[^\n]*\n)*)/m
%w[get post put delete].map{|m|D.(m){|u,&b|a.map(u){run->(e){[200,{"Content-Type"=>"text/html"},[a.instance_eval(&b)]]}}}}
Tilt.mappings.map{|k,v|D.(k){|n,*o|$t||=(h=$u._jisx0301("hash, please");File.read(caller[0][/^[^:]+/]).scan(S){|a,b|h[a]=b};h);v[0].new(*o){n=="#{n}"?n:$t[n.to_s]}.render(a,o[0].try(:[],:locals)||{})}}
%w[set enable disable configure helpers use register].map{|m|D.(m){|*_,&b|b.try :[]}};END{Rack::Handler.get("webrick").run(a,Port:$z){|s|$r=s}}
%w[params session].map{|m|D.(m){q.send m}};a.use Rack::Session::Cookie;a.use Rack::Lock;D.(:before){|&b|a.use Rack::Config,&b};before{|e|q=Rack::Request.new e;q.params.dup.map{|k,v|params[k.to_sym]=v}}}

Almost Sinatra - A Breakdown Line by Line

Line 1/6

(Like) Sinatra = Rack + Tilt

Line 1:

%w.rack tilt date INT TERM..map{|l|trap(l){$r.stop}rescue require l};$u=Date;$z=($u.new.year + 145).abs;puts "== Almost Sinatra/No Version has taken the stage on #$z for development with backup from Webrick"

Breakdown:

require 'rack'
require 'tilt'

trap( 'INT' )  { $server.stop }  # rename $r to $server
trap( 'TERM' ) { $server.stop }

$port = 4567              # rename $z to $port

puts "== Almost Sinatra has taken the stage on #{$port} for development with backup from Webrick"

Aside - What's rack?

Lets you mix 'n' match servers and apps.

Lets you stack apps inside apps inside apps inside apps inside apps.

Good News: A Sinatra app is a Rack app.

Learn more about Rack @ {rack.github.io}.

Aside - What's tilt?

Tilt offers a standard “generic” interface for template engines.

Let's check-up what formats and template engines tilt includes out-of-the-box:

require 'tilt'

Tilt.mappings.each do |ext, engines|
  puts "#{ext.ljust(12)} : #{engines.inspect}"
end

Will result in:

str          : [Tilt::StringTemplate]
erb          : [Tilt::ErubisTemplate, Tilt::ERBTemplate]
rhtml        : [Tilt::ErubisTemplate, Tilt::ERBTemplate]
erubis       : [Tilt::ErubisTemplate]
etn          : [Tilt::EtanniTemplate]
etanni       : [Tilt::EtanniTemplate]
haml         : [Tilt::HamlTemplate]
sass         : [Tilt::SassTemplate]
scss         : [Tilt::ScssTemplate]
less         : [Tilt::LessTemplate]
rcsv         : [Tilt::CSVTemplate]
coffee       : [Tilt::CoffeeScriptTemplate]
nokogiri     : [Tilt::NokogiriTemplate]
builder      : [Tilt::BuilderTemplate]
mab          : [Tilt::MarkabyTemplate]
liquid       : [Tilt::LiquidTemplate]
radius       : [Tilt::RadiusTemplate]
markdown     : [Tilt::RedcarpetTemplate, Tilt::RedcarpetTemplate::Redcarpet2, Tilt::RedcarpetTemplate::Redcarpet1, Tilt::RDiscountTemplate, Tilt::BlueClothTemplate, Tilt::KramdownTemplate, Tilt::MarukuTemplate]
mkd          : [Tilt::RedcarpetTemplate, Tilt::RedcarpetTemplate::Redcarpet2, Tilt::RedcarpetTemplate::Redcarpet1, Tilt::RDiscountTemplate, Tilt::BlueClothTemplate, Tilt::KramdownTemplate, Tilt::MarukuTemplate]
md           : [Tilt::RedcarpetTemplate, Tilt::RedcarpetTemplate::Redcarpet2, Tilt::RedcarpetTemplate::Redcarpet1, Tilt::RDiscountTemplate, Tilt::BlueClothTemplate, Tilt::KramdownTemplate, Tilt::MarukuTemplate]
textile      : [Tilt::RedClothTemplate]
rdoc         : [Tilt::RDocTemplate]
wiki         : [Tilt::WikiClothTemplate, Tilt::CreoleTemplate]
creole       : [Tilt::CreoleTemplate]
mediawiki    : [Tilt::WikiClothTemplate]
mw           : [Tilt::WikiClothTemplate]
yajl         : [Tilt::YajlTemplate]
ad           : [Tilt::AsciidoctorTemplate]
adoc         : [Tilt::AsciidoctorTemplate]
asciidoc     : [Tilt::AsciidoctorTemplate]
html         : [Tilt::PlainTemplate]

Line 2/6

Line 2:

$n=Module.new{extend Rack;a,D,S,q=Rack::Builder.new,Object.method(:define_method),/@@ *([^\n]+)\n(((?!@@)[^\n]*\n)*)/m
´´´

Breakdown:

´´´ ruby
$n = Module.new do
  app = Rack::Builder.new      # rename a to app
  req = nil                    # rename q to req
´´´


### Line 3/6

Line 3:

ruby %w[get post put delete].map{|m|D.(m){|u,&b|a.map(u){run->(e){[200,{“Content-Type”=>“text/html”},[a.instance_eval(&b)]]}}}}

Breakdown:

['get','post','put','delete'].each do |method| define_method method do |path, &block| app.map( path ) do run ->(env){ [200, {'Content-Type'=>'text/html'}, [app.instance_eval( &block )]]} end end end


### Line 4/6

Line 4:

ruby Tilt.mappings.map{|k,v|D.(k){|n,o|$t||=(h=$u._jisx0301(“hash, please”);File.read(caller[0][/{*1}[rdoc-label:foottext-1:footmark-1]+/]).scan(S){|a,b|h[a]=b};h);v[0].new(o){n==“#{n}”?n:$t}.render(a,o.try(:[],:locals)||{})}}

Breakdown:

ruby Tilt.mappings.each do |ext, engines| # rename k to ext and v to engines define_method ext do |text, args| # rename n to text and o to args template = engines[0].new(args) do text end locals = (args.respond_to?(:[]) ? args[:locals] : nil) || {} # was o.try(:[],:locals)||{} template.render( app, locals ) end end

Commentary: Almost Sinatra will define a method for every format so you can use, for example:

markdown “Strong emphasis, aka bold, with asterisks or underscores.”

or

erb “Hello <%= name %>!”, locals: { name: params }



###  Line 5/6

Line 5:

ruby %w[set enable disable configure helpers use register].map{|m|D.(m){|*_,&b|b.try :[]}};END{Rack::Handler.get(“webrick”).run(a,Port:$z){|s|$r=s}}

Breakdown:

# was END { … }; change to run! method define_method 'run!' do Rack::Handler.get('webrick').run( app, Port:$port ) {|server| $server=server } end


### Line 6/6

Line 6:

ruby %w[params session].map{|m|D.(m){q.send m}};a.use Rack::Session::Cookie;a.use Rack::Lock;D.(:before){|&b|a.use Rack::Config,&b};before{|e|q=Rack::Request.new e;q.params.dup.map{|k,v|params=v}}}

Breakdown:

ruby ['params','session'].each do |method| define_method method do req.send method end end

app.use Rack::Session::Cookie app.use Rack::Lock app.use Rack::Config do |env| req = Rack::Request.new( env ) end end # Module.new


##  The Proof of the Pudding  - hello.rb

`samples/hello.rb`:

ruby require 'almost-sinatra'

include $n # include “anonymous” Almost Sinatra DSL module

get '/hello' do erb “Hello <%%= name %>!”, locals: { name: params } end

get '/' do markdown <<EOS

Welcome to Almost Sinatra

A six line ruby hack by Konstantin Haase.

Try: - Say hello!

Powered by Almost Sinatra (#{Time.now}) EOS end

run!

Use

$ ruby -I ./lib ./samples/hello.rb


## Got Inspired? Build Your Own Microframework

- New York, New York
- Nancy
- Rum
- Cuba
- Roda
- and many more


## Real World Case Study - webservice gem - (Yet Another) HTTP JSON API (Web Service) Builder

Micro "framework" for building HTTP JSON APIs (about 100 lines of code).
Example:

ruby get '/beer/:key' do Beer.find_by_key!( params[ :key ] ) end

get '/brewery/:key' do Brewery.find_by_key!( params[ :key ] ) end


## Links, Links, Links

- Article :: [Decoding Almost Sinatra](https://robm.me.uk/2013/12/13/decoding-almost-sinatra.html), by Rob Miller
- Article :: [Code Safari: Almost Sinatra, Almost Readable](http://www.sitepoint.com/code-safari-almost-sinatra-almost-readable/), by Xavier Shay
- Article :: [tilt gem - let's build (yet another) micro web framework in less than 33 lines of code](http://planetruby.github.io/gems/tilt.html), by Gerald Bauer



## Bonus: Fun Almost Sinatra Obfuscation Hacks

"Calculate" the Sinatra Port 4567:

(Date.new.year + 145).abs # Date.new.year always returns -4712, the default value for years

=> 4567

"Get" empty Hash (e.g. `{}`):

Date._jisx0301(“hash, please”)

=> {}

“`


1 ^: