List of Favorite OS X Web Developer Apps

The following tools are either open source, shareware or freeware. For shareware, I will post freeware alternatives.DEVELOPMENT* JetBrains RubyMine (for Ruby and Rails development)

* Charles Proxy
* PHP xdebug with remote_enable = true

* SourceTree (if you use git)
* Cornerstone (if you use svn)

* iTerm2
* Zshell with Oh My Zsh
* BetterSnapTool
* Mou (Markdown Editor)
* Tabs Outliner extension for Chrome

* LittleSnitch
* Skitch
* Dropbox
* SuperDupr! or Time Machine
* RescueTime
* Solarized Theme (for shell, TextMate, etc.)

Remote repository hosting

A dedicated IDE or Editor. Pimping Your IDE will be in another post. Here are my most used editors/workflows (not in any specific order. Usually the right one for the job.)

MacVim with solarized (light) BG but I’m constantly tweaking the colorscheme. Font is one of Anonymous, Source Code Pro, Consolas, Inconsolata, all patched for powerline (optional.)

I use NeoBundle to manage my vim packages. Here are my current bundles:

" After install, exec ~/.vim/bundle/vimproc, (n,g)make -f your_machines_makefile</pre>
NeoBundle 'Shougo/vimproc'
NeoBundle 'Shougo/vimshell'
NeoBundle 'kien/ctrlp.vim.git'
NeoBundle 'honza/writer.vim'
NeoBundle 'vim-ruby/vim-ruby'
NeoBundle 'plasticboy/vim-markdown'
NeoBundle 'juvenn/mustache.vim'
NeoBundle 'tpope/vim-rails'
NeoBundle 'tpope/vim-fugitive'
NeoBundle 'tpope/vim-unimpaired'
NeoBundle 'tpope/vim-abolish'
NeoBundle 'tpope/vim-surround'
NeoBundle 'tpope/vim-bundler'
NeoBundle 'tpope/vim-surround'
NeoBundle 'tpope/vim-characterize'
NeoBundle 'tpope/vim-haml'
NeoBundle 'tpope/vim-rake'
NeoBundle 'Lokaltog/vim-easymotion'
NeoBundle 'jeetsukumaran/vim-buffergator'
NeoBundle 'tilljoel/vim-automatic-ctags'
NeoBundle 'git://'
NeoBundle 'altercation/vim-colors-solarized'
NeoBundle 'vim-scripts/Vim-R-plugin'
NeoBundle 'vim-scripts/R-MacOSX'
NeoBundle 'vim-scripts/vim-mou'
NeoBundle 'vim-scripts/ShowMarks'
NeoBundle 'vim-scripts/dbext.vim'
NeoBundle 'vim-scripts/Txtfmt-The-Vim-Highlighter'
NeoBundle 'vim-scripts/mru.vim'
NeoBundle 'vim-scripts/YankRing.vim'
NeoBundle 'majutsushi/tagbar'
NeoBundle 'mileszs/ack.vim'
NeoBundle 'ervandew/screen'
NeoBundle 'L9'
NeoBundle 'Gundo'
NeoBundle 'mattn/zencoding-vim'
NeoBundle 'scrooloose/nerdtree'
NeoBundle 'scrooloose/nerdcommenter'
NeoBundle 'jistr/vim-nerdtree-tabs'
NeoBundle 'nathanaelkane/vim-indent-guides'
NeoBundle 'git://'
NeoBundle 'git://'
NeoBundle ''

Tmux + vim

Send and Receive POST Requests with PHP

This can be used to build an API or generally have different sites and scripts communicate externally.

function postback($url, $params, $optional_headers=null, $decode=true) {
$params['handshake'] = HANDSHAKE;
 $params['url'] = $url;
 $params['remote_ip'] = $_SERVER['REMOTE_ADDR'];
 $data = http_build_query($params);

$options = Array('http' => Array(
 'method' => "POST",
 'header'=> "Accept-language: en\r\n".
 "Content-type: application/x-www-form-urlencoded\r\n",
 "Content-Length: " . strlen($data) . "\r\n",
 'content' => $data));

if ($optional_headers !== null) {
 $params['http']['header'] .= $optional_headers;

$ctx = stream_context_create($options);
$fp = fopen($url, 'r', false, $ctx);
 if (!$fp) throw new Exception("Problem with URL '$url' {$php_errormsg}");

$response = stream_get_contents($fp);
 if ($response === false) throw new Exception("Problem reading data from $url, $php_errormsg");

 if ($decode) $response = json_decode(rawurldecode($response), TRUE);

// Invalid query?
 if (!isset($response['handshake']) || ($response['handshake'] !== HANDSHAKE))
 return NULL;

return $response;


Bulk Upload Images as Simple Products in Magento

The client has thousands of images, each of which is a (simple) product they want in Magento. Each image is named the associated product’s SKU number with a JPG extension. I began by using a recorded Batch script in Photoshop to resize all the images to reduce filesize and make the image more web-friendly.

The following Ruby script generates a Magento Product CSV file from JPG images in a directory. It is pasted as-is.

#!/bin/env ruby

require 'rubygems'
require 'csv'
require 'htmlentities'
require 'uri'

def main
    #Dir.glob("media/products/#{catname}/*").each_with_index do |imgpath, i|

    # Magento 1.7 CSV product headers
    csv_headers = '"store"	"websites"	"attribute_set"	"type"	"category_ids"	"sku"	"has_options"	"name"	"meta_title"	"meta_description"	"image"	"small_image"	"thumbnail"	"url_key"	"url_path"	"custom_design"	"page_layout"	"options_container"	"image_label"	"small_image_label"	"thumbnail_label"	"country_of_manufacture"	"msrp_enabled"	"msrp_display_actual_price_type"	"gift_message_available"	"price"	"special_price"	"weight"	"msrp"	"status"	"visibility"	"Featured"	"Deal"	"Hot"	"enable_googlecheckout"	"tax_class_id"	"is_recurring"	"description"	"short_description"	"meta_keyword"	"custom_layout_update"	"news_from_date"	"news_to_date"	"special_from_date"	"special_to_date"	"custom_design_from"	"custom_design_to"	"qty"	"min_qty"	"use_config_min_qty"	"is_qty_decimal"	"backorders"	"use_config_backorders"	"min_sale_qty"	"use_config_min_sale_qty"	"max_sale_qty"	"use_config_max_sale_qty"	"is_in_stock"	"low_stock_date"	"notify_stock_qty"	"use_config_notify_stock_qty"	"manage_stock"	"use_config_manage_stock"	"stock_status_changed_auto"	"use_config_qty_increments"	"qty_increments"	"use_config_enable_qty_inc"	"enable_qty_increments"	"is_decimal_divided"	"stock_status_changed_automatically"	"use_config_enable_qty_increments"	"product_name"	"store_id"	"product_type_id"	"product_status_changed"	"product_changed_websites"'

    path = "output.csv"
    open(path, 'w') { |f| f.puts csv_headers } 

    Dir.glob("*.JPG").each_with_index do |imgpath, i|
        image_name = File.basename(imgpath)
        image_url = "/#{image_name}"
        sku = image_name[0,image_name.size-4]
        name = sku
        desc_short = sku
        desc = sku
        csv = '"default"	"base"	"Default"	"simple"	"2"	"SKU"	"0"	"NAME"	"META_TITLE"	"META_DESC"	"IMAGE"	"IMAGE"	"IMAGE"	"url-key-SKU"	"url-key-SKU"	""	"No layout updates"	"Block after Info Column"	""	""	""	" "	"Use config"	"Use config"	"No"	"1.0000"	"China"	"1.0000"	""	"Enabled"	"Catalog, Search"	"0"	"0"	"0"	"Yes"	"None"	"No"	"LONG_DESC"	"SHORT_DESC"	"META_KEYWORDS"	""	""	""	""	""	""	""	"10000.0000"	"0.0000"	"1"	"0"	"0"	"1"	"1.0000"	"1"	"0.0000"	"1"	"1"	""	""	"1"	"0"	"1"	"0"	"1"	"0.0000"	"1"	"0"	"0"	"0"	"1"	"NAME"	"0"	"simple"	""	""'
        csv.gsub!('SKU', sku)
        csv.gsub!('NAME', name)
        csv.gsub!('LONG_DESC', desc)
        csv.gsub!('SHORT_DESC', desc_short)
        csv.gsub!('META_TITLE', sku)
        csv.gsub!('META_DESC', sku)
        csv.gsub!('META_KEYWORDS', sku)
        csv.gsub!('IMAGE', "#{image_url}")
        open(path, 'a') { |f| f.puts csv } 

    p "Finished."



#require 'yaml'
#require 'hpricot'
#require 'tempfile'
#require 'mechanize'
#require 'open-uri'
#require 'highline/import'
#HighLine.track_eof = false

class Importer
  def initialize
    CSV.foreach('/Users/bluish/Desktop/export_ish.csv') do |row|
      p row.inspect
    return'/Users/bluish/Desktop/export_ish.csv', 'wb') do |csv|
      p 'injecting csv'
      csv << ['123', '', '456'] 

class String
  def decode

  def encode
def saveImage(remote_url, thumb_url, dir='./media/import')
  require 'open-uri'
        base = File.basename(remote_url)
        img = "#{dir}/#{base}"
        unless File.exist? img
          p img
          open(img, 'wb') do |file|
            file << open(remote_url).read
        thumb =  "#{dir}/thumb-#{base}"
        unless File.exist? thumb
          p thumb
          open(thumb, 'wb') do |file|
            file << open(thumb_url).read
        p "GOT MEDIA"

       p "FAILED to grab #{remote_url}"
       p "or #{thumb_url}"
       p 'Possible 404? continuing...'


Add Items to Category Programmatically in Magento

The code on the bottom will take the following array:

$categories = array(
#   "nickname" => category_id,
    "Eyeliner" => 107,
    "Lipstick" => 108,
    "General" => 18,
#   etc.

The $lineItems array contains a line of category+sku-items in the format “CategoryNick SKU1 SKU2 SKU3.” CategoryNick is matched against the $categories array above to figure out the Magento category ID. If the product is already in the category, it doesn’t do anything. The product will retain the categories it is already associated with rather than dropping the existing category associations.

$lineItems = Array();
$lineItems[] =     "Eyeliner 7897";
$lineItems[] =     "Eyeliner 7898 7772 7771 7770";
$lineItems[] =     "Lipstick 7909-15 7909-16 7939 7941 7984";
$lineItems[] =     "General 7940"; 

This is the full code. It should be placed in a /filename.php file and visited at /filename.php – Make a backup of your database before using this (and daily!) Don’t keep executable PHP files hanging around after use. If you need to, make sure the permissions are 600 or 700 and NOT world-writable or rename to .phps so they aren’t executable by web

define('MAGENTO', realpath(dirname(__FILE__)));
require_once MAGENTO . '/app/Mage.php';

# assign duct tape to all categories
function FindBySku($sku) {
    return Mage::getModel('catalog/product')->loadByAttribute('sku', $sku);

function AddToCategory($product, $newcat, $save=false) {
    $cats = $product->getCategoryIds();

    # Don't double categories
    if (in_array($newcat, $cats)) return;

    $cats[] = $newcat;

    if ($save) return $product->Save();

# 3 categories nicknamed and linked to respective Magento IDs
$categories = array(
    "Eyeliner" => 107,
    "Lipstick" => 108,
    "General" => 18,

# Add 5 items to the Eyeliner category (107), 5 to Lipstick and 1 to General.
$lineItems = Array();
$lineItems[] =     "Eyeliner 7897";
$lineItems[] =     "Eyeliner 7898 7772 7771 7770";
$lineItems[] =     "Lipstick 7909-15 7909-16 7939 7941 7984";
$lineItems[] =     "General 7940"; 

foreach($lineItems as $line) {
    $pieces = explode(' ', $line);
    $cat = $pieces[0];

    #$skus = implode(' ', $pieces);
    #$skus = substr($skus, strpos($skus, ' ')+1, strlen($skus) - strlen($cat));
    # $cat is the category NickName and $skus is a space-delimited list of SKU #s

    $skus = array_slice($pieces, 1);
    # $cat is the category NickName and $skus is an array of SKU numbers

    if (array_key_exists($cat, $categories) && $categories[$cat] != 0) {
        $cat_id = $categories[$cat];
        echo "Adding to category $cat (id = $cat_id)\n";
        echo '\tAdding ' . sizeof($skus) . " SKUs\n";
        #$item_skus = explode(" ", $skus);
        $i = 0;

        foreach($skus as $sku) {
            $product = FindBySku($sku);
            if (!$product) {
                echo "** ERROR: NO PRODUCT FOUND SKU=$sku\n";
            AddToCategory($product, $cat_id, true);
            echo "\tadded $sku to $cat ";

        echo "\nTOTAL $i/" . sizeof($skus) . " items to $cat\n\n";
    } else {
        echo "** ERROR: CANNOT FIND CATEGORY '$cat'\n\n";


Using Rsync to Copy Remote Files to Local Machine on OS X / Linux

The following is used to copy files from a remote server to your local machine using Rsync to keep both synchronized. This is useful for things like backing up important directories on your web server to your external hard drive or elsewhere.

Rsync can incrementally synchronize files between two locations whether local or remote. This means only the updated files on your remote server are updated on your local machine.

From here on in I assume you’re using OS X but this should work on Linux as well. Press CMD+Space to open a Terminal (I use TotalTerminal.) At the command prompt, just use the following:

rsync -a -e "ssh" --rsync-path="sudo rsync -vau " .
rsync -a -e "ssh" --rsync-path="sudo rsync -vau " .
rsync -a -e "ssh" --rsync-path="sudo rsync -vau " .

This will copy /home, /var, and /etc from your remote machine to the current directory.

Installing Alan Storm’s LayoutViewer in Magento (works with 1.7!)

I use the debug toolbar but nothing beats Alan Storm’s configviewer and layoutviewer. Unfortunately the only archive of this on Alan’s site is missing a module config file to activate it. Below is a simple shell script to download, install and activate the LayoutViewer in any version of Magento. I can confirm it works flawlessly in Magento 1.7. If you’d like to manually install this module, read the “manual installation” section below.




# If in root, go to app/code/local
if [ -f "index.php" ]; then
    cd app/code/local;

echo "Using curl to download Magento LayoutViewer"
curl -so - $VIEWER_HTTP_DOWNLOAD | tar xvzf -

echo "Writing app/etc/modules/ config file"
cat <<'ConfigFile'
<?php xml version="1.0"?>
) > ../../etc/modules/Alanstormdotcom_Layoutviewer.xml

echo "Done. Visit any page with ?showLayout=page"

Save this to a file and run “sh” in your Magento root.

Manual Installation

Download the MagentoLayoutViewer and extract the Alanstormdotcom folder to [magento-root]/app/code/local/

Create a new config file in app/etc/modules/ named Alanstormdotcom_Layoutviewer.xml  and paste in it the following:

<?php xml version="1.0"?>

Done. See usage below.

Module Usage

Visit any URL with ?showLayout=page (or handle or package) to retrieve the layouts XML

The module also accepts a showLayoutFormat=text argument if you’d like plain text instead of XML.

Example: http://my-store.cxm/product/123?showLayout=page&showLayoutFormat=text

This vs That — Be Tech Agnostic

Linux (Ubuntu) vs Windows 8 vs OS X
iOS vs Android vs Windows Mobile vs Whatever …

I’m not a “frontend developer.” That’s not my job. I’m a “backend” developer” and I only code PHP and MySQL on OS X. <-- Move away from this mentality and be more accepting of other technologies. Investors and businessmen don't care so much what's under the hood, and neither should you.

Don't limit yourself to a single OS or platform. Dabble in open source and use Linux*, OS X and Windows. Learn WordPress, Joomla, Drupal and Magento. Learn Django, Rails, Symfony and Ruby, Python and PHP. If you're wondering where to start, it's not the tools that matter, but how you use them. In IT, you'll always find a layer of abstraction above where you are right now. There's always a right tool for the job. Taking a top down approach, see the goal and work backwards on how to achieve it. It's true that if you know one programming language, you know them all, and even if you aren't as efficient with one language compared to another, those are "problems" that get streamlined early on.

Don't tie yourself down to an OS. Use whatever works for you. I don't have a "main" workstation. I have a Macbook Pro when I'm on the go and usually at home. I have Ubuntu and Windows on a desktop and Android on my phone. I am a developer. An engineer. I can code for all of these platforms and there's no reason to coerce yourself into one niche.

Zoom out and take note of the technologies and skills you've accumulated under your belt. These are all labels (i.e., a “wordpress developer” is or at least should be a PHP developer) Yes there are bad programmers and good programmers, but the difference isn’t in the code anymore. Working in IT means always being able to adapt to changes in trends. Make your work your sandbox. Don’t bullshit anyone, but don’t deny what you’re capable of either. Create.

See yourself as a problem solver and tackle challenges accordingly. Use your developer skillset to keep the wheels in motion. What makes programming worthwhile? Why reinvent the wheel when you can move the damn thing forward a millimeter or two?

Quake Live Tips

Quake Live is a free, manly game to play. QL is a version of Quake 3 that runs as a browser plugin for Firefox, Safari, and IE. It features a skill-matched game finder, a friend’s system, and other modern features. Think a Lite, browser-based version of Steam. Quake 3 came out in 1999, and people have been playing it on a regular basis since. That’s about 11 years ahead of you if you’re new (doesn’t mean you can’t become excellent fast.)

The following Quake Live tips apply to those games: Quake 3, Quake World, Death Match Classic, Warsow, etc.

Quake Live Tips – The Basics

The point of the game is to control the map, not to get the most frags. Kills happen because other players are trying to take over your territory.

Don’t chase your opponent. Chasing is predictable, and will almost always get you killed if you don’t know what you’re doing.

Pay attention to your opponent. What items are they picking up? Their usual routes (surprisingly predictable,) etc.

Pay attention to what your opponent does when their health is low. They will either become very aggressive and erratic, or change their route and generally keep their distance from you.

Learn to strafe jump. It’s not as easy as in Counter-Strike <1.1, but it’s a skill that you can carry into other shooters (listed above), and is a core part of the game. Quake Live makes this easy with a movement practice mode and video tutorials.

Use a low sensitivity. Mine is 1.5. It’s more accurate, and you quickly learn how much force to give the mouse to flick the crosshair if you need to. You don’t need to do 180s and 360s. Once you become good, you will know where opponents are likely to come from, and have the crosshair always in that general direction.

Use ASDW to move, and bind every weapon nearby. I use R for rocket, Q for rail, E for lightening, F for shotgun, etc.

Learn the maps. Duels are a great way to learn maps. You can also learn a map by deciding on a specific item route to follow, and then following it until you’ve memorized it. Then memorizing another route, and so on. For example, rocket to RA to rail to MG to shotgun to rocket to RA ….

Use the right gun for the job. QL/Q3 has the most balanced arsenal in a shooter. Every gun including the machine gun and gauntlet are useable and very powerful. In some situations a machine gun is better than a rocket, such as when the opponent is very far away.

Quake Live Tips – Beyond Basics

Learn the amount of seconds each item takes to spawn. Then learn to countdown internally exactly when that item will respawn. This isn’t as hard as it sounds. Begin by only focusing on big items such as Red Armor (RA), Mega Health (MG), etc. You have to control the map (items), and also be able to spot what your opponent has, and both require that you know when items were taken, and/or when they will respawn. You can make the timer count up or down via

Control important items. Focus on controlling at least RA, MG, rocket and rail.

Try to predict what your opponent has equipped. There’s a good chance your opponent will have equipped whatever items were in the immediate vicinity. If they’re walking out from near the lightening gun (LG), they probably have the LG out. This is especially true in pubs and when you know your opponent just spawned or did not pick up a better gun. If there’s a common route, like from rocket to RA, and you know the RA had spawned recently, your opponent likely has RA with rockets a’blazin’

Fire at spawn points. QL has a very low invincibility time when you respawn, and you can die again right away. Memorize the spawn points in each map, and shoot at them when you expect a respawn. If you frag someone, and you know there’s a spawn point behind you, turn around and begin shooting. If they respawn at that point, they’ll be welcomed back with a rocket or balls of plasma.

Watch demos (replays.) Get the Firefox demo player and hop over to ESReality.

You have to actually play the game. Playing is how you get good. I put this tip at the end because people who don’t read up to this point, who will close this page and go play, don’t need the tip. People who read through this entire page are more likely to also be the type of person looking for a shortcut to becoming pro at QL – there are no shortcuts. You have to play the game, and enjoy every loss. Experience comes from playing.

Quake Live Links

Quake Live – Official Site

List of command variables