Devo_normalgbuesing

http://twitter.com/gbuesing

Testing CouchDB view functions in Ruby with Johnson

# When CouchDB view functions get complex, it's nice to be able to unit test them.
# When your development dataset is large, and views take some time to build, better
# to find bugs via unit tests over having to wait for a view build to see if things
# are working correctly.

# This implementation uses Johnson for executing Javascript -- see the evaluate_map method
# at the end of this file. Note that we can define the emit and log functions as Ruby Proc 
# objects and have them work in the Javascript context. Pretty cool.

# I think it would be possible to test show and list functions as well, using a similar
# technique to mock out the getRow() and send() functions.
require 'rubygems'
require "johnson"
require "json"
require "test/unit"

# Johnson monkeypatch, so that assert_equal works as expected with array values
class Johnson::SpiderMonkey::RubyLandProxy
  def ==(other)
    case other
    when Array
      to_a == other
    else
      super
    end
  end
end

class TestViews < Test::Unit::TestCase

  def setup
    # Set the design doc in an instance variable. In practice, you'll want to get the 
    # design doc from the server using your preferred HTTP client, ex:
    # 
    #   @ddoc = JSON.parse RestClient.get('localhost:5984/mydb/_design/myapp')
    #
    # If you're getting the same ddoc again and again for each test, you can store it in a
    # class variable or constant on test suite startup, thus avoiding the overhead of 
    # HTTP and JSON parsing of the ddoc for each test.
    @ddoc = JSON.parse(<<-EOV)
      {
        "_id": "_design/myapp",
        "views": {
          "by_city": {
            "map": "function(doc) { emit([doc.city, doc.name], doc); }"
          }
        }
      }
    EOV
  end

  def test_by_city
    mapfun = @ddoc['views']['by_city']['map']
    doc = {"name" => "Geoff", "city" => "Chicago", "state" => "IL"}
    
    rows = evaluate_map(mapfun, doc)['rows']
    
    assert_equal 1, rows.size
    assert_equal ["Chicago", "Geoff"], rows.first['key']
    assert_equal doc, rows.first['value']
  end
  

  private
    def evaluate_map(fun, doc)
      rows, log = [], []
      emitfun = Proc.new {|k, v| rows << {"key" => k, "value" => v}}
      logfun = Proc.new {|m| log << m}
      map = Johnson.evaluate(fun, :emit => emitfun, :log => logfun)
      map.call(doc)
      {"rows" => rows, "log" => log}
    end
end
Reveal More
Added over 1 year ago

Automatic html escaping with Sinatra and Erubis

# Requires Sinatra >= 1.0 and Tilt >= 0.9
# (fyi Tilt 0.8 is bundled with Sinatra 1.0, so you'll need to upgrade 
# to the latest gem version)
#
# Tilt 0.9 adds an :escape_html option for Erubis, which allows you
# to leverage Erubis' auto-escape functionality, so that anything inside 
# <%= %> blocks will be html-escaped by default. 
#
# To skip escaping, use a <%== %> block.
require 'rubygems'
gem "sinatra", ">= 1.0"
gem "tilt", ">= 0.9"
require 'sinatra'
require 'erubis'

# Globally set erubis to render with auto-escaping of html
set :erubis, :escape_html => true

get '/' do
  erubis :index
end

helpers do
  # Convenience method for manually escaping html
  def h(text)
    Rack::Utils.escape_html(text)
  end
  
  def link_to(text, href)
    %(<a href="#{href}">#{h(text)}</a>)
  end
end

__END__

@@ index
<p><%= "This string <b>will</b> be html escaped."%></p>
<p><%== "This string <b>will not</b> be html escaped."%></p>
<p><%== link_to "This link tag inner text <b>will</b> be escaped manually in our link_to helper, but the surrounding tag will not", "#" %></p>

@@ layout
<!DOCTYPE HTML>
<head><title>Erubis escaping test</title></head>
<body><%== yield %></body>
Reveal More
Added over 1 year ago

Run Once Proc

class RunOnceProc < Proc
  class MultipleCallAttempt < StandardError; end
  
  def call(*args)
    raise(MultipleCallAttempt, "You cannot call this proc more than once") if called?
    @_called = true
    super
  end
  
  def called?
    !!@_called
  end
  
end

p = RunOnceProc.new {|name| puts "hi #{name}"}

p.call("John")  # => hi John
p.call("Paul")  # => RunOnceProc:: MultipleCallAttempt: You cannot call this proc more than once
Reveal More
Added almost 2 years ago

Passing a block to Rack::Response#finish

# Passing a block to Rack::Response#finish
#
# You can pass a block to #write, which yields self;
# calls to #write inside the block will write directly to the
# output stream instead of Rack::Response's internal buffer
class App
  def call(env)
    Rack::Response.new.finish do |resp|
      resp.write "one\n"
      sleep 1
      resp.write "two\n"
      sleep 1
      resp.write "three\n"
    end
  end
end

run App.new

# Run with "rackup" command; use curl to connect:
#
#   > curl localhost:9292
#   one
#   two
#   three
Reveal More
Added almost 2 years ago

Search for Ruby gems on the command line

# Just found out about this one. You can search for gems on remote sources
# (Gemcutter etc.) via the "gem search" command.
# For docs see http://docs.rubygems.org/read/chapter/10#page36

gem search -r sinatra

adamwiggins-sinatra (0.10.1)
async_sinatra (0.1.5)
BJClark-sinatra-content-for (0.2.1)
blindgaenger-sinatra-rest (0.3.3)
bmizerany-sinatra (0.9.1)
cehoffman-sinatra-respond_to (0.3.6)
christiank-sinatra-entries-visible (0.1.0)
... etc ...
Reveal More
Added almost 2 years ago

Base62 encoding in Ruby

# Base62 encoding in Ruby
# Useful for creating tiny url slugs from numeric ids
#
# Example:
#   Base62.encode 1_000_000   # => "4c92"
module Base62
  CHARS = ('0'..'9').to_a + ('a'..'z').to_a + ('A'..'Z').to_a

  # Adapted from http://refactormycode.com/codes/125-base-62-encoding
  def self.encode(i)
    return '0' if i == 0
    s = ''
    while i > 0
      s << CHARS[i.modulo(62)]
      i /= 62
    end
    s.reverse!
    s
  end
end
Reveal More
Added almost 2 years ago

Simple server-side geocoding

# Very simple server-side lat/lng lookup via Google's geocoder service
#
# Example:
#
#   point = Geocoder.get "Wrigley Field"
#   point.lat   # => 41.9482
#   point.lng   # => -87.6557

%w(rubygems rack restclient json).each {|lib| require lib}

module Geocoder
  class Location < Struct.new(:name, :lat, :lng); end

  def self.get(location)
    resp = JSON.parse(get_raw(location))
    if placemark = resp["Placemark"]
      new_location(location, placemark[0])
    end
  end

  def self.get_raw(location)
    uri = "http://maps.google.com/maps/geo?sensor=false&output=json&q=#{Rack::Utils.escape(location)}"
    RestClient.get(uri)
  end

  private
    def self.new_location(location, placemark)
      Location.new(location, placemark["Point"]["coordinates"][1], placemark["Point"]["coordinates"][0])
    end
end
Reveal More
Added about 2 years ago

Rack middleware for modifying response headers

module Rackables
  # Allows you to tap into the response headers hash. Example:
  #
  #   use Rackables::ResponseHeaders do |headers|
  #     headers['X-Foo'] = 'bar'
  #     headers.delete('X-Baz')
  #   end
  #
  class ResponseHeaders
    def initialize(app, &block)
      @app = app
      @block = block
    end

    def call(env)
      response = @app.call(env)
      headers = ::Rack::Utils::HeaderHash.new(response[1])
      @block.call(headers)
      response[1] = headers
      response
    end
  end
end
Reveal More
Added about 2 years ago

Rack.env and Rack.env.development? (& etc.) for Rack apps

# Adds pretty reader and query methods for ENV['RACK_ENV'] value.
# A copy of Rails' Rails.env for Rack apps.
#
# Examples:
#
#   Rack.env                # => "development"
#   Rack.env.development?   # => true
module Rack
  def self.env
    @env ||= Utils::StringInquirer.new(ENV["RACK_ENV"] || "development")
  end

  module Utils
    # TAKEN FROM ACTIVE SUPPORT
    class StringInquirer < String
      def method_missing(method_name, *arguments)
        if method_name.to_s[-1,1] == "?"
          self == method_name.to_s[0..-2]
        else
          super
        end
      end
    end
  end
end
Reveal More
Added about 2 years ago

Rack::Get middleware, for simple Sinatra-like routing

# Simplest example:
#
#   use Rack::Get, '/ping_monitor' do
#     'pong'
#   end
#
# Rack::Response object is yielded as second argument to block:
#
#   use Rack::Get, '/json' do |env, response|
#     response['Content-Type'] = 'application/json'
#     %({"foo": "bar"})
#   end
#
# Example with regular expression -- match data object is yielded as third argument to block
# 
#   use Rack::Get, %r{^/(john|paul|george|ringo)} do |env, response, match|
#     "Hello, #{match[1]}"
#   end
#
# A false/nil return from block will not return a response; control will continue down the
# Rack stack:
#
#   use Rack::Get, '/api_key' do |env|
#     '12345' if env['myapp.user'].authorized?
#   end
module Rack
  class Get
    def initialize(app, path, &block)
      @app = app
      @path = path
      @block = block
    end

    def call(env)
      path = env['PATH_INFO'].to_s
      if env['REQUEST_METHOD'] == 'GET' && ((@path.is_a?(Regexp) && match = @path.match(path)) || @path == path)
        response = Rack::Response.new
        response.body = @block.call(env, response, match)
        response.body ? response.finish : @app.call(env)
      else
        @app.call(env)
      end
    end
  end
end
Reveal More
Added about 2 years ago

Rack::Branch, a very simple routing middleware

# Rack::Branch lets you conditionally re-route the Rack stack at runtime to
# an alternate endpoint.
#
# You initialize this middleware with a block, which should either 1. return a
# valid rack endpoint, when want to branch away from the current Rack pipeline, 
# or 2. nil/false, when you want to continue on. The block is passed the current
# Rack env hash.
#
# config.ru usage example:
#
#   use Rack::Branch do |env|
#     ApiApp if env["PATH_INFO"] =~ /\.xml$/
#   end
#
#   run MyEndpointApp
#
# A slightly more complex example with multiple endpoints:
#
#   use Rack::Branch do |env|
#     if env['PATH_INFO'] =~ %r{^\/foo\/(bar|baz)(.*)$/}
#       env['PATH_INFO'] = $2
#       {'bar' => BarApp, 'baz' => BazApp}[$1]
#     end
#   end
#
#   run MyEndpointApp
module Rack
  class Branch
    def initialize(app, &block)
      @app = app
      @block = block
    end

    def call(env)
      app = @block.call(env) || @app
      app.call(env)
    end
  end
end
Reveal More
Added about 2 years ago

User-friendly exception page Rack middleware

module Rack
  # Returns a user-friendly exception page.
  # Should be included at the very top of the middleware pipeline so that unhandled 
  # exceptions from anywhere down the pipeline are rescued.
  # In development, you'd want to use the developer-friendly Rack::ShowExceptions instead. 
  # 
  # config.ru example:
  #
  #   if ENV['RACK_ENV'] == 'development'
  #     use Rack::ShowExceptions
  #   else
  #     use Rack::PublicExceptionPage
  #   end
  #
  # The default HTML included here is a copy of the 500 page included with Rails
  # You can optionally specify your own file, ex:
  #
  #   use Rack::PublicExceptionPage, "public/500.html"
  class PublicExceptionPage

    def initialize(app, file_path = nil)
      @app = app
      @file_path = file_path
    end

    def call(env)
      @app.call(env)
    rescue ::Exception
      [500, {'Content-Type' => 'text/html', 'Content-Length' => html.length.to_s}, [html]]
    end

    private

      def html
        @html ||= @file_path ? ::File.read(@file_path) : default_html
      end

      # Exception page from Rails (500.html)
      def default_html
        <<-EOV
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
               "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

        <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

        <head>
          <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
          <title>We're sorry, but something went wrong (500)</title>
        	<style type="text/css">
        		body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
        		div.dialog {
        			width: 25em;
        			padding: 0 4em;
        			margin: 4em auto 0 auto;
        			border: 1px solid #ccc;
        			border-right-color: #999;
        			border-bottom-color: #999;
        		}
        		h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
        	</style>
        </head>

        <body>
          <!-- This file lives in public/500.html -->
          <div class="dialog">
            <h1>We're sorry, but something went wrong.</h1>
            <p>We've been notified about this issue and we'll take a look at it shortly.</p>
          </div>
        </body>
        </html>
        EOV
      end

  end
end
Reveal More
Added about 2 years ago

Using Rack::Test to test the entire app stack as defined in config.ru

# Rack::Test requires you to return an app object from the #app method in your test case.
# If you want to test one app in isolation, you just return that app, but if you want
# to test the entire app stack, including middlewares, cascades etc. you need to parse
# the app defined in config.ru.
#
# With the latest Rack version 1.1.0, you can use Rack::Builder.parse_file to return the
# outer app; with older Rack versions, you'll need to do the evaling yourself, like so:
#
#   OUTER_APP = eval("Rack::Builder.new {( " + ::File.read('config.ru') + "\n )}.to_app")

OUTER_APP = Rack::Builder.parse_file('config.ru').first

class TestApp < Test::Unit::TestCase
  include Rack::Test::Methods
  
  def app
    OUTER_APP
  end
  
  def test_root
    get '/'
    assert last_response.ok?
  end
end
Reveal More
Added about 2 years ago

Simple node.js example: output ps command

// This node.js example wraps the result of running "ps -a" in HTML
// and serves it at localhost:8080. It uses a meta tag
// to refresh the display every 5 seconds.

var sys = require('sys'), 
http = require('http'),
command = "ps -a";

function buildHtml(str) {
  var head = '<head><meta http-equiv="refresh" content="5"></head>';
  var body = '<body><pre>' + str + '</pre></body>';
  return ['<html>', head, body, '</html>'].join('\n');
}

http.createServer(function(req, res) {
  sys.exec(command).addCallback(function (stdout, stderr) {
    res.sendHeader(200, {'Content-Type': 'text/html'});
    res.sendBody(buildHtml(stdout));
    res.finish();
  });
}).listen(8080);

sys.puts('Server running at http://127.0.0.1:8080/');
Reveal More
Added about 2 years ago

Re: How to mount multiple Sinatra apps in config.ru (Rack::Cascade example)

# A simple example of mounting multiple Sinatra apps in config.ru
# MODIFIED: uses Rack::Cascade

# To run this example:
#   1. put this code in a config.ru file
#   2. run "shotgun" on the command line (http://github.com/rtomayko/shotgun)
#   3. go to http://localhost:9393/

require 'sinatra/base'

class MoonApp < Sinatra::Base
  get '/moon' do
    "Howard Moon"
  end
  
  get '/' do
    env['moon.message'] = "Hello from Moon"
    # pass
    [404, {}, []] # return 404 instead of invoking "pass"
  end
end

class NoirApp < Sinatra::Base
  get '/noir' do
    "Vince Noir"
  end
  
  get '/' do
    env['moon.message'] # set in MoonApp
  end
end

# use MoonApp
# run NoirApp
run Rack::Cascade.new [MoonApp, NoirApp]
Reveal More
Added about 2 years ago

A very simple Rack-mount example with multiple Rack apps

# To run this example:
#   1. put this code in a config.ru file
#   2. run "shotgun" on the command line (http://github.com/rtomayko/shotgun)
#   3. go to http://localhost:9393/

require 'rack/mount'

class MoonApp
  def self.call(env)
    case env['rack.routing_args'][:action]
    when 'moon'
      html = %(Moon | <a href="#{Routes.url(:noir)}">Noir</a> | <a href="#{Routes.url(:boosh)}">Boosh</a><h1>Howard Moon's Homepage</h1>)
      [200, {'Content-Type' => 'text/html'}, [html]]
    when 'boosh'
      env['moon.message'] = "Hello from Moon"
      # Rack-mount will look for other matches when 417 is returned
      [417, {}, []]
    end
  end
end

class NoirApp
  def self.call(env)
    html = case env['rack.routing_args'][:action]
    when 'noir'
      %(<a href="#{Routes.url(:moon)}">Moon</a> | Noir | <a href="#{Routes.url(:boosh)}">Boosh</a><h1>Vince Noir's Homepage</h1>)
    when 'boosh'
      %(<a href="#{Routes.url(:moon)}">Moon</a> | <a href="#{Routes.url(:noir)}">Noir</a> | Boosh<h1>Boosh Homepage</h1><p>#{env['moon.message']}</p>)
    end
    [200, {'Content-Type' => 'text/html'}, [html]]
  end
end

Routes = Rack::Mount::RouteSet.new do |set|
  set.add_route MoonApp, { :path_info => '/moon' }, { :action => 'moon' }, :moon
  set.add_route NoirApp, { :path_info => '/noir' }, { :action => 'noir' }, :noir
  set.add_route MoonApp, { :path_info => '/' }, { :action => 'boosh' }, :boosh
  set.add_route NoirApp, { :path_info => '/' }, { :action => 'boosh' }
end

run Routes
Reveal More
Added about 2 years ago

How to mount multiple Sinatra apps in config.ru

# A simple example of mounting multiple Sinatra apps in config.ru

# To run this example:
#   1. put this code in a config.ru file
#   2. run "shotgun" on the command line (http://github.com/rtomayko/shotgun)
#   3. go to http://localhost:9393/

require 'sinatra/base'

class MoonApp < Sinatra::Base
  get '/moon' do
    "Howard Moon"
  end
  
  # this action doesn't return an HTTP response -- it sets an env variable, 
  # and passes, so that another action in this app or another app further 
  # down the Rack pipeline has the opportunity to match this route as well
  get '/' do
    env['moon.message'] = "Hello from Moon"
    pass
  end
end

class NoirApp < Sinatra::Base
  get '/noir' do
    "Vince Noir"
  end
  
  get '/' do
    env['moon.message'] # set in MoonApp
  end
end

# We're mounting MoonApp as a middleware, and NoirApp as as an endpoint.
# This makes sense, because MoonApp is in the middle of the chain, and NoirApp
# is at the end of the line. MoonApp will be checked first for matching routes,
# followed by NoirApp.
use MoonApp
run NoirApp

# When a Sinatra app is run as a middleware, it will call the next app in the Rack
# pipeline when a route is not matched, instead of raising a NotFound error.
# For more on how this actually works, check out the code for the Rack::Builder 
# class and Sinatra::Base#initialize, #call! and #route_missing.
Reveal More
Added about 2 years ago

Rack middleware to 301 urls with trailing slashes

# Rather than serving a resource at both /foo and /foo/, I'd rather just serve it
# at /foo, and return a 301 for /foo/, so that Google won't think I have separate
# pages with the same content, and I won't be keeping duplicate content in caches.
#
# This middleware handles this. Another strategy would be to handle this in Nginx 
# instead of middleware.
module Rack
  class TrailingSlash

    def initialize(app)
      @app = app
    end

    def call(env)
      if env['PATH_INFO'] =~ %r{^/(.*)/$}
        location = "#{env['rack.url_scheme']}://#{env['HTTP_HOST']}/#{$1}"
        location = "#{location}?#{env['QUERY_STRING']}" if env['QUERY_STRING'].to_s =~ /\S/
        [301, {"Location" => location}, []]
      else
        @app.call(env)
      end
    end

  end
end
Reveal More
Added about 2 years ago
Post Code