A nice button

input[type=submit] {

  font-size: inherit;

  padding: 0.5ex 1ex;
  margin: 0;

  border: 1px solid #ccc;

  /* http://border-radius.com/ */
  -webkit-border-radius: 4px;
  -moz-border-radius: 4px;
  border-radius: 4px;

  /* http://www.colorzilla.com/gradient-editor/ */
  background: #ffffff; /* Old browsers */
  background: -moz-linear-gradient(top, #ffffff 0%, #cccccc 100%); /* FF3.6+ */
  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#cccccc)); /* Chrome,Safari4+ */
  background: -webkit-linear-gradient(top, #ffffff 0%,#cccccc 100%); /* Chrome10+,Safari5.1+ */
  background: -o-linear-gradient(top, #ffffff 0%,#cccccc 100%); /* Opera11.10+ */
  background: -ms-linear-gradient(top, #ffffff 0%,#cccccc 100%); /* IE10+ */
  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#cccccc',GradientType=0 ); /* IE6-9 */
  background: linear-gradient(top, #ffffff 0%,#cccccc 100%); /* W3C */
}

re-scoping javascript callback functions using call and apply

Preamble:
jQuery assigns the this object in an event handler to the element that the event handler is bound to. For example, the following will log the element with the id arbitrary-id:

$("#arbitrary-id").click(function (e) { console.log(this); });

But what if we don’t want this to be bound the element we just clicked on? We can get the element anyways by referring to e.currentTarget. What if we want to assign this to an arbitrary element?

Problem:
Develop a function, accessible as a method on any function, that will allow us to set the element this refers to inside the function. For example, this code will log the element with the id my-foo-div:

$("#arbitrary-id").click(function (e) { console.log(this); }.bind( $("#my-foo-div") ));

Solution:
We need this method to be available on any function. We can add it to the Function prototype to accomplish this.

As an aside, why do we all cringe whenever anyone mentions doing anything to an object’s prototype? Are there really no safe use-cases? It seems like too powerful a tool to ignore completely.

But anyway, here’s the start of a method definition:

Function.prototype.bind = function (element) { console.log(element); };

We can call it like this:

( function () {} ).bind( $("#arbitrary-id") );

How to define the this object inside the callback function called by the jQuery click handler?  Hmm … Well, the callback function is just a function and we did just add a bind method to all functions, so we can start there. What if we just assign this to the element passed in, like so:

Function.prototype.bind = function (element) { this = element; console.log(this); };

Okay, that threw “ReferenceError: Invalid left-hand side in assignment”, so let’s try the call method, which exists to set the scope of a function at the time it’s called.

Function.prototype.bind = function (element) { console.log(this); };
( function () { console.log( this ); } ).call( $("#my-foo-div") ).bind( $("#my-foo-div") );

Things are starting to get seriously weird, and it looks like call doesn’t return a function anyway, which means we can’t chain bind to it. What are we doing again? Oh, yeah, basically, we want to be able to call call on a function, but have the function be runnable later as a callback, something like a deferred call.

We can at least get bind to return a function, so the function’s callable later:

Function.prototype.bind = function (element) { return function () { console.log(element); } };

Now if I run $("#arbitrary-id").click(function (e) { console.log(this); }.bind( $("#my-foo-div") ) );, the callback logs the element with id my-foo-div. So, I have a reference to my-foo-div inside the callback for the event handler attached to arbitrary-id. I think this is progress. I just need to get the callback function to run in the scope of my-foo-div and I think I can do this with call.

Function.prototype.bind = function (element) { return function () { console.log( this.call( element ) ); } };

I’m getting an error “Uncaught TypeError: Object # has no method ‘call'”, which makes sense because jQuery is still setting this to the element that the event handler is attached to, which is the whole point of this exercise, but we’re close! I need to get this to refer to the callback function itself, not a dom element. Inside the bind function definition, this refers to the caller, which is the callback function. Let’s cache this in the scope of the callback function, and then refer to the cached object inside the returned function:

Function.prototype.bind = function (element) { var that = this; return function () { console.log( that ); } };

Nice! When I click on the arbitrary-id element, I see “function (e) { console.log(this); }”, which is my stringified callback function, in the log. So now I just need to call that function via the call method, passing in the element I want to bind the scope to:

Function.prototype.bind = function (element) { var that = this; return function () { that.call( element ); } };

Yay! It works. Before calling it quits, I’d like to be able to pass arguments to the callback. Fortunately, call‘s cousin apply and the native arguments object makes this easy:

Function.prototype.bind = function (element) { var that = this; return function () { that.apply( element, arguments ); } };

This calls for an image to chill out to. Here’s a picture of a toucan doing his thing:

Toucan Three by Rhea Monique
Toucan Three by Rhea Monique

Ruby utility for YQL storage

motivation:

  • I want to be able to interact w/ YQL storage as easily as I can w/ SQLite on my own machine.  Ideally, I’d just like to be able to say storage.use(‘table’).set(‘foo’, ‘bar’) and forget about it.

overview:

  • This class is based on the SQLite utility class, YQL utility function, and simple key/val layer for YQL storage I recently posted.  The methods available are use(), set(), and get().  The use() method accepts the select, update, and execute addresses of a YQL storage record.  Calling get() or set() fires off a request to read or write, respectively, a key using the YQL key/val table.

requirements/environment:

  • An “installation” (it’s just a couple static files on your server) of the YQL key/val table mentioned above
  • All other requirements are the same as for the SQLite class & YQL fn mentioned above

code:

class YqlStorage
  def use(settings={})
    @settings = settings
    return self
  end
  def get(key)
    
    response = yql( %{
      use 'http://{your domain}/kv.xml' as kv;
      select * from kv where key = '%s' and select = '%s'
    } % [ key, @settings[:select] ] )
    
    if response['error']
      raise 'error: %s ' % response['error']['description']
    elsif !response['query']['results']
      return nil
    end
    
    return response['query']['results']['result']
  end
  
  def set(key, val)

    response = yql( %{
      insert into kv (key, val) values ('%s', '%s')
    } % [ key, val ], { 'env' => 'http://{your domain}/kv.env' } )
    
    if response['error']
      raise 'YQL error: %s ' % response['error']['description']
    end
    
    return response
  end
end

usage:

  1. Save the code below into a file
  2. Edit the file to change all occurrences of ‘{your domain}’ to your domain
  3. Use (ha!) the YQL storage addresses defined in the key/val table setup for the use() settings
  4. here’s some example code
    store = YqlStorage.new.use( {
      # get these from YQL: http://developer.yahoo.com/yql/console/#h=desc%20yql.storage.admin
      :execute => 'store://h5Y4iRockdwzZdEHvGbBkCe',
      :select => 'store://deGTN05aNePaper04EOL30W',
      :update => 'store://qG4Scissors8917SHDjv88Wb'
    } )
    store.set( 'foo', 'bar' )
    p store.get( 'foo' )
    

Please let me know if you’ve got any suggestions/questions.  

And now, to lighten the mood, here’s a picture of a squirrel yawning:
Squirrel yawning
Photo credit: _temaki_

initial attempt at simple key/val layer on YQL storage

motivation:

  • YQL storage is very nice.  It’s globally distributed, performant, and free(!), but if you want to say “get me value where key = ‘foo'”, you need to apply a layer to map our keys to YQL’s keys.  Here’s my first attempt at this.

overview:

  • A single YQL storage record is used to store a JavaScript hash, JSON encoded.  When a value is requested, the table extracts the JSON from storage, decodes it, and applies the key passed w/ the request (we’ll let JS manage the mapping of keys to values ;).  If the request is to modify or delete the value, the updated hash is re-encoded to JSON and saved back to storage.

requirements/environement:

usage:

  1. Save the code below to a file on your server, eg kv.xml
  2. Edit the file to change all occurrences of ‘{your domain}’ to your domain
  3. Create a file called kv.env and define your table url and YQL storage addresses into it, as described in the YQL documentation
  4. For select queries, just pass the select address in the request.  For all other queries, set env={your domain}/kv.env in the request URL

code:

<?xml version="1.0"?>
<table xmlns="http://query.yahooapis.com/v1/schema/table.xsd">
    <meta>
        <author>Erik Eldridge</author>
        <documentationURL></documentationURL>
        <sampleQuery>use 'http://{your domain}/kv.xml' as kv; select * from kv where key='foo' and select='store://{select store id}'</sampleQuery>
    </meta>
    <bindings>
        <select itemPath="" produces="XML">
            <urls><url></url></urls>
            <inputs>
                <key id="key" type="xs:string" paramType="variable" required="true"/>
                <key id="select" type="xs:string" paramType="variable" required="true"/>
            </inputs>
            <execute>                
                // http://www.JSON.org/json2.js w/ alert removed
                y.include('http://{your domain}/json2.js');
                
                // supplant fn (credit: http://www.crockford.com/javascript/remedial.html)
                if(!String.prototype.supplant){String.prototype.supplant=function(o){return this.replace(/{([^{}]*)}/g,function(a,b){var r=o[b];return typeof r==='string'||typeof r==='number'?r:a;});};}
                
                // response
                response.object = function () {
                    var queries = [],
                        results = [];
                
                    queries[0] = "select * from yql.storage where name = '{select}'"
                        .supplant( { 'select' : select } );
                    results[0] = y.xmlToJson( y.query(queries[0]).results );
                    
                    if ( results[0].results.result.value ) {
                        return results[0].results.result.value[key];
                    }
                }();
            </execute>
        </select>
        <insert>
            <!-- sample query: use 'http://{your domain}/kv.xml' as kv; insert into kv (key, val) values ('foo', 'bar')
            <!-- note: use env file to define table url, select, & update -->
            <urls><url></url></urls>
            <inputs>
                <key id="select" type="xs:string" paramType="variable" required="true"/>
                <key id="update" type="xs:string" paramType="variable" required="true"/>
                <value id="key" type="xs:string" paramType="variable" required="true"/>
                <value id="val" type="xs:string" paramType="variable" required="true"/>
            </inputs>
            <execute>
                // http://www.JSON.org/json2.js w/ alert removed
                y.include('http://{your domain}/json2.js');
                
                // supplant fn (credit: http://www.crockford.com/javascript/remedial.html)
                if(!String.prototype.supplant){String.prototype.supplant=function(o){return this.replace(/{([^{}]*)}/g,function(a,b){var r=o[b];return typeof r==='string'||typeof r==='number'?r:a;});};}
                
                // response
                response.object = function () {
                    var queries = [],
                        results = [];
                    
                    queries[0] = "select * from yql.storage where name = '{select}'"
                        .supplant( { 'select' : select } );
                    results[0] = y.xmlToJson( y.query(queries[0]).results );
                    
                    results[0].results.result.value[key] = val;
                    
                    queries[1] = "update yql.storage set value = '{value}' where name = '{update}'"
                        .supplant( { 
                            'value' : JSON.stringify( results[0].results.result.value ), 
                            'update' : update
                        } );
                    results[1] = y.xmlToJson( y.query( queries[1] ).results );
                    
                    return results[1].results
                }();
            </execute>
        </insert>
        <update>
            <!-- note: use env file to define table url, select, & update -->
            <urls><url></url></urls>
            <inputs>
                <key id="select" type="xs:string" paramType="variable" required="true"/>
                <key id="update" type="xs:string" paramType="variable" required="true"/>
                <key id="key" type="xs:string" paramType="variable" required="true"/>
                <value id="val" type="xs:string" paramType="variable" required="true"/>
            </inputs>
            <execute>
                // http://www.JSON.org/json2.js w/ alert removed
                y.include('http://{your domain}/json2.js');
                
                // supplant fn (credit: http://www.crockford.com/javascript/remedial.html)
                if(!String.prototype.supplant){String.prototype.supplant=function(o){return this.replace(/{([^{}]*)}/g,function(a,b){var r=o[b];return typeof r==='string'||typeof r==='number'?r:a;});};}
                
                // response
                response.object = function () {
                    var queries = [],
                        results = [];
                    
                    queries[0] = "select * from yql.storage where name = '{select}'"
                        .supplant( { 'select' : select } );
                    results[0] = y.xmlToJson( y.query(queries[0]).results );
                    
                    if ( !results[0].results.result.value || !results[0].results.result.value[key] ) {
                        return {
                            error : 'key not found'
                        }
                    }
                    
                    results[0].results.result.value[key] = val;
                    
                    queries[1] = "update yql.storage set value='{value}' where name='{update}'"
                        .supplant( { 
                            'value' : JSON.stringify( results[0].results.result.value ), 
                            'update' : update
                        } );
                    results[1] = y.xmlToJson( y.query( queries[1] ).results );
                    
                    return results[1].results
                }();
            </execute>
        </update>
        <delete>
            <!-- sample query: use 'http://{your domain}/kv.xml' as kv; delete from kv where key='foo' -->
            <!-- note: use env file to define table url, select, & update -->
            <urls><url></url></urls>
            <inputs>
                <key id="select" type="xs:string" paramType="variable" required="true"/>
                <key id="update" type="xs:string" paramType="variable" required="true"/>
                <key id="key" type="xs:string" paramType="variable" required="true"/>
            </inputs>
            <execute>
                // http://www.JSON.org/json2.js w/ alert removed
                y.include('http://{your domain}/json2.js');
                
                // supplant fn (credit: http://www.crockford.com/javascript/remedial.html)
                if(!String.prototype.supplant){String.prototype.supplant=function(o){return this.replace(/{([^{}]*)}/g,function(a,b){var r=o[b];return typeof r==='string'||typeof r==='number'?r:a;});};}
                
                // response
                response.object = function () {
                    var queries = [],
                        results = [];
                    
                    queries[0] = "select * from yql.storage where name = '{select}'"
                        .supplant( { 'select' : select } );
                    results[0] = y.xmlToJson( y.query(queries[0]).results );
                    
                    if ( !results[0].results.result.value || !results[0].results.result.value[key] ) {
                        return {
                            error : 'key not found'
                        }
                    }
                    
                    delete results[0].results.result.value[key];
                    
                    queries[1] = "update yql.storage set value='{value}' where name='{update}'"
                        .supplant( { 
                            'value' : JSON.stringify( results[0].results.result.value ), 
                            'update' : update
                        } );
                    results[1] = y.xmlToJson( y.query( queries[1] ).results );
                    
                    return results[1].results
                }();
            </execute>
        </delete>
    </bindings>
</table>

Please post back w/ suggestions/questions.  Here’s a duck for good luck:

Flickr picture of a mallard

Photo credit: foxypar4

A simple Ruby Rack router

Motivation

  • I want to be able to handle http requests.  I like the simplicity of Rack, but I want more control over the initial mapping.  I like Sinatra, but I want to easily handle request methods other than GET, PUT, POST, DELETE, and HEAD. So, the Router class below maps regular expressions to handler functions.

Requirements/Environment

  • Mac os x 10.5.8
  • Ruby 1.8.6 (2008-08-11 patchlevel 287) [universal-darwin9.0]
  • Rack gem version 1.1.0

Code

class Router
  def initialize(routes)    
    @routes = routes
  end
  def default
    [ 404, {'Content-Type' => 'text/plain'}, 'file not found' ]
  end
  def call(env)
    @routes.each do |route|
      match = env['REQUEST_PATH'].match(route[:pattern])
      if match
        return route[:controller].call( env, match )
      end
    end
    default
  end
end

Usage

# assumes router code is in router.rb
require 'router'

use Rack::CommonLogger
use Rack::ShowExceptions
use Rack::Lint
use Rack::Static, :urls => ["/static"]

run Router.new([
  {
    :pattern => %r{^/page1$}, 
    :controller => lambda do |env, match|
    
      [ 200, {'Content-Type' => 'text/html'}, 'page 1' ]

    end
  },
  {
    :pattern => %r{^/}, 
    :controller => lambda do |env, match|
    
      [ 200, {'Content-Type' => 'text/html'}, 'index!' ]
  
    end
  }
  
]);

Run it on the command line using Rack’s native Rackup ($ rackup config.ru) or via Ryan Tomayko’s shotgun, which conveniently auto-reloads.

Reference

Update Mar 5, 2011

  • because Ruby < 1.9 doesn't preserve ordering in hashes, I’ve updated the code to use an array of routes. It’s more verbose, but now the route matching correctly runs top to bottom.
  • I also added a method for overriding the default response from the router

Update Mar 9, 2011

updated Ruby utility for simple SQLite3 key/val storage

motivation:

  • modify my previous sqlite utility to allow for table name definition & auto table creation

requirements/environment:

  • the requirements and environment are the same as in my previous post

code:

require 'rubygems'
require 'sqlite3'

class Storage
  def initialize
    @db = SQLite3::Database.new( 'sqlite' )
  end
  def create(table)
    begin
    @db.execute( %{
      CREATE TABLE #{table}
      (key varchar(100) PRIMARY KEY,
      value varchar(1000))
    } )
    rescue SQLite3::SQLException => details
      # puts details
    end
  end
  def use(table)
    @table = table
    self.create( table )
    return self
  end
  def get(key)
    results = @db.get_first_row( %{
      SELECT value FROM #{@table} WHERE key='#{key}'
    } )
    if results
      return results[0]
    end
  end
  def set(key, val)
    result = @db.execute( %{
      REPLACE INTO %s
      (key, value)
      VALUES ('%s', '%s')
    } % [@table, key, val ] )
  end
end

usage:

require 'rubygems'
require 'sqlite3'
require 'json'

Storage.new.use( 'user' ).set( 'user123', { :str => 'orale!' }.to_json )

json = Storage.new.use( 'user' ).get( 'user123' )

p JSON.parse json

notes:

  • DataMapper looks like it might provide much of this functionality. If my script’s complexity continues to increase, I’m inclined to incorporate a more sophisticated solution to run it. Thoughts?

a simple SQLite key/val store in Ruby

motivation:

  • I just like having a place to store blobs of data on my local machine

environment/requirements:

  • Mac os x 10.5.8
  • ruby 1.8.6 (2008-08-11 patchlevel 287) [universal-darwin9.0]
  • SQLite3
  • sqlite3-ruby (1.2.1) gem

code:

require 'rubygems'
require 'sqlite3'
class Storage
  def self.init
    db = SQLite3::Database.new( 'sqlite' )
    db.execute( %{
      CREATE TABLE foo 
      (key varchar(100) PRIMARY KEY, 
      value varchar(1000), 
      modified timestamp(20))
    } )
  end
  def self.get(key)
    db = SQLite3::Database.new( 'sqlite' )
    db.get_first_row( %{
      SELECT * FROM foo WHERE key='#{key}'
    } )
  end
  def self.set(key, val)
    db = SQLite3::Database.new( 'sqlite' )
    result = db.execute( %{
      REPLACE INTO foo
      (key, value, modified) 
      VALUES ('%s', '%s', %d)
    } % [key, val, Time.now.to_i] )
  end
end

usage:

require 'rubygems'
require 'sqlite3'
require 'json'
...
# init table once (anyone have a table detection query?)
Storage.init
...
# put data in
value = {:foo => 'bar'}
Storage.set( '123', value.to_json )
...
# get data out
row = Storage.get( '123' )
...

Ruby YQL utility function example

I can’t tell whether it’s YQL, Ruby, or my lil’ YQL utility function, but I’m having fun. Here’s an example of usage:

json = yql(%{
  use 'http://www.datatables.org/github/github.repo.xml' as github;
  select * from github where id = 'yql' and repo = 'yql-tables'
})

Dig the multiline string syntax (inspiration: benschwarz’s Smoke gem). YQL allows POST requests for select statements (to work around URL-length limits), so I can continue to use POST for everything 🙂

1st attempt at a Ruby YQL utility function

Motivation:
I use YQL a lot and I find myself writing query = “select * from …”, passing the query to the YQL webservice, and then JSON-parsing the response, repeat … I searched (briefly) for a Ruby gem for YQL, but couldn’t find one, so I made (a very basic) function to perform the actions listed above.

Usage:
Drop the code below in a file and run it

Code:

require 'net/http'
require 'rubygems'
require 'json'

def yql(query)
  uri = "http://query.yahooapis.com/v1/public/yql"

  # everything's requested via POST, which is all I needed when I wrote this
  # likewise, everything coming back is json encoded
  response = Net::HTTP.post_form( URI.parse( uri ), {
    'q' => query,
    'format' => 'json'
  } )

  json = JSON.parse( response.body )
  return json
end

WebDAV pseudo-PUT request code w/ YQL

My previous post presented the YQL code required to handle a WebDAV GET request for a “file” in yql storage. To update the file, we need an additional table.

Prerequisites:

  • A sherpa record w/ this value in it: {“file1″:”content 1″, “file2″, “content 2″}. The keys and values w/in the JSON can be anything. If you haven’t worked w/ YQL storage before, check out the documentation.
  • The code below edited to use your storage record’s select address

Flow:

  • The WebDAV client issues a PUT/POST request with “path” set to the key of the value to change, and “contents” set to the updated value.
  • The YQL table will extract the stored record, decode it, update the value, encode it, and store it back

Code:

<?xml version="1.0" encoding="UTF-8"?>
<table xmlns="http://query.yahooapis.com/v1/schema/table.xsd">
    <meta>
        <author>Erik Eldridge</author>
        <description>
        </description>
        <sampleQuery></sampleQuery>
    </meta>
    <bindings>
        <insert produces="XML">
            <inputs>
                <value id="path" type="xs:string" paramType="variable"/>
                <value id="contents" type="xs:string" paramType="variable"/>

            </inputs>
            <execute><![CDATA[
                var execute = 'store://{your store val}',
                    select = 'store://{your store val}',
                    update = 'store://{your store val}';
                
                // http://www.json.org/json2.js
                y.include('http://{your domain}/json2.js');
                    
                //credit: http://javascript.crockford.com/remedial.html
                if (typeof String.prototype.supplant !== 'function') {
                    String.prototype.supplant = function (o) { 
                        return this.replace(/{([^{}]*)}/g, 
                            function (a, b) {  
                                var r = o[b];
                                return typeof r === 'string' ? r : a; 
                            }); 
                    };
                }
                
                response.object = function () {

                    //put queries and results in arrays so we can reuse the var w/o overwriting values
                    var queries = [],
                        results = [];

                    queries[0] = 'select * from yql.storage where name="{select}"'.supplant({'select':select}),
                    results[0] = y.xmlToJson( y.query( queries[0] ).results );
                    
                    if (results[0].results.result.value[path]) {
                        
                        results[0].results.result.value[path] = contents;
                        
                        queries[1] = "update yql.storage set value='{json}' where name='{update}'"
                            .supplant({
                                'json' : JSON.stringify(results[0].results.result.value),
                                'update' : update
                            });
                        results[1] = y.xmlToJson( y.query( queries[1] ).results );

                        return {
                           "headers" : {
                               "HTTP/1.1 status" : "204",
                               "Date" : new Date().getTime(),
                               "Location" : "/webdav/" + path,
                               "Content-Length" : contents.length,
                               "Connection" : "close",
                               "Content-Type" : "text/plain; charset=UTF-8"
                           }
                        }
                    }

                    results[0].results.result.value[path] = contents;

                    queries[1] = "update yql.storage set value='{json}' where name='{update}'"
                        .supplant({
                            'json' : JSON.stringify(results[0].results.result.value),
                            'update' : update
                        });
                    results[1] = y.xmlToJson( y.query( queries[1] ).results );

                    return {
                        headers : {
                            "HTTP/1.1 status" : "201",
                            "Date" : new Date().getTime(),
                            "Location" : '/webdav/' + path,
                            "Content-Length" : contents.length,
                            "Connection" : "close",
                            "Content-Type" : "text/plain; charset=UTF-8"
                        }
                    };
                }();
            ]]></execute>
        </insert>
    </bindings>
</table>

Notes:

  • We could use some ruby like this in a rack app to intermediate between YQL and the WebDAV client:
        request = Rack::Request.new(env)
        
        contents = '';
        while part = request.body.read(8192)
          contents += part
        end
        
        query = "use '{the uri for the YQL table file using the code above}/put.xml' as table;"+
          "insert into table (path, contents) values ('#{file}', '#{contents}')"
        host = 'http://query.yahooapis.com'
        path = '/v1/public/yql'
        
        response = Net::HTTP.post_form( URI.parse( "#{host}#{path}" ), {
          'q' => query,
          'debug' => true
        } )
        doc = REXML::Document.new(response.body)
        headers = REXML::XPath.first( doc, "///headers" )
        # you could dump out the headers here
        # p(headers[0].text)
        
        # return an empty success response
        [204, {}, '']