Browser Stats from gs.StatsCounter.com

June 1, 2010

Source: StatCounter Global Stats – Browser Version Market Share

Filed under: Browser Wars — Tags: , , , , , , — Mike Clagg @ 9:53 AM
Comments (0)

jQuery RESTful forms

May 5, 2010

jQuery has shortcut interfaces to their ajax functions for GET and POST request methods. The other two RESTful verbs can be called using the ajax functions directly, or you can create your own shortcuts.

I feel that using the shortcuts gives a supporting developer a better understanding of the flow, and how the front end communicates to the web services.


<script type="text/javascript">
function _ajax_request(url, data, callback, type, method) {
    if (jQuery.isFunction(data)) {
        callback = data;
        data = {};
    }
    return jQuery.ajax({
        type: method,
        url: url,
        data: data,
        success: callback,
        dataType: type
    });
}
jQuery.extend({
    put: function(url, data, callback, type) {
        return _ajax_request(url, data, callback, type, 'PUT');
    },
    del: function(url, data, callback, type) {
        return _ajax_request(url, data, callback, type, 'DELETE');
    }
});
</script>

So now you have your shortcuts established, we need to setup a form.

</p>
<form action="/service/user/">
<input type="text" name="name" value="" />
<input type="text" name="email" value="" />
<input type="text" name="twitter_acct" value="" />
<input id="gender[]" type="radio" name="gender" value="m" />
<input id="gender[]" type="radio" name="gender" value="f" />
<input id="ui-form-submit-ajax-method-put" type="submit" name="ui-admin-add-user" value=" Create User &gt; " />
</form>
<p>

The form’s action attribute should store the relative url of your web service, or a proxy path, after all this is a vanilla ajax pattern.

We start with the document.ready (jQuery’s onreadystate function).
Disable all forms default behvior. Of course you can pick and choose to your liking which forms get this.

Then we add an “click” event trigger to all input[type=submit]. If you look at the HTML above you will see the method is in the id. A dash (-) separated namespace with the matching method to our shortcuts for jQuery.


jQuery(document).ready(function () {
     jQuery("form").submit(function (event) { event.preventDefault(); });
     jQuery("input[type=submit]").click(function () {
          var method,wsUri,requestData;
          requestData = {};
          method = jQuery(this).attr("id").toString().split("-").pop();
          wsUri = jQuery(this).parents('form:first').attr("action");
          jQuery(this).parents('form:first').find("input, select, textarea").each(function () {
               var inputType, dataName, dataVal;
               inputType = jQuery(this).attr("type");
               if (inputType === "text" || inputType === "checkbox" || inputType === "radio") {
                    dataName = jQuery(this).attr("name");
                    dataVal = jQuery(this).val();
                    if (dataVal != "") {
                         requestData[dataName] = dataVal;
                    }
               }
               inputType,dataName,dataVal = null;
          });
     jQuery[method](wsUri,requestData,function (data, textStatus) {
          switch (data.responses[0].status.request_method) {
               case "GET":
               break;
               case "PUT":
               break;
               case "POST":
               break;
               case "DELETE":
               break;
          }
     }, 'json');
  });
});

This is currently used with a simple web service patten in PHP that is easy to implement.

PHP Simple Web Service Pattern

.htaccess for pretty urls (apache and lighthttpd)

apache:



Options +FollowSymLinks
RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^.*$ ./index.php

lighttpd:



url.rewrite-once = (
"/(.*)\.(.*)" => "$0",
"/(css|files|img|js|stats)/" => "$0",
"^/([^.]+)$" => "/index.php/$1"
)

Drop your .htaccess into the root of where you want your “app” to live.

Now your index.php is a very light controller, and should be fitted with more error checking and reporting, but you are capable of those details.

This script will consider everything after the base url of the script as parameters for the script.

My file structure is pretty simiple:


./
    .htaccess
    index.php
    /assets
        /css
        /js
        /php
        /img
        /views
            home.phtml
            service.phtml

<?php

$request = str_replace ( array( str_replace("index.php","",$_SERVER['PHP_SELF']) , "?term=" ), "", $_SERVER['REQUEST_URI'] );

$params = split("/", $request);

$viewPath =  "./views/";

$viewExt = ".phtml";

/* redirect if not enough parameters are sent, quick work around that should have real error reporting  */
if ( count($params) < 3 ) header ( "Location: ../index.html" );

$file = $viewPath . $params[1] . $viewExt;

if ( file_exists($file) ) include_once ($file);

?>

We need a few classes for the example to work:

./assets/php/lib/class.ws_response.php :

<br />
<?php<br />
class ws_response<br />
{<br />
    var $response;<br />
    public function __construct()<br />
    {<br />
        $this->response->{'responses'} = array();<br />
    }<br />
    public function addResponse($responseMap)<br />
    {<br />
        foreach ($responseMap as $responseKey => $responseVal)<br />
         {<br />
             $response->{$responseKey} = $responseVal;<br />
         }<br />
         array_push($this->response->responses,$response);<br />
    }<br />
    public function getResponseAsJSON()<br />
    {<br />
       return json_encode($this->response);<br />
    }<br />
}<br />
?><br />

./assets/php/lib/class.ws_status.php :


<?php
class ws_status
{
    var $statuses;
    public function ws_status()
    {
    }
    public static function addStatus($code, $message, $request_method, $type)
    {
        $status->{'code'} = $code;
        $status->{'message'} = $message;
        $status->{'request_method'} = $request_method;
        $status->{'type'} = $type;
        $status->{'http_headers'} = array();
        return $status;
    }
    public function getStatus()
    {
        return $this->statuses;
    }
    public function getStatusAsJson()
    {
        return json_encode($this->statuses);
    }
}
?>

Now let's look at service.phtml


<?php
set_include_path(implode(PATH_SEPARATOR, array(realpath(dirname(__FILE__).'/../../assets/php/lib'),get_include_path(),)));
require_once("class.database.mysql.php");
require_once("class.ws_response.php");
require_once("class.ws_status.php");
/* this is my simple web service response wrapper, it needs work */
$database = new database();
$response = new ws_response();
switch ($params[1])
{
    case 'itemById':
        switch ($_SERVER['REQUEST_METHOD'])
        {
            case 'GET':
                try
                {
                    $sqlSet = "SELECT `items`.* FROM `items`" . ((count($params)>2 &#038;&#038; !empty($params[2])) ? " WHERE `items_dev`.`id` = ".$params[2]:"");
                    $item = $database->make_query($sqlSet);
                    $item = count($item) === 1 ? array_shift($item):$item;
                    $response->addResponse(array("status" => ws_status::addStatus(200,'success', $_SERVER['REQUEST_METHOD'],'screen'),$params[1] => $item));
                }
                catch(Exception $e)
                {
                   $response->addResponse(array("status" => ws_status::addStatus(500,'error: ' . $e->getMessage() , $_SERVER['REQUEST_METHOD'],'screen'),$params[1] => NULL));
                }
                echo $response->getResponseAsJSON();
            break;
            case 'POST':
                $len = count($_POST);
                $i = 0;
                $sqlSet = "UPDATE `items` SET ";
                foreach ($_POST as $name => $value)
                {
                    if ($name != "id")
                    {
                        if ($i>0) $sqlSet.= ", ";
                        $sqlSet.= "`".$name."` = ";
                        $sqlSet.= (preg_match("/^\d/",$value)) ? mysql_escape_string($value):"'".mysql_escape_string($value)."'";
                    }
                    $i++;
                }
                $sqlSet.= " WHERE `items_dev`.`id` = " . $params[2] . " ;";
                try
                {
                    if($database->make_query($sqlSet))
                    {
                        $response->addResponse(array("status" => ws_status::addStatus(200,'success', $_SERVER['REQUEST_METHOD'],'screen'),$params[1] => "Item " . $params[2] . " has been updated."));
                    }
                    else
                    {
                        $response->addResponse(array("status" => ws_status::addStatus(200,'success', $_SERVER['REQUEST_METHOD'],'screen'),$params[1] => "Item " . $params[2] . " was not updated."));
                    }
                }
                catch(Exception $e)
                {
                   $response->addResponse(array("status" => ws_status::addStatus(500,'error: ' . $e->getMessage() , $_SERVER['REQUEST_METHOD'],'screen'),$params[1] => NULL));
                }
                echo $response->getResponseAsJSON();
            break;
            case 'PUT':
                /* ..code.. */
            break;
            case 'DELETE':
                /* ..code.. */
            break;
        }
        break;
        case 'service2':
            switch($_SERVER['REQUEST_METHOD'])
            {
                case 'DELETE':
                    /* ..code.. */
                break;
                case 'DELETE':
                    /* ..code.. */
                break;
                case 'PUT':
                    /* ..code.. */
                break;
                case 'DELETE':
                    /* ..code.. */
                break;
        }
        break;
}
?>

That is the basics, as you can see the REQUEST METHOD is sent back in the status message, along with other useful information.

I welcome any feedback that improves on the basics here.

Filed under: jQuery,JSON,PHP,REST,web service — Mike Clagg @ 3:06 PM
Comments (0)

Lorem Ipsum Tattoo

April 6, 2010
Lorem Ipsum Tattoo

Lorem Ipsum Tattoo

Filed under: Awesomeness — Mike Clagg @ 7:25 AM
Comments (0)

SxSWi Day One

March 12, 2010
my sxsw badge

my sxsw badge

Just got checked in and am sitting in the room where Battle Deck is being held.  Battle Deck is a karaoke style kamikaze presentation where your group gets up and has to improve a presentation to random slides.   The slides are heavy on the anti-glen beck and vaginas.  The slides are way better than the actual speakers.  Best joke heard, “We need to get all the lighters back from Homeland Security and melt all the plastic people into the oil that America needs.”

These were the cont3stants…

@aedison

@aedison

@jenbee

@jenbee

@communicatrix

@communicatrix










@sacca

@sacca

@seoulbrother

@seoulbrother

@zeldman

@zeldman







And the winner was Josh A Cagan

@joshacagan

@joshacagan

Filed under: SxSWi — Mike Clagg @ 4:26 PM
Comments (0)

Robusta Coffee

March 11, 2010

After learning and laughing along with one of my favorite web comics – TheOatmeal.com’s comic 15 Things Worth Knowing About Coffee, I was inspired to search for and drink robusta coffee.

Coffee Beans

There are 2 kinds of coffee beans, arabica and robusta

So I found some green non-roasted beans online and set out to not only try robusta (50% more caffeine than arabica), but to try my hand at roasting at home.  Basically the roasting was a whole lot less than rocket surgery.  Also I would like to thank my girlfriend, Annilee Ballentine for perfecting the roasting process with several more runs.

Part 1 – The Roast

Take your green beans, which cost half of what roasted beans cost, and place a quarter to half a cup onto a baking sheet.  We found that an aerated pizza pan worked really well,  but not that much better than a restaurant grade stamped aluminum sheet.  Preheat your oven at 475 degrees, and then place the cooking tray of choice into the oven.   Roast the beans at a high temperature for about 15 minutes, then drop the heat down to 375 degrees and roast until you reach the desired color.
This is basically the same process for rice, but you roast much longer.  I think for our dark dark black beans, they sat in the oven for 4-6 hours.

Now selecting and roasting your own beans are only half the battle we found.

Coffee Grinder

If you love coffee, invest money into a grinder.

Part 2 – The Grind

We found that for the dark robusta in a either a press or a drip that finer than the normal suggested grind brought out a much richer, deeper flavor in the coffee.  And this goes for most beans.  If you have a grinder with specific settings for course, medium, and fine grinds, that playing around with in between settings may actually bring out more character to your coffee.

Part 3 – The Cup

As for the actually drinking of the robusta coffee, let me tell you that it is not for caffeine lightweights.  Comments at work from, my eyes are twitching, to I am still up from yesterday, have followed a few pots that have been allowed to be brewed.  So we have fallen back to blends, which is another post in itself.

Green Bean Resources

Well thanks for checking out my blog, which I think I finally have setup just in time to go to SXSW Interactive tomorrow.  See you geeks there.

Filed under: Random Geek Task — Tags: , , — Mike Clagg @ 6:30 PM
Comments (0)
Rss Feed Tweeter button Facebook button Technorati button Reddit button Webonews button Delicious button Digg button Stumbleupon button Newsvine button Youtube button