Skip to content
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
6297 lines (4322 sloc) 251 KB
Some months ago I added book reviews from Amazon to [My Reading
List]( Under Reviews, when you click "Amazon" you
get an overlay with the 3 most useful reviews. In this post I show you how to
do it.
### Amazon ECS
![featured image](
I use Amazon all the time to look if a particular book is worth my time. Hence
book reviews had to be in this app. I found a good PHP class to do this
easily: [Amazon-ECS-PHP-Library](
Library). Using this class the app generates an URL to an Amazon page with
only the reviews on it (e.g., based on its
ISBN number. This URL is linked to under Reviews: "Amazon". When clicked, I
use Fancybox (discussed [previously](
reading-list-google-book-preview-overlay/)) to load the URL in an overlay
(iframe). See the images below.
### Inputs: ISBN (10 / 13 digits) and ASIN
Note that My Reading List uses the [Google Books
API]( which returns short and long ISBN
numbers (10 and 13 digits respectively, [about
ISBN]( The
10 digit string is usually equal to the
which is the "Amazon Standard Identification Number". If there is no response
from the short ISBN/ ASIN, I convert the longer ISBN to ASIN and try with that
one. I had to add the "lookupIsbn" method to the Amazon ECS class to be able
to do this. With the two ISBN types supported I got a response URL for almost
all the books I have tested so far.
### Caching
I was considering caching the Amazon iframe URLs, but they are only valid for
24 hours so I decided to generate them on the fly for now.
### How it looks
This is the [Practical Vim](
#### Link to reviews:
![link on book page](
#### Overlay when clicked:
![reviews in overlay](
### The code
# see comment above about 10 and 13 digit ISBNs
if($reviews = getAmazonReviews($book['ISBN_10'])) {
$amazonReviewsIframe = $reviews;
} else {
$asin = isbnToAsin($book['ISBN_13']);
$amazonReviewsIframe = getAmazonReviews($asin);
if($amazonReviewsIframe) {
echo "<a class='fboxEdit' href='$amazonReviewsIframe'>Amazon</a>";
# get the first two values from the Product Advertising API,
# last 2 values are needed for Amazon-ECS-PHP-Library
define("COUNTRY_CODE", "com");
define("ASSOC_TAG", "myrealis");
function amazonApi() {
require_once 'AmazonECS.class.php';
return $client;
function getAmazonReviews($asin) {
$client = amazonApi();
$response = $client->category('Books')->responseGroup('Reviews')->search($asin);
if($response->Items->Item->ASIN == $asin) {
return $response->Items->Item->CustomerReviews->IFrameURL;
} else {
return False;
function isbnToAsin($isbn){
$client = amazonApi();
# I extended the AmazonECS.class with the "lookupIsbn" function
$response = $client->category('Books')->lookupIsbn($isbn);
if(isset($response->Items->Item->ASIN)) {
return $response->Items->Item->ASIN;
} else {
return False;
# added inside the AmazonECS.class
public function lookupIsbn($isbn) {
$params = $this->buildRequestParams('ItemLookup', array(
'ItemId' => $isbn, 'IdType' => 'ISBN', 'SearchIndex' => 'Books'
return $this->returnData(
$this->performSoapRequest("ItemLookup", $params)
In this posts some notes on installing Ruby and RubyGems on Bluehost, my
hosting provider. While I was writing this I also tried it on Solaris 11 and
Ubuntu 12.10 as a regular (non-root) user. So now I can deploy the latest
version of Ruby to most places I push code to.
Of course this only makes sense if you want to have the latest version or if
Ruby is not installed at all and you have no sudo / root rights. For Ubuntu I
usually have full access so running an "$ apt-get install ruby" would be all
it takes. For Bluehost I obviously don't have root access, for Solaris usually
the same. Bluehost does come with gem 1.7.2 and ruby 1.8.7 out of the box.
Learning Ruby I want to make sure though I can use the latest version on my
local- and remote hosts, hence this post.
### Installation steps
* First get the latest version:
$ mkdir ~/ruby && cd ~/ruby
$ wget
(at this time)
$ bzcat ruby-1.9.3-p286.tar.bz2 | tar -xvf -
$ cd ruby-1.9.3-p286
* Install it (see also [this article](
$ ./configure -prefix=$HOME
$ make
$ make install
!! On Solaris 11 you might need to [install
solaris-11) ($ sudo pkg install gcc-3) first.
!! Although this worked perfectly as a user on Ubuntu and Solaris, on Bluehost
this failed. On a hosting box it might be better to run the following commands
which I found [here](
(still in ruby-1.9.3-p286)
$ autoconf
$ nice ./configure --enable-shared --prefix=$HOME/ruby/
$ nice make
$ make install
![featured image ruby rubygems install](
* Next make sure you update your $PATH variable in your shell's dotfile with the new Ruby binary (in my case .bashrc).
If this is your first Ruby installation you can just append it to the end, if
you have multiple instances, like I had on Bluehost, you might prepend it so
that the shell looks first at the Ruby binary you installed with your user
account. For dealing with multiple Ruby versions on your box, you might want
to check out [Ruby Version Manager]( as well.
$ vi ~/.bashrc
export PATH=$HOME/ruby/bin:$PATH
If you don't want to mess with the PATH variable, you can also create aliases:
$ vi ~/.bashrc
alias ruby19='$HOME/ruby/bin/ruby'
alias gem18='$HOME/ruby/bin/gem'
* Next install RubyGems
$ wget
(at this time)
$ zcat rubygems-1.8.24.tgz | tar -xvf -
(gzcat on Solaris)
$ cd rubygems-1.8.24
$ ruby setup.rb
(where ruby is $home/ruby/bin/ruby due to the PATH change in the previous step)
<my home>/bin/ruby/lib/ruby/1.9.1/yaml.rb:56:in `<top (required)>':
It seems your ruby installation is missing psych (for YAML output).
To eliminate this warning, please install libyaml and reinstall your ruby.
RubyGems 1.8.24 installed
== 1.8.24 / 2012-04-27
* 1 bug fix:
* Install the .pem files properly. Fixes #320
* Remove OpenSSL dependency from the http code path
RubyGems installed the following executables:
<my home>/ruby/bin/gem
The libyaml error didn't get in the way so far, but usually it is better to
solve any errors I got this error on each Unix flavor (as a user).
* Bluehost recommends [the following GEM variable settings](, again in your shell's dotfile:
export GEM_HOME=$HOME/ruby/gems
export GEM_PATH=$GEM_HOME:/usr/lib/ruby/gems/1.8
export GEM_CACHE=$GEM_HOME/cache
(and if you will use rails)
ENV['GEM_PATH'] = '/path/to/your/home/ruby/gems:/usr/lib/ruby/gems/1.8'
/usr/lib/ruby/gems/1.8 is the default gems location, if this is the first
installation just leave this off.
* To test if it works I install two gems:
$ gem install RedCloth
$ gem install hpricot
$ gem list --local
*** LOCAL GEMS ***
hpricot (0.8.6)
RedCloth (4.2.9)
$ ls $HOME/ruby/gems/gems
hpricot-0.8.6 RedCloth-4.2.9
!! Note that on Ubuntu you might get the following error which means you have
to install zlib ($ sudo apt-get install zlib1g-dev) :
ERROR: Loading command: install (LoadError)
cannot load such file -- zlib
* Testing the new gem:
$ irb
irb(main):001:0> require "rubygems"
=> true
irb(main):002:0> require "RedCloth"
=> true
irb(main):003:0> r ="this is a *test* of _using RedCloth_")
=> "this is a *test* of _using RedCloth_"
irb(main):004:0> puts r.to_html
<p>this is a <strong>test</strong> of <em>using RedCloth</em></p>
=> nil
And that's it. Now I can write Ruby 1.9.x scripts knowing that they will work
on my remote servers because I will simply install the latest Ruby version as
my user.
In this post I will show how you can dynamically load
[omdbapi]( data into your page. This example only uses the
rating info because all other movie data I get already from [themoviedb
API]( I use this feature on
### How it works
![featured image](
I was clicking a lot on the IMDB link on movie pages (example [movie
page]( Mostly because the vote
count is much higher than TMDB's so it is a good extra reference to see if a
movie is worth watching. The code in this post shows you how to load in this
type of data, if you use PHP for your own movie site project, you can easily
re-use these examples.
The strategy is very similar to [the autocomplete in the last
multifunctional-autocomplete/). We ask Javascript (jQuery in this case) to
watch for a click on an anchor with class "imdbRating". Upon a click on this
element, we replace the parent "span" element with a loader gif image, while
calling the omdbapi in the background. When this query comes back with the
result, we replace the loader image with it.
### The actual coding
* The html that sets up a span and anchor within it with a class and a unique id. The imdb movie is hardcoded for this example, but in the [real app]( it is dynamically generated from the database (cached movie) or themoviedb API (new movie):
<span><a class="imdbRating" href="#" id="tt0068646">IMDB</a></span>
* The Javascript that handles the click on the link (include the jQuery library first):
$(document).ready(function() {
var ratingLink = $(this);
var ratingDiv = $(this).parent();
var imdbTitle = ratingLink.attr("id");
ratingDiv.html("<img src='i/mini-loader.gif'>");
$.post("get_imdb_voting.php", { title: imdbTitle }, function(data){
return false;
This watches for the imdbRating class to be clicked and before finishing it
returns false to not actually follow the link. It defines the ratingDiv as the
parent of the anchor, in this case the wrapped "span" I showed in the html
code. It populates this DOM element with the gif loader to show the user we
are making progress. It then does a post (ajax) request to the PHP that is
going to query the omdbapi. When it does the defined readingDiv (span) will be
populated with the result of this, loading it in with a fadeIn jQuery
* The get_imdb_voting.php script:
if(!isset($_POST['title'])) return -1;
$title = $_POST['title'];
$imdbVoting = getImdbVotes($title);
if($imdbVoting && $imdbVoting['rating'] != '' && $imdbVoting['votes'] != '') {
echo $imdbVoting['rating'] . ' (' . $imdbVoting['votes'] ;
echo ' <a href="'.$title.'" target="_blank">IMDB</a> votes)';
function getImdbVotes($title){
$imdbApiUrl = "$title"; # title like tt0103759
$results = useCurl($imdbApiUrl);
$results = json_decode($results);
$imdbVoting = array();
$imdbVoting['rating'] = $results->imdbRating;
$imdbVoting['votes'] = $results->imdbVotes;
return $imdbVoting;
function useCurl($url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FAILONERROR, 1);
$results = curl_exec($ch);
$headers = curl_getinfo($ch);
$error_number = curl_errno($ch);
$error_message = curl_error($ch);
return $results;
This is pretty straight-forward, it uses curl to query
[]( for the title it receives. It uses
json_decode to parse the results. At this time I am only interested in the
rating and voting which I print out if present. I could do some error
handling, but for now I just return no html if nothing comes back. Also
omdbapi returns "N/A" if it doesn't find a result which is ok for me.
And that's all that is needed to show omdbapi data dynamically on your page.
If you want to use more data of the return result just study the structure of
the data, for example:
and/or use a "print_r($results);" after the "$results =
json_decode($results);" statement to see in Firebug what the post call comes
back with.
### See this in action
See the process in the featured image of this post or in the following
![matrix movie page](
![loader when clicking the imdb link](
![with the results from imdbapi](
Today two posts about enhancements in
[]( In this post I will show you how to
make a multifunctional autocomplete box. In the next article I show you how to
dynamically load in IMDB vote results for a particular movie.
Autocompletes are around for a long time, but I still love them for my apps.
It makes the app flow faster and navigation can be much shorter not cluttering
the page.
I implemented a multi-use autocomplete [some time
features/) for [FB Reading List](
![fbreadinglist autocomplete](
I will show you a similar approach I took for
### How it works
When you search for "be" it finds movies for that string:
![searching for be](
But if you prepend the search term with the "@" sign, for example "@be" it
starts to find persons (actors/directors) and users in the same
![searching for person be](
### Implementation
This is an extension to the movie search interface with instant trailers I
blogged about [last year](
Autocomplete plugins work with AJAX: javascript calls a serverside script (PHP
in this case) which returns its result in a certain format (JSON in this
instance). Javascript digests this result and sticks it into the DOM.
The backend script (search.php) looks like:
if (!isset($_GET["term"])) {
include 'libs/functions.php';
$term = $_GET["term"];
$orgTerm = $term;
$lang = 'en'; # for now
$return_arr = array();
# limit size of autocomplete window
$maxNumItems = array();
$maxNumItems['movies'] = 5; # 5 items for movies because the content is big
$maxNumItems['persons'] = 4; # persons can add up to 7, 4 for api, 3 for users
$maxNumItems['users'] = 3;
$counter['movies'] = 0;
$counter['persons'] = 0;
$counter['users'] = 0;
# formatting in JS:
# searches starting with @ search for persons (actors / directors from the API) or sharemovies users
if(preg_match('/^@/i', $term)){
$term = preg_replace('/^@\s*/i','',$term);
$term = urlencode($term);
# 1. persons
$results = queryApi3('','','search/person', $term, '', $lang);
if(! empty($results->results)) {
foreach($results->results as $person) {
if ($person->adult == true || $person->profile_path == '') continue;
$row_array['type'] = "person";
$row_array['id'] = $person->id;
$row_array['value'] = $person->name;
$row_array['img'] = $POSTER_SMALL . $person->profile_path;
$row_array['slug'] = createSlug('person',$person->id,$person->name);
if($counter['persons'] == $maxNumItems['persons']) {
# 2. users in db
include "libs/db_connect.php";
$term = $mysqli->real_escape_string($term);
$search_term = str_replace(array("+"), " ", $term);
$q = "SELECT,,shmov_users.ins as joined,count( as total ";
$q .= "FROM `shmov_users_movies` join shmov_users on ";
$q .= "WHERE LCASE( like '%$search_term%' ";
$q .= "group by order by total desc";
$r = $mysqli->query($q);
if($r->num_rows) {
while($row = $r->fetch_object()){
$row_array['type'] = "user";
$row_array['id'] = $row->id;
$row_array['value'] = $row->name . " (user)";
$row_array['total'] = "<small>" . $row->total . " movie" ;
$row_array['total'] .= ($row->total != 1)? "s": "" ;
$row_array['total'] .= " on watchlists";
$row_array['joined'] = " / Joined " . timeAgo($row->joined) . "</small>";
$row_array['img'] = "".$row->id."/picture?width=40&height=40";
$row_array['slug'] = "user/".$row->id;
if($counter['users'] == $maxNumItems['users']) {
# if nobody found return error message (see JS: it expects a message to start with Oops)
array_push($return_arr, "Oops ... nobody found for $orgTerm");
} else {
# else = movie title search
$term = urlencode($term);
$results = queryApi3('','','search/movie', $term, '', $lang);
if(empty($results->results)) {
# if nobody found return error message (see JS: it expects a message to start with Oops)
array_push($return_arr, "Oops ... no movies found for $orgTerm");
} else {
foreach($results->results as $movie) {
$row_array['type'] = "movie";
$row_array['id'] = $movie->id;
$row_array['value'] = ucwords($movie->title);
if($movie->original_title && ($movie->title != $movie->original_title)) {
$row_array['org'] = ucwords($movie->original_title);
} else {
$row_array['org'] = '';
$row_array['pop'] = $movie->popularity;
$row_array['rating'] = $movie->vote_average . " (". $movie->vote_count." votes)";
$row_array['plot'] = get_n_words($movie->overview, 15) . " ..." ;
$row_array['year'] = substr($movie->release_date, 0, 4);
$row_array['img'] = $POSTER_SMALL . $movie->poster_path;
$row_array['slug'] = createSlug('movie',$movie->id,$movie->title);
if($counter['movies'] == $maxNumItems['movies']) {
echo json_encode($return_arr);
![featured image](
* The search term is sent from JS, if it starts with @ it performs a search against [themoviedb]( and against the users in the sharemovies database. If no @ is prepended to the search, it does a regular movie search, again against themoviedb API. I could cache the results but for now the search box use seems within the themoviedb limits (I do cache movie and person pages though which reduces the amount of calls to the API significantly).
* The $maxNumItems array restricts the number of items in the returned result so that the autocomplete result box fits reasonably well within the page.
* The json_encode is run on the array of row arrays for JS consumption.
* queryApi3 is a routine to query themoviedb API, I omitted it for brevity, interesting in more details, just drop me a line
The Javascript code to digest the result (after including the jQuery library):
$(document).ready(function() {
// prevent autocomplete field from being submitted on enter
// actually ace's solution worked here:
var keycode = (event.keyCode ? event.keyCode : event.which);
if (keycode == '13') {
search: function(event, ui) {
open: function(event, ui) {
source: "search.php",
minLength: 2,
select: function(event, ui) {
var currentUrl = document.URL;
var baseUrl;
if( currentUrl.match(/sharemovi\.es/) ){
baseUrl = "";
} else {
baseUrl = "";
var redirectTo = baseUrl + ui.item.slug;
}).data( "autocomplete" )._renderItem = function( ul, item ) {
if(item.value.match(/^Oops/)) {
return $( "<h2 class='notfound'></h2>" )
.data( "item.autocomplete", item )
.append( "<p>"+item.value+"</p>" )
.appendTo( ul );
} else {
var movieInfo = '';
movieInfo += "<a id="">";
movieInfo += "<img src='" + item.img + "' />";
movieInfo += "<span><h4>" + item.value+ "</h4>";
if(item.type == "user"){
movieInfo +=;
movieInfo += item.joined;
if(item.type == "movie"){
movieInfo += "<small><p>";
if( movieInfo += "Original Title: ""<br>";
movieInfo += "Release Year: "+item.year;
movieInfo += "<br>TMDB Rating: "+item.rating;
movieInfo += "<br><small>Popularity: "+item.pop;
movieInfo += "</small></p>";
movieInfo += "<div id='"+item.value+' '+item.year;
movieInfo += "' class='trailer'>";
movieInfo += "<img src='i/youtube.png'>";
movieInfo += "</div></small>";
movieInfo += "</span></a>";
$( "<li></li>" )
.data( "item.autocomplete", item )
.append( movieInfo )
.appendTo( ul );
// if search is for person or user, the autocomplete box needs to be smaller
if(item.type == "user" || item.type == "person") {
$(".ui-autocomplete li").css("height", "50px");
$(".ui-autocomplete li a").css("height", "50px");
$(".ui-autocomplete li a img").css({"height": "40px", "width": "40px"});
$(".ui-autocomplete li a span").css("left", "50px");
return false;
// more sharemovies JS code ..
* source: "search.php" => here you see the backend PHP script being called.
* .data( "autocomplete" )._renderItem => this consumes the elements returned by the PHP backend script. It wraps it in convenient HTML I styled with CSS (which exact styling is beyond the scope of this tutorial).
* Note in the last part I overwrote some of the CSS rules of the autocomplete jQuery plugin to present smaller entries for person vs. movie results (see printscreens at the start of this post). The movie results give back more verbose data. So now each result takes up just the necessary space.
* Some things are hardcoded like the domain name and the handling of no results ("item.value.match(/^Oops/)") but that is fine for now.
At last the form html that has the DOM element Javascript acts upon:
<form action="#" id= "searchForm" method="post">
<p class="ui-widget">
<input type="text" id="autocomplete" name="autocomplete" class="defaultText"
title="Search movie ... [ use '@' for actors/directors/sharemovies users ] "/>
This tutorial gives you a 90% jumpstart how to implement a rich multi-
functional autocomplete. I hope it inspires you to build something similar.
Also let me know if you have implemented a similar feature in any other
Cannot convert this post's html to plain text
#code #cpan #getubuntu #git #github #google #hack #interactivepython #language
#mac #mountainlion #perl #programming #python #rebase #ruby #vim
> 5 Ways To Learn Code From The Comfort Of Your Own Browser
[]( via
> Bob Belderbos (@bbelderbos) [October 31,
> [#ruby]( beginner's guide by smashing
> Bob Belderbos (@bbelderbos) [October 31,
> The Best [#Ruby]( Books
> Bob Belderbos (@bbelderbos) [October 31,
> Perl vs. PHP vs. Ruby []( via
> Bob Belderbos (@bbelderbos) [October 30,
> [#language]( hacking - cool app / idea
> Bob Belderbos (@bbelderbos) [October 29,
> cool learn [#ruby]( interactively
> Bob Belderbos (@bbelderbos) [October 28,
> RT [@Harjeet:]( most important thing you can
do to improve your [@ycombinator](
application is reading [@paulg's]('s) application
guide first: http
> Bob Belderbos (@bbelderbos) [October 25,
> Ubuntu 12.10 is out now. Download a better operating system today.
> Bob Belderbos (@bbelderbos) [October 19,
> I signed up for An Introduction to Interactive Programming in
[#interactivepython]( - a free
[@coursera]( class
> Bob Belderbos (@bbelderbos) [October 14,
> RT [@boagworld:]( The problem with web
design is that each site you do, you learn something new and want to revisit
all old sites.
> Bob Belderbos (@bbelderbos) [October 12,
> [#vim]( in muscle memory
> Bob Belderbos (@bbelderbos) [October 11,
> []( evaluating
[#perl]( books
> Bob Belderbos (@bbelderbos) [October 07,
> RT [@smashingmag:]( Useful: Scripting
languages PHP, Perl, Python, Ruby syntaxes compared -
> Bob Belderbos (@bbelderbos) [October 07,
> [#vim]( tabstop, softtabstop, shiftwidth and
expandtab settings well explained [](
> Bob Belderbos (@bbelderbos) [October 06,
> Nice modern [#perl]( in epub format
> Bob Belderbos (@bbelderbos) [October 06,
> Finished practical [#vim]( from
[@nelstrom]( and I have to say I edit my files
faster and in a more intelligent way!
> Bob Belderbos (@bbelderbos) [October 06,
> Seven habits of effective text editing
> Bob Belderbos (@bbelderbos) [October 04,
> [#git](
[#rebase]( how to remove a commit from
github [](
> Bob Belderbos (@bbelderbos) [September 23,
> [#git]( how to undo last commit
> Bob Belderbos (@bbelderbos) [September 23,
> Useful gitconfig aliases [](
> Bob Belderbos (@bbelderbos) [September 18,
> RT [@hnycombinator:]( Stop Validating
Email Addresses With Your Complex Regexes
> Bob Belderbos (@bbelderbos) [September 16,
> Because the people who are crazy enough to think they can change the
world, are the ones who do
> Apple Inc.
> Bob Belderbos (@bbelderbos) [September 15,
> use [#perl](
[#cpan]( on
[#mac]( os working, get Xcode, some good
instructions here [](
> Bob Belderbos (@bbelderbos) [September 12,
> The Beginners Guide to Learning to Program - great post
> Bob Belderbos (@bbelderbos) [September 12,
> RT [@newsycombinator:]( The 2 Biggest
Mistakes I Made When Learning to Code
> Bob Belderbos (@bbelderbos) [September 07,
> RT [@aarron:]( Pro tip: if you want to be taken
seriously for a job in the web industry build a website that shows your work
and how you so
> Bob Belderbos (@bbelderbos) [September 07,
> RT [@BkVirendra:]( "Being honest with
myself: my journey to learning how to code"
> Bob Belderbos (@bbelderbos) [September 06,
> RT [@codinghorror:]( If open source is so
great, where are all the open source billionaires?
> Bob Belderbos (@bbelderbos) [September 06,
> RT [@newsycombinator:]( How a bedroom
developer's 'ugly little game' became an App Store hit
> Bob Belderbos (@bbelderbos) [September 05,
> RT [@BkVirendra:]( "How we keep
[#GitHub]( fast"
> Bob Belderbos (@bbelderbos) [September 05,
> Wow command line is coming, firefox 16 beta
> Bob Belderbos (@bbelderbos) [September 04,
> RT [@igrigorik:]( "An Introduction to
Programming in Go" - []( - free, and
awesome ebook!
> Bob Belderbos (@bbelderbos) [September 04,
> RT [@smashingmag:]( A great article on
customer experience: "Why Waiting Is Torture" -
> Bob Belderbos (@bbelderbos) [September 04,
> RT [@newsycombinator:]( Computer
programming will soon reach all Estonian schoolchildren
> Bob Belderbos (@bbelderbos) [September 04,
> RT [@Jiaaro:]( Python Cheat Sheet
> Bob Belderbos (@bbelderbos) [September 04,
> RT [@DZone:]( "Reading Code"
> Bob Belderbos (@bbelderbos) [September 04,
> RT [@mashable:]( Hackers Allegedly Leak 1
Million Apple Device IDs - [](
> Bob Belderbos (@bbelderbos) [September 04,
> [@BkVirendra:]( Google's "Did You Mean"
[#Hack]( in
[#google]( - this is neat
> Bob Belderbos (@bbelderbos) [September 03,
> [#mountainlion]( launch pad is
really nice, just map a keyboard short cut to it and open apps very quickly
> Bob Belderbos (@bbelderbos) [September 02,
> RT [@edorsey:]( Software Dev is the only
profession that is asked to do something which has never been done before &
tell someone else how
> Bob Belderbos (@bbelderbos) [September 01,
Powered by [tweetdigest](
At least [3 years ago](
[NetBeans]( started to support PHP. [PHP Application
Development with NetBeans](
development-with-netbeans-beginners-guide/book) gives a nice tour of how to
simplify development with this IDE.
Disclaimer: I received a copy from Packt for review.
![featured image](
I haven't seen any title so far that specifically deals with PHP development
in NetBeans, in that sense it filled a gap ([amazon
keywords=netbeans+php&rh=i%3Aaps%2Ck%3Anetbeans+php)). NetBeans has been
around for [quite some time]( now. It
was one of my favorite IDEs some years ago, but I gradually moved to
[TextMate](, then to [Sublime
Text](, and lately I exclusively use
Yes, these are text editors, which can be expanded to full IDEs. Honestly, I
haven't used a full-blown IDE in a while so this was an interesting book to
see what's supported these days. This book shows interesting examples in that
* Chapter 1: Setting up your Development Environment
* Chapter 2: Boosting Your Coding Productivity with the PHP Editor
* Chapter 3: Building a Facebook-like Status Poster using NetBeans
* Chapter 4: Debugging and Testing using NetBeans
* Chapter 5: Using Code Documentation
* Chapter 6: Understanding Git, the NetBeans Way
* Chapter 7: Building User Registration, Login, and Logout
* Appendix A: Introducing Symfony2 Support in NetBeans 7.2
* Appendix B: NetBeans Keyboard Shortcuts
I didn't have time to check if the code examples worked on my machine, but I
did like the way the author structured and explained them. The book starts
with quite some installation screenshots, these could have been moved to an
appendix probably. However, when coding started (ch3) the book's pace speeds
up and nice features like debugging, building and automating tests make it an
interesting read. After all I felt the book reached a good balance between GUI
options, programming and development.
One annoying thing was that the code in the epub version of the ebook was not
indented. So I switched to the PDF version which was perfect. I guess the
printed edition is like the PDF so that should not be an issue.
[PHP Application Development with NetBeans](
application-development-with-netbeans-beginners-guide/book) is recommended
reading if you want to explore the NetBeans IDE. Even if you like to work in
the terminal like me this book is a good exploration what an IDE can offer
you. It is always good to keep analyzing your tools. I can even foresee a
mixture of IDE and editor use in my case.
The PHP and JS code is good, well explained, but I think the takeaway is the
easiness with which NetBeans facilitates Unit Testing, Version control,
writing PHP documentation, Debugging, etc. In 250 pages a lot is covered, some
things only at the surface (debugging for example), but that is inherent to a
beginner's guide.
I think the book meets its goal: show how PHP development could be simplified
in this IDE.
![book cover](
I finished reading [PHP & MYSQL Novice to
Ninja]( and I recommend it for
beginners and intermediate learners of PHP. It is easy to digest, teaches best
practices and the code is clean and usable.
![featured image](
Disclaimer: contrary to other book reviews, I did not get a copy for this one.
As I found this a remarkable PHP book, I wanted to dedicate a blog post to it.
The author probably can describe best what kind of book this is
([source]( " Just as PHP and
MySQL have grown from the young upstarts of the web development world into
mature, stable platforms for billion-dollar businesses, this book that I've
been writing again and again for over a decade has grown up It's time to
write PHP like the big kids do ".
Why is this such a good starter for learning PHP?
* This is the 5th edition of what has become a best-seller book on PHP. Each edition has grown in handling more recent PHP versions and ways PHP is used nowadays. It uses a clean MVC (model-view-controller) structure seperating PHP controlling code from HTML view templates. It attacks common security pitfalls and presents the concepts in a logical order. From reading the 4th and 5th editions I can attest that it is a well crafted book.
* Beginnings of OOP: the database code has been rewritten in this edition to use [PDO]( (PHP Data Objects API) which professional PHP developers should use. PDO is like JDBC (Java) or DBI (Perl). It provides a data-access abstraction layer which makes changing from one database server to another easy. PDO works with prepared statements which protect you from SQL-injection attacks. Required is a [database-specific PDO driver]( but I get sidetracked ;)
* Code best practices: apart from PDO, we see try/catch blocks, MVC architecture, eye for security practices (for example the use of [is_uploaded_file]( when dealing with file upload forms) and more
* I found the database chapters amazing. Again clear, concise, and teach good practices in design. It is mixed in with the PHP chapters: ch2 gives you some basics about mysql, ch5 explains Relational Database Design, ch6-9 uses it heavily in the building of the internet joke database CMS, ch10 - MySQL Administration and ch11 - Advanced SQL Queries, teach more advanced topics. Plus 3 appendices with MySQL reference material. Even if you are going to use another database product then MySQL, the database intro alone provided in this book is worth spending the money.
* Very practical: this book shows you how to build your first dynamic database driven website, step by step. The examples are very clear. I got some years of experience now so the first 100 or so pages I just gazed over, but the project code still had good insights for me. So even having the basics, this can still be a useful reference to do things the right way.
The Sitepoint podcast [interviewed the author](
/sitepoint-podcast-161-the-return-of-the-yank-with-kevin-yank/) some months
back about this new 5th edition. To go deeper, I think [PHP
Master]( might be a good sequel (I
have to read it yet). It explains more advanced topics like OOP, testing,
security and APIs.
![cover php mysql novice to nina](
After having learned the basics in [Learning
Perl]( ("Llama book"),
[Intermediate Perl]( takes
you to the next level. It is aimed at doing more serious development work
(programs of over 1000 lines of code). Hence it introduces the more advanced
concepts as complex data structures, references, packages and OOP.
### Structure
![featured image](
Disclaimer: I received a copy from O'Reilly for review.
This book covers a lot in its 21 chapters. It serves well a classroom or self-
paced learning discipline with equally length chapters of approximately one
hour reading time and exercises that help you practice the newly introduced
I like the introductional chapters, getting into [CPAN](,
intermediate foundations (grep, map, eval) and references. Good prep work to
start tackling more complex problems. Up until chapter 10 it expands on
references concepts which was a really good read. At chapter 11 there is a
turning point into software development: how to write bigger and more
maintainable programs in Perl. How to create your own Perl distribution,
Perl's OOP design and (Advanced) Testing, ending with
[Moose]( and contributing to CPAN. At this
point, when using all these concepts in your daily work, you are probably
becoming a master. The next logical step is to then read [Mastering
Perl](, the 3rd O'Reilly Perl
learning series book or the Perl bible, [Programming
### OOP Perl
I found this the most complex part of the book, not as clear as the other
topics (ch 1-11 and Testing). I am not sure this is inherent to Perl's design
of OOP or the way Intermediate Perl presented the content and/or its samples.
I probably have to start using OOP more in my work, and I am going to read
Damian Conwey's [OOP title]( on the subject
which seems a good place to learn OOP Perl in depth.
### Overall opinion
I can recommend [Intermediate
Perl]( as a logical step
after the Llama. If you want to build larger, more powerful/ robust programs,
this gives you a jumpstart. You might as me need more material and practice on
object oriented Perl but that is fine, this is still a very useful reference
that brings together fundamental intermediate to advanced Perl techniques.
If you are serious about a career in Perl development this book should be on
your shelf. For simple scripting needs it might be overkill, but even then you
will need to pass a reference around form time to time, re-use software from
CPAN, etc. Apart from that, you will learn better, faster, and more efficient
coding practices in Perl from this book.
![intermediate perl book cover](
[Practical Vim: Edit text at the speed of
Thought]( is an intermediate to
advanced book on Vim published by [The Pragmatic
Bookshelf]( I found this a very useful Vim resource to
sharpen my skills. In this post some more feedback on the book.
(Disclaimer: I received a copy of Practical Vim for review)
### Overview
This book teaches a lot of Vim skills in 121 tips. Some are easy, others
advanced, but above all they teach the Vim way! Following the examples you
start to approach text edit challenges differently. Even feeling comfortable
with most editing tasks, there is probably a better way to do it. This book
had quite a few eye-openers for me, for example the Dot Formula. The examples
are based on real-life scenarios, and compare different approaches, explaining
why one is better than the other. The examples are also very well illustrated
with tables showing how the Buffer Content looks after one or more keystrokes.
### Mode of use
Cover-to-cover read, or as reference guide, both approaches work. Practicing
the examples in Vim while reading is recommended, it helped me memorizing the
keystrokes better. Apart from that, [as noted
changing 100% to Vim for all my projects and text editing has been a great
help to get better.
### I learned a lot
I added quite some things to my standard repertoire: diw, df, dt, use s
instead of x + i, ctrl+r to paste a register, the "+ to copy/paste to/from OS
clipboard, macros, better navigation between words (w b e ge vs W B E GE),
CTRL-O and CTRL-I to navigate the jumplist (across open files), nmap ,c
:%s///gn in .vimrc as a shortcut for number of search hits, make power
searches, better use of regex, and more A lot of good stuff, but I am glad
with the tips structure, so I can easily go back and commit more to my muscle
memory (it is all about making it a habit, see [Seven habits of effective text
Two more things I liked about this book: 1. Practical Vim shows you how to get
by with Vims core functionality (almost no plugins), 2. a lot of links to
Vim's Built-in Documentation and external resources.
I think this book and the author's [screencasts]( are both
unmissable to become proficient in Vim.
![practical vim book cover](
I like to solve common day problems, at the same time learning more coding.
Learning to code is all about [practice](
to-grow-craft-programming/). Today I show you a Perl script to get a daily
email listing all movies broadcasted by Spanish TV in the coming 24 hours.
![featured image](
I tried to do this [some time
- [BeautifulSoup]( is very
powerful for html parsing, but with the typical [tv guide](http://parrilla- I had to rely on a class "cine inactiu" to filter the
movies out. This worked pretty well, but it was not complete and as the movie
names are in Spanish I linked to imdbapi for more info which usually did not
yield any result. So I needed to redo this exercise
I didn't start scripting. First I looked for a better source which [I
found]( This page does half
of the work finding all movies to be aired today on Spanish TV. Usually it is
not necessary to code everything! What I wanted to add on top of this was:
* Enrich this list with info for each movie, mainly the director and actors. This was easy following and parsing each link of the list.
* Email this list to my email with a cron job. An issue was the encoding not showing up well in Apple Mail and mailx not sending out the cron mail when the encoding was not defined well in the command's switches. More on that in a minute, without further ado the script which is also available [on Github](
#!/usr/bin/perl -w
# author: bob belderbos
# v0.1 sept 2012
# purpose: send an email with all movies on Spanish TV in the next 24 hours
# has a pretty complete list
# this script servers best in a daily cronjob
use strict;
use Data::Dumper;
use LWP::Simple;
use Encode qw(encode decode); #
my $enc = 'utf-8';
my $output;
my $email = "yourname\";
my @html = getUrl("");
# movie lines start with hh:mm timestamps
for (grep {/^\d{2}:\d{2}|^<br/} @html){
# separate days
s/<br \/>//g;
$output .= createHeader($_, "*");
# parse movies
m/(\d{2}:\d{2}) - <a.*?"([^"]+)" href="([^"]+)".*- ([^<]+).*/;
my ($time, $title, $url, $channel) = ($1, $2, $3, $4);
$output .= encode($enc,
createHeader("$time / $channel / $title") .
"$url\n" .
getMovieInfo($url) .
# send me the generated movielist
sendEmail($email, $output);
sub getUrl {
my $url = shift;
my @html = split /<\/?li[> ]/, get($url);
return @html;
sub getMovieInfo {
my $url = shift;
my $info;
next if(! /column/);
for my $line (split /<\/?h3[> ]/, $_){
if($line =~ /Director:|rpretes|Idioma|Nacionalidad|A&ntilde/){
$line =~ s/.*?strong>(.*)<\/strong>(.*)/$1$2\n/g;
$line =~ s/A&ntilde;o/Estreno/g;
$info .= $line ;
return $info;
sub createHeader {
my $str = shift;
my $delimiter = shift // "=";
my $width = 70;
my $output = "\n" . $delimiter x $width . "\n" . $str . "\n" . $delimiter x $width . "\n";
return $output;
sub sendEmail {
my ($to, $output) = @_;
my $subject = "Today's movies Spanish TV";
# on mail pipe:
open my $pipe, '|-', 'mailx',
'-s', $subject,
# char issue mailx:
'-S', 'ttycharset=utf-8', '-S', 'sendcharsets=utf-8', '-S', 'encoding=8bit',
$to or die "can't open pipe to mailx: $!\n";
print $pipe $output;
close $pipe;
### Some notes / things I learned
* I learned how send email via Perl, see the "sendEmail" subroutine. I used a [pipe / mailx](, using [sendmail]( would not work with my hosting account. The only thing I had trouble with were Spanish characters like , , , etc. Mailx would complain, not send out the mail, putting the email in dead.letter in my home. Encoded to utf-8 the help of [this article]( did the trick!
![example mail](
* The other challenge with Spanish characters was parsing the [source page]( I found [a good article]( explaining encodings and code to do this.
* The rest is pretty basic Perl, some Regex that really makes me like Perl and use it for more and more text parsing. I probably have to use some eval {} if one of the movie pages does not respond, otherwise the cronjob will send me another mail with the stderr output of the script execution (or I can say 2>/dev/null in the cronjob, but I think script should handle this).
### Bonus
Having the time coded as hh:mm, Apple mail recognizes this and when clicking
it you can add an event to your calendar. This way I can easily put a reminder
of a movie I potentially want to see later in the evening.
![add event from mail](
### Update 23.09.2012
The movie page does not always give the English movie name and sometimes it
has a generic term like "La pelcula de la semana" which doesn't give any
clew. So I did [some more parsing](
blob/master/perl/ include the movie name, original name and plot
info. I also included the Twitter and Facebook sharing links which are on each
movie page at the bottom, example:
![new output](
Learning Vim takes time, but once you master enough commands (keys), you
understand why it is one of the most powerful editors out there. In this post
some simple tips that helped (and still helps) me improving my Vim skills.
### Practice
OK, like [becoming a better programmer](
to-grow-craft-programming/) practice is everything, but to facilitate that you
need to get out of your comfort zone. I used Textmate a lot on the mac and
recently I was using [Sublime]( for all my coding
on each OS. If you are serious about learning Vim though, start using it for
all your work. At the beginning you will not be able to do all things as fast
as in your favorite (graphical) editor but that pushes you to learn the
shortcuts / commands, and believe me: with Vim you can do it all! I think this
is the most important step towards becoming a Vim ninja.
### Start looking up key shortcuts / commands / combinations
![featured image](
This advice comes from [destroy all
software]( when you don't know
about the functionality of a certain key shortcut, look it up with :help . Go
from unshifted keys, to shifted keys, to control keys as this is the order of
importance of the keys. Check out the motion commands (:help motion.txt) as
well. An important rule is to never repeat yourself, when you do it probably
indicates a hole in your Vim knowledge (cheers
[Gary]( for this great piece of advice).
You can't remember all shortcuts in a week, but gradually over time you can
learn quite a lot. Just by doing this I found out about f+char to go to the
next char (so you can delete a phrase till the end dot by df.) , markers with
'm', named buffers, using w or e to go to next words including/excluding
trailing spaces, and other less frequently used shortcuts. It is also
important to learn useful combinations like diw (delete in word), or daw =
deleting a word killing the adjacent white space as well, or copying or
deleting till the end of the line, y$ and d$ respectively. Another very useful
thing to try to grasp early on is repeating commands: . for repeating a
command in normal mode, n/N for forward/backward repeated search and & to
repeat a replacement (:s/foo/bar/g) operation.
### Check out some books / resources about Vim
Even with some prior knowledge I found $ vimtutor a useful program to run and
doublecheck, it takes +/- 20 min and towards the end there are some neat
tricks. If you are at a beginner level or you have not used Vim in a while
this is the best place to start (refresh).
Books: I am using [Hacking
Vim]( and [Learning
Vim]( to study and
practice. They are invaluable. Learning Vim goes from basic to pretty
advanced, I am halfway through and it is a really good resource. Hacking Vim
is a cookbook and has tought me some useful tricks so far: cntrl+n for
autocomplete, gd on a variable name to go to its initial definition (go to
definition), .vimrc settings (see next), % to go to the end of a code block,
g, / g; for go back of recent locations you made changes, g# / g* for search
for the word your cursor is on, etc. It even introduces to Vim scripting!
### Resources II) Google, stackoverflow and blogs
Yes everything is possible in Vim, for example I wanted to open multiple
files, no problem : vim -p files and you open files in tabs, ct takes you to
the next tab, in horizontal split? No problem, use vim -o files, or when
editing a file, open a 2nd one in vertical split with ex command :vsp file2 -
most I found by just googling and on stackoverflow. There is so much material
out there, and practicing it bit by bit, making notes and doing it over and
over again, you become faster and faster in Vim.
### Make life easier with .vimrc
Try to tweak your config, I started pretty basic with just a few settings: set
nu to set line numbers, mapleader for
[command-t]( you really
should check out (also discovered this via the very useful destroy all
software Vim screencasts), highlighting, etc. I probably end up with 100+
lines over time, but this small set of config settings is helpful already:
syntax on
let mapleader = ","
set nu
set cursorline
highlight CursorLine guibg=lightblue ctermbg=lightgray
set hls is
" <Ctrl-l> redraws the screen and removes any search highlighting.
nnoremap <silent> <C-l> :nohl<CR><C-l>
### More Vim
I opened a category for Vim to share more tricks and resources in the future.
Efficient use of a text editor is one of the fundamental tools of any software
### Your editor?
Would Vim be your editor of choice? Why or why not?
Of course there is not one correct answer to this question.In this post I
will outline 7 steps I think you can take to grow as a programmer. For myself
it should be a reference to stay on track. Maybe you find it useful as well.
If you have additional tactics please share them.
"We will encourage you to develop the three great virtues of a programmer:
_laziness, impatience, and hubris_." -
### How to grow as a programmer?
#### Read / write
content/uploads/2012/09/better_programmer.png)1. You learn programming by
practice, a lot of practice. Write a lot of code, best way to actually do this
is to start work (and hobby) projects, look around at problems you can solve.
If you cannot find interesting problems, try to automate silly or actual
useful common tasks you or your team mates have to do on a regular basis.
Another way is to contribute to open source.
2. Read other people's code, both bad and extraordinary code. It definitely
takes some discipline to go through code that does not make sense at first
sight. Look up keywords and functions you don't understand, run it if
possible, take it to pieces. What is good practice, what would you do
differently and why? What creative solutions did the programmer apply?
3. Develop a non-stop learning attitude, read at least 2 programming books a
month, both on concepts / from the pros ([code complete](
8&qid=1346513645&sr=8-1&keywords=code+complete), [clean
350882/ref=sr_1_1?ie=UTF8&qid=1346513639&sr=8-1&keywords=clean+code), [making
[coders at work](
ork)) as well as the programming tutorials (I personally like the O'Reilly
books), it is important to NOT get into a comfort zone: PHP and Perl enough?
No, learn Python and/or a compiled language. Only know Java? Check out Ruby.
Done a lot of OO (object oriented)? Look into FP (functional programming).
#### Get into the field
4. Hang out/ work with programmers that are better than you: it might feel
awkward, but this way you will step on theacceleratorof your learning
process. I think most good software developers are willing to help you if you
show genuine interest, passion and a proactive attitude. In practice this
means, when you don't understand something they do, go learn it and come back
for more. Experienced people will like this, they probably recognize
themselves when they started. Keep in mind that they were not born with the
skills eiher, they learned it at some point as well.
#### Skills / practices
5. Become a good problem solver - this is usually what programming is all
about, writing the code (syntax) is one thing, but at its core you need to
understand how to effictively / creatively solve a problem. Usually this also
requires being an allrounder: understand the software stack, understand some
Networking and OS as well, learn to ask the right questions.
6. Not only code to code: what is a client/user need, is the program easy to
use for his/her skill level? Software development practices and processes,
deployment to a server, get out of the coder's cave and try to understand the
bigger picture. An interesting article from Coding Horror: [How To Become a
Better Programmer by Not Programming](
/how-to-become-a-better-programmer-by-not-programming.html).On another note:
learn TDD (test drive development), learn to write code for humans (the next
coder that gets to see your code), learn to write clean code. This is not a
binary thing ("not done, now done"), you gradually develop these skills with
experience I think.
7. Develop your toolkit. Save time and brain cycles, prevent headaches! So use
version control software (I use Git), use a fast and powerful text editor (I
am getting better with Vim). Automate repetitive / easy tasks to focus on the
real learning challenges (e.g. shell aliases, automate ssh login, learn more
Vim keystrokes, all shave off seconds, but over time it adds up!). There
probably is a lot you could add or discuss here, but it boils down to becoming
more efficient by using existing and/or creating new tools.
### And you?
I assume most readers of this blog have a programmer, web developer or alike
background. What techniques do you use to become better in your field?
Facebook Apps can live inside or outside Facebook. Inside Facebook they can
live in [the Canvas]( or
in a [Page
Tab]( It is
important to be able to detect these modes to provide a customized experience.
Although the documentation is pretty clear, I feel it is useful to make some
notes here how this works.
![featured image](
According to the [Canvas
"whenever your application is loaded within a Canvas page, Facebook will make
an HTTP POST to your Canvas URL. This request will include a POST variable
called signed_request.". For [Page
Tabs]( this
works similarly: "When a user selects your Page Tab, you will receive the
signed_request parameter with one additional parameter, page. This parameter
contains a JSON object with an id (the page id of the current page) "
It is the signed_request parameter I will focus on in this post. I will show
you how to get the necessary information out of it and show examples how to
use it to determine the flow of your app.
### Why is this useful?
In this post I will give you two examples how this can be useful:
* 1. get rid of the vertical scrollbar in ONLY the Canvas version of My Reading List
* 2. identify within a page tab application what Facebook page id it runs in to deliver specific content based on that page id.
### How to parse the signed request?
You can do it manually with [this PHP code
or use Facebook's [PHP SDK](
Facebook requires your app's Secret to ensure the request was sent by Facebook
and not by a third party. The response will yield a JSON object, something
In the canvas:
![signed request response inside the canvas](
And in a page tab:
![signed request response inside a page tab](
### Practical examples:
#### 1. My Reading List
For [My Reading List]( (and any app for that matter)
I typically want to get rid of the vertical scroll bar in the canvas. One way
to do this is with Facebook's Javascript SDK's method: [FB.Canvas.setAutoGrow(
Grow/) (another method is [FB.Canvas.setSize](
docs/reference/javascript/FB.Canvas.setSize/)). Additionally you set the CSS
property "overflow" to "hidden" on the body tag.
However doing this makes the "standalone" (outside Facebook) non-scrollable
which presents a problem.
So a solution is to ask the App if it is in the Canvas, example in PHP:
if (canvasMode($fb_secret)) {
echo "<body style='overflow:hidden;'>";
} else {
echo "<body>";
function canvasMode($secret){
$canvas = false;
if( isset($_REQUEST['signed_request']) ) {
$data = parse_signed_request($_REQUEST["signed_request"], $secret);
if( $data ) {
$canvas = true;
return $canvas;
and the result is I have the best of both worlds:
1. inside the Facebook Canvas: no scrollbar
![inside Facebook = no scrollbar](
2. outside of Facebook, overflow is not set to "hidden" on the body, hence the
content is scrollable (default html rendering):
![outside Facebook = scrollbar](
#### 2. Dynamic content in page tabs
Page tabs is a cool feature I recently learned about. It allows you to attach
a Facebook app to a Facebook Page. Facebook makes this really easy as you can
read [here](
Again, you can leverage the signed request response to tell your app on which
Facebook page it is running. This way you can load different content on
different pages that have the Page Tab app installed.
To illustrate this, we can use the same code as above, however we are looking
for the page id bit in the JSON object:
$signed_request = parse_signed_request($_REQUEST['signed_request'], $secret);
$pageId = $signed_request['page']['id'];
Now you can match this page id against for example a database which holds
different content for every Facebook page (id).
### Conclusion
Standalone (outside Facebook) apps, Canvas and Page Tab apps are different in
nature so it is important to identify when you are operating in one or the
The Facebook SDK or the raw code example Facebook provided, suit this purpose
quite well. Once you parse the signed request you can influence the way the
App works in every instance (environment).
If you have interesting use cases, other than the two examples shown in this
post, feel free to share them in the comments.
The anticipated new release of [Sharemovies]( is up. It
provides an easier way to browse movies and provides a lot more info. I
redesigned it from the ground up, keeping the most valuable elements of the
initial version. In this post an overview of the new design and features.
### V1
I started [Sharemovies]( back [in
2010]( Since then
I did some tweaking and the site looked like this:
![initial release](
![initial release](
### V2
The new version is taking a whole new approach. I started using [themoviedb
API v3]( It is a rich API with
reasonable [request limits](
limits). You should still use caching though.
The new release consists of 4 parts: the homepage, movie pages, person (cast /
crew member) pages, and user pages.
#### Homepage
The homepage shows the upcoming, top-rated and "now in theatres" movies. This
data gets refreshed every 24 hours.
![the new homepage](
#### Seach is important
![featured image](
Central in the [fixed positioned header](
to-make-a-fixed-sidebar-or-header-with-css/) is the search box, it uses
autocomplete to provide a rich search result including trailers ( I did this
with v2 of themoviedb API [some time ago](
/sharemovies-new-feature-search-interface-instant-trailers/) ). I think it is
one of the most important / most useful things of the site: movie info can be
found quickly and easily! Before clicking on the result ( = going to the movie
page), you can watch the trailer to decide if you want to know more about that
movie. Appending a year makes the search more accurate and it understands
other languages (at least in the cases I tried: "Caza al terrorista" gives me
"The Assignment" and "Ciudade de Deus" gives me "City of God").
#### Autocomplete in action
#### With trailer when hovering over the dedicated area:
### Movie page
When selecting the search result or clicking on a poster anywhere on the site,
you are taken to the movie page:
![moviepage logged out view](
When you log in with Facebook you can comment, like and add the movie to your
watchlist and/or favorites.
![moviepage when logged in](
The "Share" button is linked to [addthis]( which
surprisingly lets you use a customizable button. This way the page can be
shared to most social networks.
![addthis button](
Below the big version of the movie poster are links to Amazon, Netflix, IMDb
and TMDb
![infolinks to other services](
### Trailers and similar movies
One of the things I really liked in v1 were the [Youtube
gdata/) in [boxy overlay]( -
this had to stay the same.
When you click on the red play button alongside the movie title, an overlay is
shown with the trailer. Warning: browsing movies and watching trailers this
way can be pretty addictive ;)
![view youtube trailer on moviepage](
You can also search for similar movies by clicking "You might also like "
under the Plot summary.
### Actors
When you click the actor photos or the crew names you are taken to a page
dedicated to that person. There you will see his/her details and movies. There
is a filter box at the top that lets you easily find a particular movie.
![actor page](
### Last but not least: user profiles
When you login with Facebook you can start adding movies to your Watchlist and
Favorites (see the blue buttons at the top of each movie page). When adding or
deleting movies from your list the button text and icon change on the fly
When you get more movies on your profile page ( the profile URL is , see mine
[here](, you can filter the results by
string (like we saw with the person pages), by genre or by "month added".
Again, some Javascript (jQuery) makes the user experience a lot better: things
can be found quickly and it is visually more attractive. Clicking on the
"Favorites" and "Watchlist" links at the top, scrolls you down to the
particular list.
![user profile page](
Best thing to get familiar is to open [my
profile]( and click around, then login with
Facebook and start building your own movie profile by adding movies when
browsing movie pages
#### Search and scroll
![filtering items](
![scroll to a list](
#### Filter on genre
![show all crime movies ](
![show all comedies](
#### From here
Enjoy this app, and share the word. I think it is useful for the movie fans
out there. I'm open for any issues or feedback
As far as I tried it worked reasonably well on an ipad. I'd like to build an
iphone app later this year
The coming weeks I will have 3 articles for you about best practices of
software development. Following these methods could lead to more efficient
development and better quality code. In this post: Programming Techniques and
### Purpose and planning
![featured image](
The purpose of this series is to have some understanding of how to write
quality code to become a better developer. It should be a theoretical
reference to go back to from time to time. It should complement the practical
day-to-day learning.
* [I) Programming Techniques and Strategies](
* [II) Good Coding Methodologies](
* III) Maintaining Quality Code
Potentially I will follow up with a 4th article about Objected-oriented
Coding, whenever I have some more experience with it.
### Programming Techniques and Strategies
50% to 75% of development time goes into testing and validation of code. Why?
Software development is a complicated process. In this posts some techniques
to handle this.
### Complexity
4 things that define uncontrolled complexity and should be limited:
* Coupling: the more dependencies of subsystems the more complex the system overall and the easier bugs get introduced;
* Control flows; the number of independent paths (see also [Cyclomatic complexity](;
* Complexity of data structures;
* The overall size of the program.
To avoid or reduce complexity a developer can:
* Partition: break the program up in smaller units or classes;
* Add hierarchy to your programs. A good example is the [OSI model]( similar communication functions are grouped into logical layers. In programs, units are black boxes and are self contained. This way they are easier to test. Use local (not global) variables. Build in privacy: data should be local to each blackbox element (method or function);
* Limit functionality at the start. Adding functionality scales up complexity in a non-linear fashion;
* Use an iterative development process: have early and frequent feedback loops. See further down
### Elegant software
Good software ought to be non verbose and elegant. The most efficient piece of
software for a particular purpose uses the least amount of code and the
smallest number of abstracted entities.
### Human factor
The human factor is important. Pair programming can reduce the amount of bugs
introduced into the system. Same with peer reviews and collective code
ownership (if communicated well and version controlled).
### Software quality
Good software can be measured by the following key metrics:
* External factors: availability, efficiency, flexibility, integrity/ security, interoperability, reliability and usability.
* Internal factors: maintainability (average time to correct faults), reusability (well documented, platform independent, modular), readability, portability, testability.
### Defensive programming
The best prevention measure is to write robust code that anticipates problems,
validates data inputs and terminates gracefully providing debug info to de
To be defensive a developer should use:
* Assertions: test for errors that should never occur;
* Input validations: test ALL data inputs (whether it comes from a database, users, or from methods);
* Error handling: these errors are expected but it depends the application how to handle them (a cash withdrawal machine should handle errors differently than a word processing program);
* Error containment: shut down parts of the program to prevent damage to other parts.
### Software development methodologies:
There are different types of development styles:
* [The Waterfall model]( is a sequential design process with its roots in the manufacturing industry. Progress flows from the top to the bottom. Requirements, design, implementation, verification and maintenance, each stage is completed before moving to the next one. The risk here is when requirements change throughout the process. In that case you would have to go through most of the process again.
* [Iterative development]( is cyclic and a response to the weaknesses of the Waterfall model. It is more flexible because it works with smaller iterations. It is adaptive, starts with a small version, changes per cycle are smaller so easier to correct if wrong. The customer has an early demo ready.
* [Agile methods]( are iterative and incremental. They are adaptive rather than predictive and people-oriented rather than process driven. The work is done in small self-organizing, cross-functional teams.
* [Extreme Programming]( is a type of agile software development. It is characterized by simplicity and clarity in code (using coding standards), rapid iterations, team work (pair programming), refactoring and unit testing, frequent feedback from the customer (changes are a fact of life) and 40-hour workweeks (no overtime).
### More resources
Two must-reads on this topic are: [The Pragmatic Programmer: From Journeyman
to Master](
engineering/) and [Clean Code: A Handbook of Agile Software
Two of my previous posts that are related: [Some tips to make a developer's
life easier]( and
[Becoming a good debugger](
This is part II of the Best practices of Software Development series. If you
want to know how to write quality code to become a better developer read on
Today: Good Coding Methodologies.
### Planning
* [I) Programming Techniques and Strategies](
* [II) Good Coding Methodologies](
* III) Maintaining Quality Code
### Good Coding Methodologies
This post will talk about writing better code, common issues with program
structures and how to improve them, building good methods and a short note on
### Writing better code
![featured image](
One of the most important criteria is human-readable code. Code should be self
documenting. Don't rely on comments, use coding standards instead. It makes
the code more concise and predictable and thus easier to test, debug and
modify. The [required code-
indenting]( is one of the
reasons I like Python for example.
[Control structures]( should be
wrapped in blocks even if not strictly required. I also try to make a habit of
keeping lines under 70 or 80 characters wide. Whether you use
[camelCase]( or underscore-based style
for your variables, use a consistent scheme and stick to it. .
Comments are better than external documentation because they stick with the
code (modification). However don't overuse them: don't repeat the code, don't
provide unnecessary explanations, watch out for code changes (forgetting to
edit the comments as well!). Good comments summarize the code, describe its
intent and provide info that the code cannot express.
### Structure of programs
Examples of common issues:
* Improper use of pointers ([wild or dangling pointers](
* The use of [global variables]( making the program harder to maintain.
* Using literals instead of [constants]( (e.g. 86400 instead of SECONDS_IN_DAY).
* Confusing comparison "==" with assignment "=" operators.
* Wrong use of loop initial and end-condition values.
* [Short-circuit operators]( ("&&" and "||") - understanding that they might not execute in some circumstances.
* Comparing objects ("equals" and not "==").
* Fall-through: forget to "break" each "case" in a switch statement.
* Lack or improper use of error handling (see further down).
#### Expressions
To generally improve your writing of proper expressions you should use
parentheses in complex expressions. It is clearer to express Boolean
expressions positively unless the outcome is likely to be negative - so use:
"if(bookInStore)" instead of "if(!bookInStore)". Also avoid "(a>b) == TRUE",
just say "(a>b)". You should work with TRUE and FALSE booleans instead of 1
and 0, if your language does not support them you can define them as
#### Sequence
It is important to sequence code properly so that dependencies are visible
reading the method names. This implies that one method executes only one task
(more on Methods further down).
#### Variables and types
The advantage of [strong typing](
is that a variable can hold only one type of value. This encourages input
validation and errors that are detected early on at compilation time. You
should limit the lifetime of a variable, so declare it as close as where you
are going to use it. Named constants and enumerations make code more readable
and thus maintainable.
#### Structured flow-control
As mentioned: use the most likely outcome in the if statement (not in the
"else"). Enforce blocks for better readability. Other best practices for
loops: separate loop control code from loop work code, keep nesting down to
two or three levels at most, be careful (document) break and continue
### Methods (functions)
#### Building good methods
The purpose of a method should be clear from its name. I stress it again: one
method equals one task. This alone makes code much more readable and reliable.
It makes bugs easier to solve or prevents some of them at all. It also favors
code re-use.
Variables should be localized to the method to prevent unexpected
modifications from outside the method. In OOP you'd hide the data used by a
method within a object only making it accessible through the object's public
Bottom line is to think well what the goal of the method is, what should it
accomplish? Then write it.
#### Handling arguments and return values
A method should not have more than 7 or 8 parameters. Optional arguments
should be avoided because they lure you in 1 method > 1 task (use other
methods to handle them). Distinguishing between input/working/output variables
(an example: input (passed to the method) = grossSalary; working = incomeTax ;
output = netSalary (to be calculated and returned) ). This prevents the wrong
variables to be modified with undesired results.
It is important to validate arguments, otherwise bad data will creep in
leading to failures further into the program (which are harder to debug).
Return empty arrays rather than null values.
### Exceptions
Unexpected events / errors should be handled by exceptions. If not, the user
is presented with errors that usually don't make any sense. So make sure that
the catch/except block actually handles the event. Depending the problem you'd
control what happens as a result, for example: disable a module, only show a
message, show debug info under the hood, etc. etc. See
[Wikipedia]( for more info.
Today a quick post on how to use Git to push your code to a remote location. I
found this very useful when developing sites. Welcome to post #100.
![featured image](
I learned this from [Using Git to manage a web site](
/git-website-howto) and after using this technique at 2 websites, I found this
needed to have a dedicated post. The article explains it very well, but this
note taking helps me to remember and implement it well. And you guys might
find this quite useful :)
Note that it is highly recommended to set up a key-based login to your remote
server, see [this useful SSH reference]( for more
### Steps from local git to remote mirror
* On the local server you begin with your project and commits, see [my first Git post]( how to get started if you are not familiar with Git.
* On the remote node you start by creating a new repository (assuming ~/repositories as the home of all code repos):
$ cd ~/repositories && mkdir repository.git
$ cd repository.git
$ git init --bare
You will see something like: Initialized empty Git repository in
/home/user/repository.git/ and it means you have a new repository to mirror
the local one to.
* Make a destination directory where your code will be copied to:
$ mkdir /home/user/target_dir
* Create the following script that will take care to check out the latest copy in the source target directory (/home/user/target_dir) when you commit to the remote server (with $ git commit aliasName ; see towards the end of this post )
$ vi hooks/post-receive
Enter the following:
# #!/bin/sh
#GIT_WORK_TREE=/home/user/target_dir git checkout -f
* And give the script execution permissions with:
$ chmod +x hooks/post-receive
You can find a quick wrapper script at [Github](
* Back on your localhost:
* Define a name of the remote mirror (replacing aliasName, user, and repository.git with your stuff ):
$ git remote add aliasName ssh://
* Push the code to remote location, creating a new "branch" master there, the hooks/post-receive script causes the source code to be copied to /home/user/target_dir you defined earlier.
$ git push web +master:refs/heads/master
* Following updates are easy:
After local committing with: $ git add . ; $ git commit -m "message" , you
push your code to your remote mirror with: $ git push aliasName
Editor's note: This is a guest post from [M.
Schilling](!/mschilling). He shares with us his views on
the inspiring book [Hackers & Painters: Big Ideas from the Computer
Age]( What can we get
out of this book, how to become a better programmer and some other goodies
### Hackers & Painters
I have just finished reading Hackers & Painters - Big Ideas from the Computer
Age by Paul Graham. This book consists of an interesting collection of essays
that are all independent of one another.
In 15 essays Graham covers a wide range of topics centered on computers. The
first half of the book is much about social theory and politics, while the
latter focusses more on computer science in general.
If you are a programmer (or hacker, as how Graham likes to call it) yourself,
you will find a lot of moments of recognition (e.g. "Yep!", or "So that's
why.."). That's one of the things I like about the book, and it makes reading
it easy.
### How to benefit from this book?
As a programmer, this book will bring you a lot of good thoughts and insights.
Some of the ideas might not be totally new to you, but Graham writes it is
such a way that you will find it interesting and it will make you think about
everything a lot more (which is, by definition, not always desirable ;-)).
Simply put: this book will affect the way how you look at your daily
programming tasks and projects.
### How to become a better programmer?
![featured image](
You will not find direct answers in this book. However, if you read very
carefully you will find out that the answer is there. Andy Hertzfeld is quoted
on the back cover of the book, saying "He may even make you want to start
programming in Lisp" and that's probably one of the things you'll have to do.
I believe that, on your way to become the best programmer, you have to know
about different programming languages and dare to admit that some languages
are more powerful than others.
Don't get stuck in your own comfort zone keep saying that your programming
language is the best, but keep your mind open for new suggestions.
Why not try a couple of (totally) different programming languages for a few
weeks and make them do the same job. When you're done, try to classify the
high level languages for yourself, from less
powerful to more powerful. It's the process of doing so, that will help you on
the long term to become a better programmer.
### Conclusion and what's next
If you're interested in the field of computer science, or even if you're just
curious about the topic "why are nerds unpopular on high school", I can
recommend you to read [Hackers &
A video resource I can recommend is the TV mini-series: [Nerds 2.0.1: A Brief
History of the Internet]( Enjoy this series,
and try to find out yourself, how it relates to the chapters in this book
(e.g. "Good Bad Attitude", "How to Make Wealth")
You can find more info on the author's page on the book at
== end ==
#### M.Schilling is software developer and database designer. You can follow
him at [Twitter](!/mschilling) or contact him via
From time to time I install a Linux variant on one of my boxes and start
fresh, good performance, lean install. I use git for all my projects now so I
am building up repositories on remote hosts fast. Wouldn't it be nice to be
able to clone them all to the newly installed host? Quite useful for quick
migrations, so I wrote a quick bash script that achieves this
### Assumptions
![import all remote repos](
* 1. You have a REMOTE host that holds all repositories in a central place, ~/repositories in this example.
* 2. I use [Git]( as version control software (I didn't look further yet as it is amazing software!).
* 3. You can ssh to the remote host. You can set up a key based login in [3 simple steps]( (on local host: ssh-keygen && ssh-copy-id -i ~/.ssh/ your-remote-host).
* 4. You remote repositories are "bare", see [how to push code to a remote web server with Git]( as a reference. I use this technique for each new coding project now, it is highly efficient.
* 5. My naming convention for remote repositories is project_name.git, located at ~/repositories/project_name.git.
* 6. I have a CODE directory in my home directory ($HOME) on my LOCAL host. You should change lines 5-9 of the script below with your own settings
* 7. You might want to refine the script if you are going to use it in a more professional (enterprise) environment. For me it fits the purpose for now though.
### Code (also available [here](
# import multiple remote git repositories to local CODE dir
# settings / change this to your config
remoteRepos=$(ssh -l $remoteUser $remoteHost "ls $remoteDir")
# if no output from the remote ssh cmd, bail out
if [ -z "$remoteRepos" ]; then
echo "No results from remote repo listing (via SSH)"
# for each repo found remotely, check if it exists locally
# assumption: name repo = repo.git, to be saved to repo (w/o .git)
# if dir exists, skip, if not, clone the remote git repo into it
for gitRepo in $remoteRepos
localRepoDir=$(echo ${localCodeDir}${gitRepo}|cut -d'.' -f1)
if [ -d $localRepoDir ]; then
echo -e "Directory $localRepoDir already exits, skipping ...\n"
cloneCmd="git clone ssh://$remoteUser@$remoteHost/$remoteDir"
cloneCmd=$cloneCmd"$gitRepo $localRepoDir"
cloneCmdRun=$($cloneCmd 2>&1)
echo -e "Running: \n$ $cloneCmd"
echo -e "${cloneCmdRun}\n\n"
### Explanation what some concepts mean
* ssh -l user host "remote command" => allows you to run remote commands from your local machine, quite nice for automating tasks over the network.
* remoteRepos=$(.. command ..) => $() executes the command and returns the output to a new variable called remoteRepos (remoteRepos for assigning values, $remoteRepos to access them).
* -z "$remoteRepos" => checks for an empty string.
* cut -d'.' -f1 => takes the .git off (-d'.' = set delimiter to dot (.) , -f1 = field #1).
* echo -e => the -e flag allows "echo" to handle the new line character (\n).
* cloneCmd=$cloneCmd"..string.." => concatenation technique to keep lines shorter (I ideally strive to not put more than 80 chars per line).
* 2>&1 at the end of an Unix command sends the stderr output to stdout, basically I want to send all info to the same output stream. In bigger scripts I usually send it to a stderr directory with "2>stderr/file_unix_timestamp.err" (I got this tip from a more senior programmer; it has saved me time because I get crucial data for debugging!).
### Example running this from cli
Directory /home/bob/CODE/bamboo already exits, skipping ...
Directory /home/bob/CODE/codesnippets already exits, skipping ...
Directory /home/bob/CODE/myreadinglist already exits, skipping ...
$ git clone ssh:// /home/bob/CODE/myreadinglist_v2
Cloning into '/home/bob/CODE/myreadinglist_v2'...
$ git clone ssh:// /home/bob/CODE/portfolio
Cloning into '/home/bob/CODE/portfolio'...
$ git clone ssh:// /home/bob/CODE/stock
Cloning into '/home/bob/CODE/stock'...
Directory /home/bob/CODE/tweetdigest already exits, skipping ...
#books #facebook #github #howto #instagram #javascript #jquery #productivity
#reading #ssh #sublime #twitter #ubuntu and more
** Two months merged together because I didn't tweet that much lately. I do want to maintain a periodic digest though. It is useful to index useful links and resources on my blog :)
> 3 Steps to Perform [#SSH]( Login Without
Password Using ssh-keygen & ssh-copy-id
> Bob Belderbos (@bbelderbos) [June 30,
> RT [@chrismatthieu:]( Very interesting
article on how AWS is doing API security better -> Designing a Secure REST
(Web) API without OAut
> Bob Belderbos (@bbelderbos) [June 30,
> [@newsycombinator:](
[#Twitter]( Continues to Restrict Access
to Our Tweets []( - i agree: their
rate limits are discouraging!
> Bob Belderbos (@bbelderbos) [June 30,
> [@BkVirendra:]( TechCrunch | Why Students
Should Gain Entrepreneurship Experience Before GraduatingTechCrunch
[]( - indeed!
> Bob Belderbos (@bbelderbos) [June 30,
> RT [@fbreadinglist:]( More Reading:
JavaScript: The Definitive Guide ->
> Bob Belderbos (@bbelderbos) [June 30,
> [#sublime]( 2 on
> Bob Belderbos (@bbelderbos) [June 30,
> Watch "TEDxEastsidePrep - Scott Young - Can you get an MIT education for
$2000?" Video at TEDxTalks: []( -
> Bob Belderbos (@bbelderbos) [June 29,
> [#javascript]( validation tool -
> Bob Belderbos (@bbelderbos) [June 29,
> 24 [#JavaScript]( Best Practices for
Beginners | Nettuts+ [](
> Bob Belderbos (@bbelderbos) [June 29,
> Make Your Own Bookmarklets With
[]( via
> Bob Belderbos (@bbelderbos) [June 29,
> I want to learn more [#javascript](
> Bob Belderbos (@bbelderbos) [June 29,
> [#github](
[#howto]( Generating SSH Keys
> Bob Belderbos (@bbelderbos) [June 29,
> SitePoint Podcast [#166:]( Front End
Development with Mason Stewart - [](
- interesting interview [#javascript](
> Bob Belderbos (@bbelderbos) [June 27,
> RT [@BkVirendra:]( Sleepless at Stanford
> Bob Belderbos (@bbelderbos) [June 26,
> GTD For Hackers - [](
> Bob Belderbos (@bbelderbos) [June 26,
> Microsoft Buys Social Network Yammer for $1.2 Billion
> Bob Belderbos (@bbelderbos) [June 26,
> [#sublime]( 2 released
> Bob Belderbos (@bbelderbos) [June 26,
> Bookmarking Without JS []( - nice
one [@pageaffairs]( , I did use some JS for
hacker news though ;)
> Bob Belderbos (@bbelderbos) [June 24,
> RT [@pragpub:]( The June PragPub is out.
Here's the html version: [](
> Bob Belderbos (@bbelderbos) [June 23,
> How to Improve the quality of your software: find an old computer |
[]( via
> Bob Belderbos (@bbelderbos) [June 06,
> Bad day for LinkedIn: 6.5 million hashed passwords reportedly leaked
change yours now []( via
> Bob Belderbos (@bbelderbos) [June 06,
> The Pragmatic Bookshelf | PragPub 2012-05 | Effective Emails
> Bob Belderbos (@bbelderbos) [June 05,
> The Pragmatic Bookshelf | PragPub 2012-05 | What Makes an Awesome Command-
line Application? [](
> Bob Belderbos (@bbelderbos) [June 05,
> RT [@smai:](
[#Facebook]( is down.
[#Productivity]( is up.
> Bob Belderbos (@bbelderbos) [June 03,
> working with Ubuntu 12.04 for several weeks now and it is both pretty, smart
and fast, thanks [@ubuntudev](
> Bob Belderbos (@bbelderbos) [June 03,
> My Reading List - []( - a Facebook
[#reading]( App to share and inform
yourself about your favorite [#books](
> Bob Belderbos (@bbelderbos) [May 24,
> My Reading List: The Developer's Code
[]( via
[@fbreadinglist]( - great book
> Bob Belderbos (@bbelderbos) [May 15,
> RT [@sitepointdotcom:]( Kevin Yanks
done it again! Hes just completed a new 2012 edition of his best-selling PHP
MySQL book. [](
> Bob Belderbos (@bbelderbos) [May 03,
> interesting: Scaling [#Instagram](
> Bob Belderbos (@bbelderbos) [May 02,
> Read It Later becomes Pocket, goes free for all
[]( via
> Bob Belderbos (@bbelderbos) [May 02,
> Core Python Programming: Integrating Google APIs and Technologies
> Bob Belderbos (@bbelderbos) [May 01,
> RT [@FreeTheFeross:]( Building Beautiful
Apps from Ugly Code [](
> Bob Belderbos (@bbelderbos) [May 01,
> RT [@FreeTheFeross:]( Write Code Like
You Just Learned How to Program [](
> Bob Belderbos (@bbelderbos) [May 01,
> macbook pro has 1 port for input and output audio, didn't know that
> Bob Belderbos (@bbelderbos) [May 01,
> RT [@igrigorik:]( SSH in your browser? no
really, now in Chrome: []( - uses
NaCl under the hood.
> Bob Belderbos (@bbelderbos) [May 01,
#app #autocomplete #css #database #facebook #finder #fonts #git #inspiration
#instagram #instant #jquery #json #kindle #mac #php #productivity #python
#sublime #texteditors #unix
** Note I do a monthly tweet digest post now. I don't tweet that often and a digest once a month is enough I think
> [#git]( fetch and merge, don't pull
[]( - interesting read
> Bob Belderbos (@bbelderbos) [April 29,
> [#app:]( send to
[#kindle:]( it even converts PDF documents
to Kindle format !
> Bob Belderbos (@bbelderbos) [April 29,
> nice [#app:]( send to
> Bob Belderbos (@bbelderbos) [April 29,
> Sorting an associative array in PHP - Stack Overflow
[#arrays]( - nice to know
> Bob Belderbos (@bbelderbos) [April 13,
> CSS Font Stack :: A complete collection of web safe CSS font stacks :: Web
Fonts [](
> Bob Belderbos (@bbelderbos) [April 12,
> [@sublimehq]( wow I used it on windows today,
it is totally awesome, maybe even nicer than textmate. check out
[#sublime]( folks
> Bob Belderbos (@bbelderbos) [April 12,
> Build a Billion Dollar App, the Sale of
[#Instagram]( - SitePoint
[]( via
> Bob Belderbos (@bbelderbos) [April 11,
> Good article about [#fonts]( in web design
> Bob Belderbos (@bbelderbos) [April 11,
> [#Facebook]( to Buy Photo-Sharing
Service [#Instagram]( for $1 Billion
> Bob Belderbos (@bbelderbos) [April 09,
> Building Fast Client-side Searches -
> Bob Belderbos (@bbelderbos) [April 07,
> Taking CSS1k to the Garden of CSS-3den via
[#css]( cool: nice design in 1k !
> Bob Belderbos (@bbelderbos) [April 06,
> Validating WordPress Comment Forms with jQuery and a Custom Plugin -
SitePoint []( via
> Bob Belderbos (@bbelderbos) [April 06,
> Interesting and I think very true and relevant these days
> Bob Belderbos (@bbelderbos) [April 06,
> Show full directory path in Finder window title bars
[]( - nice!
> Bob Belderbos (@bbelderbos) [April 06,
> [#python](
[#database]( access
> Bob Belderbos (@bbelderbos) [April 05,
> My Reading List | Great by Choice
[]( via
> Bob Belderbos (@bbelderbos) [April 05,
> Always have your stuff when you need it with
[@Dropbox.]( 2GB account is free!
> Bob Belderbos (@bbelderbos) [April 05,
> Get Inspired [](
> Bob Belderbos (@bbelderbos) [April 02,
> Facebook Instant Search! Searching Facebook In RealTime !!!
[]( via
[@TeckZoneIn]( - nice!
> Bob Belderbos (@bbelderbos) [April 02,
I tweaked my [Bamboo theme](
design/) to be fresh, professional and above all cleaner. If not on a mobile
or RSS, you are probably looking at the results. I also needed to clean up the
Archive page. I found a nice plugin: [Grid
Last year I showed some [cool wordpress
but most of them I dropped or replaced by better ones (some were just
downright ugly / cluttered). One of the plugins that remained was [Snazzy
This is not a bad plugin, especially the images showing up is neat. The
annoying thing however was that after each update it seemed to overwrite user-
written CSS. The other thing I don't like is the variable height of each post,
it doesn't seem very structured. Time for a
### Why does this seem to be a good plugin?
![featured image](
* There is a [classic (HTML)]( and a [compact (JS)]( option. Good distinction.
* It has nice animated Javascript effects, play with it [here](
* There is a lot of customization possible. I was mainly interested to change the CSS to make it fit my theme design (which worked out pretty well, see below), and for it to stay across updates! In the admin panel it states: " You can also copy all the Grid Archives default css from "Plugin Editor" here and make any modifications you like, these modifications won't be lost when you update the plugin.".
* The ability to load Grid Archives resources only in specific pages. Another advantage the main concern about enabling WordPress plugins is increasing page size and requests, more functionality for less performance.
### Example customized CSS
The [default theme]( was not
that appealing to me:
![default theme](
Copying and editing the Grid Archives default CSS -
Overwritten CSS is [here](
content/themes/bamboo/grid.css). I think this demonstrates that this plugin is
worth a try!
Twitter's search API [only goes back 6-9 days](
/using-search). In this post I explore a way to get my full twitter history to
quickly search for what I tweeted. This way I can keep using Twitter as a way
to not only share but also as a reference tool for things I am learning.
![featured image](
There are good solutions out there to get your tweet history, see [this
archive/) for example or [snapbird]( So there is no lack
of tools, but as usual, I want to give it a try myself :)
Besides, if you want to have full control you might want to consider importing
your tweets yourself. This post is a first attempt but let me say upfront that
it is far from done. It is an idea to be further worked out. The nice thing is
that you get a bulk of data back which is good material to ask and solve many
questions your own way:
* At what time intervals do I tweet most?
* More interesting: what hashtag do I use most? With which other twitter users do I interact most?
* Simply build a search interface to search for stuff I need like a bookmarking tool (see at the end of this post).
* Etc.
All those questions you get probably answered by online apps around Twitter,
but hey it's a learning excercise as well. An important limitation is that
you can get max. 3200 tweets, for me that is ok, because I am still far below
that number. It is still a lot of data though :)
### A very simple Python script to start
#! /usr/bin/env python
# script to import tweet history via twitter's timeline pagination
import urllib
import simplejson
import pprint
user = "bbelderbos"
count = 100 # best result
pages = range(30)
for page in pages:
queryUrl = ""+user+".json?count="+str(count)+"&page="+str(page)
result = simplejson.load(urllib.urlopen(queryUrl))
for tweet in result:
print tweet['id_str']+" :: "+tweet['text'].encode("utf-8")+" :: "+tweet['created_at']
Note that user_timeline shows 200 results as max, but that doesn't mean you
always get that number. I found out it is safer to just query more slices of
Use: $ python > all_tweets_username
### Ways to enhance this script
* First off, this is one of the first scripts I wrote in Python, almost an "hello world" for me in this language. Coming weeks I am going to dive into Python and I hope to share my experience with you on this blog
* It is just a start to do the bulk import, an extension is needed to import new tweets via a cronjob (matching timestamp of last tweet, and append a file from that point on, etc.)
* It should except command line arguments to input the number of pages, the user, output file, etc. You see why inventing this kind of exercises for yourself, gets you up and running quickly in a new programming language ;)
* Install a database driver for python like [MySQLdb]( and prepare DB import statements to let the script directly append the data to a DB table.
### I did play around a bit more
This is one of the apps you could make with this data. I did a quick import
(just text-based, so not putting it here, because the official way to go is
really with "prepared statements") into mysql and I built a quick autocomplete
with php, jQuery etc.:
I get my tweets instantly when I start typing and when clicking on a
particular tweet, it will redirect the status page with the tweet:
![result upon click](
Git is really not that hard to learn, yet it is a powerful tool to manage
version control of your work. I learned the basics recently studying the [Ruby
on Rails tutorial]( From there [Pro
Git]( is an interesting follow-up. This post shows some
basic Git to get started.
New here? You might want to subscribe to my blog by
[email]( or
![featured image](
According to the Pro Git book, Git was designed to meet the following goals:
* It should be fast;
* It should have a simple design;
* It should have strong support for non-linear development (thousands of parallel branches);
* It should be fully distributed;
* Git should be able to handle large projects like the Linux kernel efficiently (speed and data size).
These are indeed advantages to use Git over other version control systems. It
is very fast, mainly because it works against local repositories (vs. remote
syncing for every operation).
### Initial config
After [installing Git]( there are some initial
configs you should make:
* Set your name: $ git config -global "Fname Lname"
* Set your email: $ git config -global
* To make optional aliases: $ git config -global checkout
* Choose your favorite text editor, mate in my case: $ git config -global core.editor "mate -w"
* Pick a diff tool: $ git config -global merge.tool vimdiff
* Color Git console outputs: $ git config -global color.branch auto ; git config -global color.diff auto ; git config -global color.status auto
### The basics - and maybe all you need to know for now (?)
It requires only a few basic concepts to successfully work with Git (the
following commands should be executed in the working directory) :
* Put your project under git control: $ git init ; this creates a .git directory in your project folder.
* Add files to be tracked (staged files): $ git add
If you use $ git add . , the . (dot) adds all files at once.
If you modify a file after you run git add, you have to run git add again to
stage the latest version of the file. You can exclude files by filling in the
.gitignore file in the working directory (for example to exlude logfiles in a
Ruby on Rails project fill it with: .bundle, db/*.sqlite3, log/*.log,
* Commit changes. Git takes a snapshot now: $ git commit -m "initial commit"
* Clone a (remote) git project: $ git clone git:// mygrit (where mygrit is the local directory you want to copy the project to) - $ git pull/ push / fetch / clone has much more to it, check out the Pro Git book for more info.
* Get info about files, commits, and changes: $ git status / $ git diff / $ git log ([gitk]( for graphical log viewing). The commands have a lot of switches, Pro Git shows a useful example for filtering in large projects: $ git log -pretty="%h - %s" -author=gitster -since="2008-10-01 \ -before="2008-11-01 -no-merges -- t/ -> pretty powerful!
### The real power: Git Branches
Branches allow to diverge from the main line of development. This allows
developers to work on bug fixes and new features without interfering with the
main (live) branch (master). Later you can merge branches with each other or
with the master branch. The basics are quite simple:
* Open up a new branch: $ git checkout -b testing ; $ git branch shows you all branches in the project, the single star before one of the branches is the active branch, in this case "testing" became the active branch with the "checkout" switch.
* Commit changes like normal: $ git commit -a -m "commit message of thing(s) done in branch". Again, if you have added any new files to the project since the last commit, you still have to run "git add" first.
* When done with your branch, switch back to the master with: $ git checkout master
* If happy with the results, merge the branch into the main one (master): $ git merge testing
* If the branch is not needed anymore, delete it: $ git branch -d testing
This is the easiest example of branching, there are more advanced options
possible like 3-way merging (merging various branches at once) and rebasing,
again see Pro Git for more info.
And this concludes the basics of Git, which matches the first 4 chapters of
Pro Git and the end of Chapter 1 of the Ruby on Rails tutorial. I think these
notes will cover me 95% in the following web projects I develop (alone). With
multiple developers I might need to write a follow-up post with more advanced
concepts !
### Set up your GitHub account
One thing is missing from this basic Git tutorial, setting up
[GitHub](! You typically want to push your code to GitHub
to have a backup and to allow for collaborations with other developers. Check
the "SSH keys" part when creating your account. After creating a new
repository in the GitHub GUI, pushing your code is easy:
* The following command configures the push location of your local working directory: $ git remote add origin
* $ git push origin master ; pushes the code to the remote location. Again you need to have the SSH key working to do this.
* From here on code changes sync to the remote server each time you run $ git push
### Heroku cloud deployment
The Ruby on Rails tutorial introduced me to [Heroku](
as well. I copy the steps here to push your code to Heroku:
* Install the corresponding Ruby Gem: $ [sudo] gem install heroku
* Sign up for Heroku and configure your SSH keys, then run $ heroku keys:add
* To create a subdomain on the Heroku servers, use: $ heroku create -stack cedar
Heroku return something like "Created | Git remote heroku added" and that is it!
You can also use Heroku also when developing Facebook Apps! See this [nice
For more info, check out the [Git site's documentation](http://git- as well
You have a blog and want to get a few bucks from linking to Amazon anytime you
discuss a book. Fair enough, you sign up for the [affiliate program](https
:// However if you link to various books in a
post, it becomes tedious to generate the links. I made a Javascript
Bookmarklet to fix this.
### The concept
![featured image](
[Bookmarklets]( provide one-click
functionality to a browser or web page. This is an affiliate link from Amazon: ; you see you only
need two variables:
* The [Amazon Standard Identification Number (ASIN)](, 0596527241 in this example.
* Your [Tracking ID](, bobbelderbos-20 in this case.
The first value is something you need to query, so that is why I leverage the
power of [My Reading List](, which has an
autocomplete field at the top that can locate the most common books. There is
some scripting behind it - which I will detail in an upcoming post - how I
find the ASIN from the ISBN (which Google Books API returns to me). In the
majority of cases it should find it.
#### Update after Googling:
After writing this post, I found a [nice post](
amazon-affiliate-links-a-bookmarklet/) which offers a bookmarklet to be used
on Amazon's site itself. This probably is a better approach. It finds the ASIN
with "document.getElementById('ASIN')".
### Try it
#### A. add the bookmarklet:
* In Firefox right-click on the Bookmark toolbar and click "New Bookmark"
* Give it a name, for example : amazonAffiliateLink
* Paste the following Javascript in the "Location Field" (a copy is also available at [github](
var tag = null;
var urlElement = findElementId('amazonLink');
if(urlElement == null) {
alert('No results for #amazonLink - does the ID element exist on this page?');
url = getUrl(urlElement);
if(tag == null) {
tag = prompt("Enter your Amazon tag for the URL: ", "Enter Tracking ID here");
url = url.split("=");
function findElementId(idElement) {
var urlElement = document.getElementById(idElement);
return urlElement;
function getUrl(urlElement) {
var urlHref = urlElement.href;
return urlHref;
function copyToClipboard (text) {
window.prompt ("Copy to clipboard: Ctrl+C, Enter", text);
Note that you can edit "var tag = null;" to define your tracking ID if you
want to use the same for all links. If not provided (null) it will ask you.
* Hit "Save".
#### Update 2 after Googling:
You can also store the script somewhere and append it to the body, see [this
nice article about bookmarklets on Smashing
bookmarklets-with-jquery/). It also explains how you can use jQuery to make
#### B. use the bookmarklet:
* Go to [My Reading List](
* In the search box at the top search your book and select it, it will go to a page with more details of the book.
* If the ISBN-ASIN conversion worked you see a "Buy at Amazon" link which is the affiliate link I use for that site. It is a hyperlink marked with an unique ID element called "amazonLink". That id is consumed by Javascript's document.getElementById which then manipulates the URL to replace my tracking id with the one you provided (see the code above).
* Click the bookmarklet, the Javascript will be executed.
* If you defined the tag (aka tracking id) it will use that, otherwise you are requested to put it in (see printscreen below )
* Finally you are presented with another input box with the link selected. Copy+C and you have it in on your clipboard. This is a nice trick I found at [Stackoverflow (2nd answer)](
### Printscreen process flow
![bookmarklet howto](
With an increasing amount of web devices (mobile, tablets, higher resolution
screens), web designers are faced with a challenge: how to respond well to
all? Ethan Marcotte's book "Responsive Web Design" provides a potential shift
in how we design for the web!
![featured image](
When I started to design for the web some years ago, I loved fixed width
designs. But most fixed width pages aren't easily readable on a mobile device.
New devices are invented every year and the number of pages accessed through
mobile browsers keeps increasing rapidly! So I think this book is a must read
for each web designer. The book is well written, with clear examples, and you
can probably digest the 141 pages in one or two days.
### Ingredients for a responsive design
Print and web are inherently different media, yet up until recently a lot of
sites were build as if they were for the print medium. I like the approach
Marcotte introduces: one responsive web design instead of different designs
for different devices. According to Marcotte the following 3 elements make up
for responsive web designs:
* A flexible, grid-based layout,
* Flexible images and media,
* Media queries (CSS3)
Chapters 2,3 and 4 deal with these. The last chapter provides additional info
on the responsive design cycle and progressive enhancement.
#### 1. The flexible grid (and fonts)
Before touching the grid design, we should set up the font-size to 100% as a
base and use "ems" to size text up or down. What the grid is concerned
Marcotte explains how to go from pixels to percentages based on the width
values. You can start with pixels and calculate the percentages with the
formula "target / context = result", so if a .blog div is 900px (target) and
lives in a body of 960px (context), the .blog div should be 900/ 960 = 0.9375
= 93.75%. Then for the divs that live in .blog, .blog is the context so a left
column would be 530/900, etc. The same applies for flexible margins (gutters
between blocks).
#### 2. Flexible images
A neat trick is to give the img a max-width of 100% to never cross the width
of the div it lives in. There are some issues (distorted images) with IE < 7,
so you can use AlphaImageLoader to correct this. The author further explains
interesting techniques with "overflow: hidden".
#### 3. Media queries
As of CSS2 you could define media types: all, screen, print, handheld, etc. As
this was very generic ("handheld" for iphone and older phones), W3C came up
with something better in the CSS3 spec: media queries.
Apart from the media type, media queries also detect the physical
characteristics of the devices or browsers that render our content. A typical
media query is "@media screen and (min-width: 1024px) { .. css rules .. }":
this not only requires the screen medium type, but also a viewport of at least
1024px wide, for the CSS rules to be applied.
This is awesome, because now we can target very specific conditions. You can
use the min-width and max-width conditions on a variety of features, for
example: width, height, aspect-ratio, resolution, grid and more (see page
76-78). And they can be chained together! For example: "@media screen and
(min-device-width: 480px) and (orientation: landscape) { }"
The most important point is that media queries are useful if you have the
groundwork set up well: a liquid layout, flexible fonts and images. The best
way is to follow the book step-by-step applying the examples on your own
"responsive web design".
### Some examples of responsive design
Four sites worth studying (just resize your browser window to see the amazing
effect - wow) :
* [Ethan Marcotte]( (book author)
* [The book's example site](
* [Dan Cederholm](
* [Happy Cog](
### Mobile first
A good approach these days is to start designing for the smaller mobile canvas
and progressively enhance with media queries targeting higher resolutions with
"min-width" ("resolution breakpoints"). I probably will experiment with this
approach designing future sites (Ethan convinced me!).
What about you? Let me know what experiences you have designing "responsive
web designs"
Today I wanted to share with you an awesome title I just finished reading:
[Technical Blogging - turn your expertise into a remarkable online
presence]( by [Antonio
![featured image](
[Technical Blogging](
teaches you how to gain influence and earn money from your blog. It applies to
any blog, but the examples are related to a programming blog, so ideal for me.
In 254 pages you get a lot of useful insight, with quite some technical depth.
The author has quite some experience in the field. Some tricks I did learn
already from a couple of years blogging. However, there was still a lot of
advice I could pick up to start improving my blog.
However there are two things the author cannot provide and are thus assumed:
you dedicate the necessary time and your are able to provide good content. If
you have that in order, complementing it with this book can indeed make you a
power blogger.
### and this book covers a lot!
The book covers the whole process from starting to maintaining a blog. From
the analysis/ planning stage (part I) to building it plus providing remarkable
content (part II), to promotion (part III), earning from your blog (part IV)
and finally scaling it (part V). In all parts are a lot of useful gems to
### Some things I especially liked:
* The focus on a technical blog. This was exactly what I needed. But any blogger can benefit from this book.
* The general depth of the topics covered (e.g. the SEO aspects are well addressed, understanding traffic stats, writing strategies (posting schedules, how to find ideas), the different ways to make money from your blog, etc.)
* As said, the wide scope and many directly and indirectly related topics that are useful to know, some of them provided me with new insights: engaging readers, target the right social networks (for programmers hacker news, reddit and dzone) and the ways these are different, tactics for writing good content. Even productivity tips like the [Pomodoro technique]( are explained.
* Clear examples, mostly drawn from [the author's blog]( In fact the author is very open sharing his visitor stats and affiliate earnings.
* A lot of links/ reference material.
### Conclusion and what's next
I highly recommend [Technical
Blogging]( to anybody
that maintains or wants to start a blog. I found it an inspiring read and it
will serve as a reference as I keep improving my blog.
I also did a post on blogging myself: [10 crucial steps to become proficient
at blogging](
If you want to extend this to building a business from your blog you might
also want to check out [ProBlogger: Secrets for Blogging Your Way to a Six-
Figure Income](
#### Disclaimer: I received a copy of this book from Packt to review
I finished reading a copy of [Sencha Touch Mobile JavaScript
Framework]( that Packt Publishing provided me
with. In this post a review of the book.
### What is this book about / what will you learn?
See also [packtpub](
#### Overview
* Learn to develop web applications that look and feel native on Apple iOS and Google Android touchscreen devices using Sencha Touch through examples
* Design resolution-independent and graphical representations like buttons, icons, and tabs of unparalleled flexibility
* Add custom events like tap, double tap, swipe, tap and hold, pinch, and rotate
* Plenty of well-explained sample code with the essential screenshots added in for thorough understanding
#### You will learn:
* Make use of technologies such as HTML5 and CSS3 to provide native-quality application experiences without the need for plugins
![featured image](
* Create a sample application using Sencha Touch that will run on Apple iOS and Google Android
* Efficiently use the list of components available in Sencha Touch framework libraries such as tab panels, scrollable list views, and toolbars
* Add custom touch events like tap, double tap, swipe, tap and hold, pinch, and rotate
* Discover the rich event communication that is available in every Sencha Touch component, allowing you to quickly respond to your users and create intuitive, native-quality applications
* Completely control the look of your application with Sencha Touch themes and styling options.
* Quickly put together simple components backed by the data package
* Allow your users to store information with forms, or access remote information from other services like Google maps and Flickr
* Learn about web storage features to store data offline, or communicate with online databases for richer storage options.
* Explore expert topics like syncing data and compiling applications for sale on an App store.
### My review
#### ++ Easy accessible
The entry level is low, somebody with a general idea about touch devices and
having some HTML/CSS skills, preferably also some knowledge of Javascript, can
jump in with great ease.
#### ++ Clear and concise explanations
The explanations and code examples were comprehensive and well structured (one
caveat with the code, see further down). Especially Chapter 8 was useful: it
combined the concepts building a Flickr Finder app - cool.
#### ++ Wide range of technologies make it an interesting read
Apart from Sencha Touch a lot of other technologies are introduced like
writing CSS with SASS, the Safari Error console, REST / building an API and
AJAX, PhoneGap/ NimbleKit, working in offline mode. I liked this, it gives you
some relevant context you need as a developer.
#### - - All code == Sencha Touch 1.1.0, you want to use Sencha Touch 2.x !
Here is where the book lost a bit my appetite. It uses Sencha Touch 1.1.0 and
this is not the latest release. Release 2.x is out and contains [important
changes]( The
Sencha Touch 2 Developer Preview was already presented in October last year,
yet this book came out if February 2012. I don't understand it doesn't take a
sneak preview at least as the [Facebook graph title I
development-with-graph-api-cookbook/) did with the Open Graph that was even
fresher when that book was released.
I noted that when I loaded the 2.x library files into the 1.1.0 code examples
things started to fail. This demotivated me a bit because I didn't see any use
in trying examples of an older release especially knowing that
[performance]( was the key improvement between 1.x
and 2.x. I guess this is inherent to writing a book about any software topic,
but I had expected a bit more here, knowing that Packt books are heavily
focussed on practical code samples.
#### ++ Overall: good book
That aside, as I already stated the material was presented in a clear and wide
scope. It was an interesting and joyful ride to get familiar with the Sencha
Touch framework. After reading I can clearly see this is a good approach to
mobile web development. I am looking forward to dive into the release 2
documentation soon to actually build something myself.
### More info
You can read more about the book [here](
Please let me know in the comments if you have any experience and/or example
apps built with Sencha Touch.
I have further tweaked the [My Reading List](
Facebook app. In this post a quick tour with printscreens of its new features.
![featured image](
This has been promoted to one of my favorite web/programming projects now :)
The Google Books API does a great job in providing rich content for a wide
range of books. With the integration of the Amazon customer reviews and the
info My Reading List users are adding, it is becoming a powerful tool to share
your reading and inform yourself about potential next reads and/or book
purchases. With this new release, it is easier to find and add new books to
your list. You can find much more info per book. And the overall look and feel
have been improved as well. A quick tour what is new:
### 3-way autocomplete
When you search a book from the search box in the header you get a result
devided in: 1. books already added and by who, 2. books not added but found by
the Google Books API with a link to add them, 3. matching usernames for the
search string sorted descending on the number of books added:
![autocomplete gives 3 results back](
### The "adding book" autocomplete search box
is always visible now. It was hidden behind an "add book" button. This was
not necessary so I made it always visible at the top of the homepage:
![add book always visible](
When you select a book from the autocomplete a form slides down to add it to
your reading list:
![when selecting a book](
### Edit everywhere
Instead of one edit place, the app shows you click-to-edit buttons (nice
[jQuery plugin](
jeditable/)) everywhere it detects a book from you:
![edit everywhere](
### Easier to find your books
After some time you can build up quite a reading list. When you click on "Edit
Books", you get an overlay with your books. At the top you find a filter box
that, when you start typing, show you matching titles on the fly:
![edit iframe - filter box](
### New book pages
Each book has a page with more info than before, apart from the Amazon
reviews, this data comes from the Google Books API:
![book pages iframe](
And as more people start adding books, sometimes you see multiple reviews from
My Reading List users for the same book (Steve Jobs bio as an example here)
![what my reading list users say about the book](
### Similar books
Google Books API can be queried for similar titles, that is what I integrated
further down the book pages (example for a book about Git version control
![similar titles](
### Easily add books to your reading list
from the book page, if you click the "Add to My Books" button, you get an
overlay identical to the "add book" form we saw before. When you add it with
or without review, the overlay closes, the page refreshes and you are linked
to that book.
![add book from page](
![overlay to add book](
### Amazon reviews
Last but not least, and one of my favorites: integration of Amazon customer
reviews. I really wanted this feature to be integrated! Today I had some time
to check out the Amazon Product Advertising API. It had some nice technical
challenges which I will dedicate another blog post to. If the app manages to
get an iframe URL with reviews it is visible on the books page. There is also
a link to the Google Books reviews (different sources is a good thing!) and a
link to buy the book at Amazon:
![amazon reviews are there (YES)](
### Where is it?
As mentioned [in my last post](
my-reading-list-v1-1/) I put My Reading List on its [own
domain]( now. You can subscribe to updates by
following the [My Reading List Facebook
page]( All new titles that get
added are streamed to [Twitter](!/fbreadinglist) as well.
### Previous blog posts
If you have doubts or questions, or you just want to know more about this app
and its feautes, I have been blogging about it for a while now (descending
* 2012/05 > [Release notes My Reading List v1.1](
* 2011/09 > [My Reading List Widget](
* 2011/09 > [My Reading List: load Google book preview in overlay](
* 2011/09 > [Nice Jquery plugin: Jeditable](
* 2011/03 > [Hi folks, what are you reading? Share it on My Reading List](
I picked up development again for one of my favorite projects: [My Reading
List]( In this post the new features for current
release 1.1
### New features / improvements
![featured image](
* My Reading List is now hosted on its own domain: []( . All links should be redirected (301) to the new domain
* User data: no email storage in the My Reading List database, I only use the email once (from the Facebook session) to send a welcome email with links and as an option for the user to provide input (early user feedback is really valuable)
* <del>Post to FB wall is opt-in instead of opt-out. Although it was always clearly visible in the "add book" form, I think it is better to let users actively toggle the select box when they want to post</del>. - It is opt-out in the end, because I think one of the purposes of this app is to sharewhat you read, so the FB wall post is something to encourage. However, it is clearly visible when adding books (see below), you are in control!![](
Moreover FB built in some granularity it didn't have before: now you accept a
FB app, and you have to explicitly accept the "post to wall" permission as a
2nd step.
* Slight changes in the querying of the Google Books API: putting a % instead of a + between the words yielded me books I couldn't find earlier, even putting % before and after the keywords helped, I also cropped duplicate titles, because the first hit is usually the book (googleID) with the most reviews/data, so that is the one you want users to grab from the autocomplete.
* URL txt-to-html conversion in the book comments ([for example]( )
* See same URL for improved share buttons, like my blog they take you outside of the page to sharer URLs where you have more options to customize your sharing.
* Better og: properties to share books with their appropriate thumbnail.
* The [Facebook Canvas App]( is back.
* Some small cleanups in the design, but overall I left it the same, because I think it is simple and clean. It also fits perfectly in the smaller width of the FB Canvas.
### Next
* I'd love this to be available on an iphone and/or ipad!
* Provide / link more info for each book. It would be great to fetch Amazon reviews. Personally I use those a lot deciding whether to read and/or buy a new book.
[Beautiful Soup]( is a Python
library to do screen scraping. I think it is a powerful tool which can be used
in many situations. See
[here]( for examples
where it is used. In this post I will show you two examples how to crawl
websites using this library.
### Get started
![featured image](
Best way to start is to download [the latest
source]( and
start playing with the many examples from the [documentation
page]( In this post I
will scrape the paging of a Dutch tv program site. In the second example I
scrape the data of top ranked artists. Another interesting one would be to
replicate the Facebook sharer.php which shows you different thumbnails found
on the page you want to share. Reddit [uses BeautifulSoup](
reddit/reddit/blob/master/r2/r2/lib/ for this as well. Maybe a nice
exercise for you?
### 2 practical examples
As you can read and practice yourself with above links, without further ado
the two examples:
#### 6 lines to get all RSS feeds of
One of my future plans is to build an iphone app to see programs from Dutch
television. I couldn't download the existing "uitzendinggemist" app because
Apple only gives you access to one store per credit card, mine is Spain. I
didn't actively look for a workaround, however this limitation is actually
good to start building something myself. I will need to leave it for the
future due to current work, but the below example gets me at least all RSS
feeds which are hidden behind a paging navigation of 93 pages, see [http://www](
#! /usr/bin/env python
import urllib
from bs4 import BeautifulSoup as Soup
base_url = ""
program_url = base_url + "/programmas/?page="
for page in range(1, 93):
url = "%s%d" % (program_url, page)
soup = Soup(urllib.urlopen(url))
for link in soup.find_all(attrs={'class': 'knav_link'}):
print link.get('title').encode("utf-8")," :: ",
print "%s%s.rss" % (base_url, link.get('href') )
* For version 4 the import statement is : from bs4 import BeautifulSoup as Soup
* soup = Soup(urllib.urlopen(url)) -> holds the whole page
* the for loop retrieves all elements with the "knav_link" class (you should look at the HTML source while coding) and gets the title and href attributes.
#### Get details about top ranked artists
This example is a little bit more challenging because we have to do more
parsing on the HTML. See [
artists/]( we
want to get the name, twitter/facebook page, number of likes/followers of top
ranked artists. Moreover, we want to save that data into a database table.
#! /usr/bin/env python
import urllib
from bs4 import BeautifulSoup as Soup
from time import time
import MySQLdb
db = MySQLdb.connect("localhost","bob","cangetin","bobbelde_models" )
cursor = db.cursor()
url = ""
soup = Soup(urllib.urlopen(url))
for row in soup.find_all(attrs={'class': 'row'}):
artist = [text for text in row.stripped_strings]
name = artist[1]
followers = artist[5]
likes = artist[7]
thumb ="img")[0]['src']
twitter ="a")[0]['href']
facebook ="a")[1]['href']
tstamp = int(time())
sql = """INSERT INTO top_ranking (id, name, followers, likes, thumb,
twitter, facebook, audit_who, audit_ins, audit_upd) VALUES
(NULL, '%s', '%s', '%s', '%s', '%s', '%s', 'admin', '%d', NULL);
""" % (name, followers, likes, thumb, twitter, facebook, tstamp)
* We loop through all elements with class "row" which are the table rows in this case
* artist = [text for text in row.stripped_strings] -> strips away all html and leaves us with bare text only. This gets us almost everything except the thumb "img src" attribute and the Twitter and Facebook URLs. Hence the extra"..")[0]['...'] statements. This does the job, but I expect that a BeautifulSoup ninja would use less code to get this :)
* We concat all together and execute the insert statement. See [this article]( to get started with MySQLdb.
### More examples?
I hope you enjoyed this post. If you have interesting use cases yourself, I
invite you to share them in the comments
Last week I finished [my new portfolio design]( It
shows some websites I have built the last couple of years. As my blog I
integrated bamboo in the design. There is a light and dark theme. And most
important it is responsive: it is compatible with the iphone and ipad.
Before designing [this portfolio site](, I used a
[WordPress plugin]( which
was pretty good. The downside was that I could not fully control the look and
feel of your portfolio. That's why I designed something myself!
I started to take screenshots with [this nice command line
Then I used the [HTML5 boilerplate]( and
[reset.css]( to get optimal
defaults. I use both tools for every site now, and it is really prevents
headaches later on in the process. Big thanks to the developers of these
The fetching of the works is done with PHP and a MySQL Database backend.
### Functionality
![featured image](
* Click on a thumbnail and you can browse through a carousel of all the works (done with [Fancybox](
* Under each work is a link to its page which is a slug based on the website's URL (for example Clean URLs is another practice I use often and are done with a simple RewriteRule in .htaccess. On the work's page you can read about the site, browse to the next/previous project, etc. It also links to any related blog if it exists. Examples: [Friends Jukebox]( or [Globe Explorer](
* In the footer there is a Feedback link that slides a Facebook Comment box open when clicked. You can also change the theme from: [light]( to [dark]( or vice-versa in the footer of the site. This sets a session variable and/or cookie to remember the theme you have selected. I first had the themes toggle each day when you'd enter the site, but I decided that I liked the light one better, so I left that as the default.
### Media queries
As [discussed earlier on my blog](
shift-read-responsive-web-desig/) media queries can be quite powerful. I only
made basic use of them, yet with sufficient result: the portfolio site
supports ipad and iphone! It is done by targeting different stylesheets upon
detection of the device width of these devices:
<link rel="stylesheet" href="css/iphone.css"
media="only screen and (max-device-width: 480px)" type="text/css">
<link rel="stylesheet" href="css/ipad.css"
media="only screen and (min-device-width : 768px) and (max-device-width : 1024px)"
These stylesheets then overwrite / add styles on top of the main styleseet.
Below you can see some printscreens how the site scales on different devices:
### 1. Benefit screen estate: the Desktop
You see that the wider your screen the more works you see. This is done with a
simple left float.
![portfolio desktop picture 2](
Homepage resizing the browser:
![portfolio desktop picture 3](
This is the experience of looking at a work's site:
![portfolio site picture](
You see the description is at the left side, where on mobile devices it moves
on top of the image.
### 2. Go smaller but mobile: Ipad
Ipad has two columns per default on the homepage.
![portfolio - ipad - picture 2](
On the work pages you see the text scales above the picture:
![portfolio - ipad - picture 1](
### 3. In your pocket, but be compact: Iphone
On the Iphone the homepage has a single column of works:
![portfolio iphone picture 1](
Secondly on a work's page the text is also placed above the image. Note that
also the header is smaller to gain screen estate:
![portfolio iphone picture 2](
Also note that the header has a fixed position so content scrolls behind it.
See [here](
header-with-css/) how I did this. It is one of the details I like most about
this design.
Let me know if you have any comments or suggestions. A followup plan could be
to extend this site to let:
* users create their own portfolio site, potentially with a subscription option to have your unique URL at []( A challenge is how to process portfolio images, you take them with a tool like [webkit2png]( which generates pretty heavy images, or you let your users upload their images. I think the latter is most practical in the end. I had to manually optimize the images for the web, because they were taking too much time to load.
* users edit the default design with a theme builder so that they can leverage the power of this layout but with customized banners, colors, fonts, etc.
#apc #api #books #chrome #computer #facebook #fbinstant #github #google #html
#ipad #markdown #php #tools
** Note I stopped including the Twitter JS ( to avoid too much clutter and shave off some KBs. Curious about the styling? Here is the CSS which fits much better with my blog's theme:
blockquote { clear: both; float: left; width: 640px;
background-color: rgba(100, 123, 82, .1); color: #7992A7;
border-radius: 7px; -webkit-border-radius: 7px; -moz-border-radius: 7px;
border-left: 5px solid rgba(100, 123, 82, .5);
border-bottom: 1px solid #ddd; margin: 10px 5px; padding: 10px; }
blockquote p {padding: 0px; margin: 0px;}
blockquote p a {text-decoration: none; color: #cc6600 }
blockquote p:nth-child(2) {font-size: 75%; float: right; padding-top: 5px; }
blockquote p:nth-child(2) a { color: #cc6633; }
### Anyways this week's tweet digest
> RT [@adulau:]( What a beautiful day? "Piping
Python Through Pipes" []( a nice and
clean replacement to "perl -e"
> Bob Belderbos (@bbelderbos) [April 02,
> RT [@darrencauthon:]( Clean Code is
still available for $10 ([]( This
book is one of the biggest influences on my caree
> Bob Belderbos (@bbelderbos) [April 02,
> []( Featured Interview: Uncle Bob
Martin - 4 important [#computer]( science
> Bob Belderbos (@bbelderbos) [April 02,
> facebook graph api search rules - Stack Overflow
[#api]( AND searches are possible, nice
> Bob Belderbos (@bbelderbos) [April 01,
> RT [@AkitaOnRails:]( This is the most
hilarious lightning talk ever. If you haven't seen it, you should
[]( by Gary Bernh
> Bob Belderbos (@bbelderbos) [April 01,
> Watch Paper by FiftyThree on Vimeo!
> Bob Belderbos (@bbelderbos) [April 01,
> [#fbinstant]( Facebook Instant Search!
Searching Facebook In RealTime !!!
[]( via
[@TeckZoneIn]( (via
> Bob Belderbos (@bbelderbos) [April 01,
> Nice April Fools from Chrome: [](
(via [@boagworld)]( ROFL
> Bob Belderbos (@bbelderbos) [April 01,
> [#google]( April Fools joke: Nintendo-
style 8-bit Google Map via [@gmanews](
[]( - haha cool
> Bob Belderbos (@bbelderbos) [April 01,
> A Basic [#HTML5]( Template
[]( via
> Bob Belderbos (@bbelderbos) [March 31,
> Video Zuckerberg's `Hacker Way' to Guide
> Bob Belderbos (@bbelderbos) [March 31,
> How much is each [#Facebook]( user
worth? []( via
[@tnwsocialmedia]( - you are not their
client, you are their product
> Bob Belderbos (@bbelderbos) [March 31,
> how to cache the twitter api result?