Connecting to The Cloud Free WiFi Service in the UK

Problem: Unable to connect to The Cloud free wifi for the first time. Browsers spin till they time our and give you an error about DNS.

Context: The Cloud is a free wifi service in the UK at various coffee shops including Cafe Nero.

According to the instructions it is as easy as 1, 2, 3 to connect and signup. Even the brochure’s Mac trouble shooting basically says, “Just connect”. But for me it never was able to connect.

Looking in the console there were errors like this: “5/27/14 1:34:47.149 PM sandboxd[463]: ([598]) ntpd(598) deny file-read-data /private/var/run/resolv.conf”

This does provide a clue, because resolve.conf is basically the file that holds a list of name servers to use for DNS.

Solution: Clear out any custom set DNS name servers in your Advanced network settings.

I had configure my DNS settings to use Google’s DNS, namely 8.8.8.8 8.8.4.4. If you clear this out then The Cloud can set the name servers to what it wants them to be.

Tips For Lowering Indention Levels

Linus Torvalds, the creator of linux, sets his tabs/indentations to 8 spaces. Personally that seems a little wide, but he says some interesting things about why shorter isn’t necessarily better. Me, I’ll stay with 4 because it just looks right. But you still shouldn’t have too many indent levels.

“If you need more than 3 levels of indentation, you’re screwed anyway, and should fix your program.” – Linus Torvalds, Linux Kernel Coding Style

Use Continue

Example:

for item in sequence:
     if is_valid(item):
          if not is_inconsequential(item):
               item.do_something()

Becomes:

for item in sequence:
     if not is_valid(item):
          continue
     if is_inconsequential(item):
          continue
     item.do_something()

Notice you have to flip the values of your if statements making this change. This only works inside a loop and if your language has continue.

Factor Out A New Method

This is true for a lot of things. As I recently heard if a method starts to get over 10-15 lines it starts to feel like maybe I’m doing something wrong.

If your indents are starting to get deep, maybe some of those sub-levels should just be put in their own routine.

On a python related note, Mr Rhodes points out if you need a new routine and you are in a class, your natural thought is to make a new method on the object. But if you aren’t using self – referring to the object in your new method – you can just make a function. These become easier to test and easier to move.

Factor Out An Iterator

Mr Rhodes says iterators are a Python superpower. I don’t understand python iterators yet and didn’t even understand his example, in the interest of completeness, here’s his example.

Example:

for item in sequence:
     for widget in item:
          for pixel in bitmap:
               pixel.align()
               pixel.darken()
               pixel.draw()

becomes:

def widget_pixels(sequence):
     for item in sequence:
          for widget in item:
               for bitmap in widget:
                    for pixel in bitmap:
                         yield pixel
 
for pixel in widget_pixels(sequence):
     pixel.align()
     pixel.darken()
     pixel.draw()

Lastly he talked about going to an argument per line for function calls. I want to write a full post on this idea because it has some cool implications in version control. The short version is once a function call gets to 79 characters, just go right to putting each parameter on its own line.

Shortening Lines & Binary Operations

One of the core values of Python is readability.

I think it’s cool a computer language has values.

To encourage this value it has a set of guidelines on how to format your code called PEP 8. Most of these points are applicable to all languages, so even if you aren’t blessed to use Python you still might want to check it out.

One of the PEP 8 rules is lines shouldn’t be longer than 79 characters. This is also a rule in typography. That’s the length of line that doesn’t require extra mental effort to move back to the beginning of the next line.

Much of this post and the next one are from a talk by Brandon Rhodes entitled A Python Aesthetic: Beauty and Why I Python and available on YouTube. It got me inspired to think about this stuff again and motivated to make these posts.

Here’s a few of tips on how to shorten lines effectively.

Shorten Lines By Adding Variables

As Mr. Rhodes pointed out in the source video, it’s a feature of math that you can assign some part of an operation to a variable and use that to pass on to another operation. Why do programmers seem to want to avoid this?

I don’t know, probably because programmers are lazy. Well some programmers. The same kind of programs that use one character variable names and magic numbers.

But not you right?

Example:

canvas.drawString(x, y, ‘Please press {}’.format(key))

Our first thought is using return to shorten.

canvas.drawString(x, y, 
     ‘Please press {}’.format(key))

But if it has to be two lines anyway….

message = ‘Please press {}’.format(key)
canvas.drawString(x, y, message)

Adding the message variable not only shortens the line, it gives us a variable that tells us what it is. The message is now easily changed, printed for debugging or logged later.

Shortening Long Formulas

In math a binary operation is where the two parameters are on either side of the operator. Like 1 + 1. In a way this is a function, just written differently than a normal function would be, ie. add(1,1).

Sometimes you have a big mathematical formula that stretches past 79 characters. Especially if you use good variable names. Here Mr. Rhodes disagrees with PEP 8 and I agree with him.

Example:

adjusted_income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deductions - student_loan_interest)

PEP-8 says to divide binary operations after the operator. This is bad.

PEP-8 badness:

adjusted_income = (gross_wages + 
     taxable_interest + 
     (dividends - qualified_dividends) - 
     ira_deductions - 
     student_loan_interest)

Donald Knuth says divide before the operator.

Knuth Goodness:

adjusted_income = (gross_wages 
      + taxable_interest 
      + (dividends - qualified_dividends) 
      - ira_deductions 
      - student_loan_interest)

This seems much clearer, and put the operators front and center. Subtractions actually look like the variable is negative.

Source: A Python Aesthetic: Beauty and Why I Python by Brandon Rhodes

Image: Photographer Ron Davis (Me). Model Virginia McConnell.

Remove Comments By Making Them Code

I like comments, probably too much. Lately I’ve been rethinking some comments because they just don’t make sense or work in the real world. But not till I watched this video did I realize you could change them into code.

Make A Comment Into a Variable.

Variable names – good variable names – are self commenting. Extreme Programming uses the phrase “Destroy All Comments”.

Example:

# is window too tall
if window.height > 100:
     fix_window_height()

Becomes:

IsWindowTooTall = window.height > 100
if IsWindowTooTall:
     fix_window_height()

Example:

widget.reset(True)  # forces re-draw

becomes:

force_redraw = True
widget.reset(force_redraw)

Also makes it easier to put in debugging code later because you already have a name for the value to print. True is also a magic number in a sense because we don’t know what it means.

Make Code Section Comments Into Functions

Example:

# open the barn
barn = code.Barn.get()
barn.unlock()
barn.open()
 
# saddle the horse
saddle = code.Saddle.get()
saddle.install(horse)

Becomes:

open_barn()
saddle(horse)
 
def open_barn():
     barn = code.Barn.get()
     barn.unlock()
     barn.open()
 
def saddle(horse):
     saddle = code.Saddle.get()
     saddle.install(horse)

I once asked a boss if we could get people to add a few more comments so it would be easier to tell what code did. His reply was that when consultant programmers who get to work on lots of code they didn’t write come into a new project, they run a script that removes all comments. He said comments are often misleading. I think that’s a little extreme, but if you turn comments into code, that documentation will always be there.

Source: A Python Aesthetic: Beauty and Why I Python by Brandon Rhodes

Tactical Coding

When programmers are taught they are taught about how programs work. They are also taught how structure large projects so the parts work together – architecture or strategic programming.

But I don’t ever remember learning tactical programming. This is how to write code at the function and line level to decrease errors and increase things like understanding and readability.

I remember reading the book Code Complete after just a few years as programmer and being blown away. Why didn’t anybody tell me this stuff. You mean my variable names shouldn’t just be one letter? I should check parameters before using them? If I write the shell of a function first, I probably won’t forget to close a bracket or return a value.

I’ve decided to write a series of blog posts about tactics of coding. They have their own category on the blog now – Coding – Tactical.

Of course that means that all our code must be black, because everything tacticool is black.

Footnote: I like the first edition of Code Complete better than the newest one, but it is still good stuff.

Configuring PyCharm, Django and Vagrant

I’ve written about doing development on virtual machines and how to set up Vagrant to run Django. I’ve also been using PyCharm CE for awhile to do simple Python development. Now I’m doing more Django and decided to upgrade to the Professional Edition of PyCharm because of all its cool Django specific features. JetBrains touts you can use it with Vagrant, and built in features to do that. They have tutorials on configuring PyCharm to work with Vagrant and how to use it with Django, but not how to do both.

Turns out this isn’t as simple as it could be and no one has written a comprehensive tutorial on how to do it.

Here it is.

This tutorial will take you from having nothing to having a new Django PyCharm project running on a Vagrant VM.

Create Your Vagrant Virtual Machine

Since I’ve already written how to create a Vagrant VM to do Django development I’m going to assume you are at the same point you were at the end of that tutorial. You have a vagrant instance created. The vagrant directory shares a folder with the server. This article going to call that folder [VagrantFolder]. Any where you see that substitute your folder’s name.

The application I’m creating during this tutorial is called “session_manager”. You should substitute the name of your app.

Launch PyCharm PE and Set-up Vagrant

This tutorial assumes you are using the Professional Edition of PyCharm, as it is the one that has Django support in it. When you launch PyCharm, you’ll get the typical startup window, with its list of known projects and options to create a new one. Now we are going to tell PyCharm about the Vagrant we just created.

Select Configure->Preferences and select Vagrant on the left side.

The only thing you should have to change here is the value of “Instance Folder”. Point it to your [VagrantFolder].

[learn_more caption=”Side Note: Vagrant Not Per Project” state=”close”] I think it is a little strange PyCharm configures Vagrant separate from a project. 

One of the main reasons I use Vagrant is so each project I’m working on has it’s own VM. This means anytime I launch a project, I’m going to want it to have its own vagrant instance. But with PyCharm, I have to come change this preference every time I change Projects.

Actually this may not be true, since a project is “connected” to the Vagrant instance via the “Python Interpreter”, which is bound to one vagrant instance. [/learn_more] 

Create A New Django Project

Close the preferences and go back to the startup window and select Create New Project.

The Create New Project panel will appear for you to fill in.

  • Project name: The name of your new Django Project. In my case this is “session_manager”
  • Location: Click the button on the right and navigate to [VagrantFolder]/django_shared/
  • Project Type: Click the button on the right and select “Django Project”
  • Interpreter: Leave this on its default. (We’re going to have to change it in a minute anyway but you can’t select a remote interpreter in this box).

Click OK to save.

The Django Project Settings panel appears asking for Django specific configuration:

  • Project name: Your project’s name, in my case “session_tracker”.
  • Application name: This has to be different from your project name. {I thought naming them the same was a time honored – if weird – Django tradition.}. I named mine “session_tracker_app”.
  • Templates Folder: I just leave it on the default.
  • Enable Django admin: I’m leaving this checked for my app.

Click OK to save.

Now PyCharm will do its thing and you’ll have a new Django app/project created in your [VagrantFolder]

[learn_more caption=”Why Can’t I Create A Project With Remote Interpreter?”] While writing this I attempted to create the Python Interpreter first, but the “Config from Vagrant File” didn’t work.

Even if I had managed to create it, you can’t select a remote interpreter in the new project window anyway.

Why can’t I create the Vagrant Interpreter before the project and then select a remote during project Creation?[/learn_more]

Create a Python Interpreter For Our Vagrant

At this point we’ve configured a Vagrant machine and we’ve made a new Django project. We need to launch our VM so we, and PyCharm, can do some configuration.

Select Tools->Vagrant->Up and watch it launch your VM launch in the terminal that appears at the bottom of the project window. If this is the first time, this will take awhile.

We have a VM to run our project on, but PyCharm doesn’t know how to run a project on it. Because my Vagrant is setup to share the “django_shared” folder between the host machine and the VM, our code is on the VM. But if you hit run right now, PyCharm would attempt to run it on your host machine, not the VM. The way to change this, is via the Python Interpreter.

In PyCharm you configure which version of Python to use when running your project. By default this uses your local Interpreter, but we want it to use the one on the VM. We need to create a new Python Interpreter.

Where Is Python Installed? It Matters.

I mentioned PyCharm needs to know which Python to use to run a project. This is true on the VM as much as your local machine. In the next step you are going to be asked where python is installed on the VM.

I’ve done this a couple of times and left that path as PyCharm set it. Using the default “/usr/bin/python” made PyCharm think nothing was installed on the VM and then it would give me errors and make me install packages until it was configured right. One of the things I had to do to make it work was to tell PyCharm to install Django, even though my Vagrant file had installed Django.

That seemed so strange I decided to see where python actually was on my VM. Once I did that and set Python Interpreter Path correctly, PyCharm knew Django was installed and didn’t give me any errors.

If you – like me – do not know where python is installed on your VM do this in PyCharm:

Select Tools->Start SSH Session…

Select “Vagrant at [VagrantFolder]” from the list that appears. A terminal will open at the bottom of the screen.

Then do this:

Last login: Sat Feb 22 21:57:54 2014 from 10.0.2.2
vagrant@lucid64:~$ which python
/usr/local/bin/python
vagrant@lucid64:~$

Copy that path for use in the next step.

Create A New Python Interpreter

  • Open Preferences.
  • Select Project Interpreter -> Python Interpreter on the left side. You should see a list of existing Interpreters.
  • Click the + button at the bottom of the list to create a new one.
  • A popup list of Interpreter paths will appear for you to select from. Pick the option, “Remote…”
  • When you do, you’ll get a new window to configure the Remote Python Interpreter. Click “Fill from vagrant config” and select the Vagrant file from [VagrantFolder]. PyCharm will fill in the values of the form.
  • Name: Give your new interpreter a meaningful name.
  • Python interpreter path: where python is installed on the VM.

Click OK to save.

[box type=”warning”] Vagrant Needs to be Up.

If you didn’t Vagrant Up before this, “Test Connection” won’t work and you’ll get a warning when closing the panel. If that happens, close prefs, and use Tools->Vagrant->Up to launch your VM.[/box]

Now PyCharm should know about your VM’s setup. It should show you a list of installed packages. If you are missing something, error messages will appear at the bottom and you can click them to install what it thinks you are missing. If there are no packages listed, try closing and reopening the Preferences window.

ProjectInterpreterWithPackages

 

Click OK to close the Preferences Window.

Configure Your Project to Use the Correct Interpreter

PyCharm now knows about our VM’s Interpreter, we need to tell our project about it and configure things so it will run our Django application in our browser when we click the Run button.

  • Select Run->Edit Configurations…
  • Select the configuration on the right PyCharm created for us. In my case it was called “session_tracker”.
  • Change “Python interpreter” to our newly created one.
  • A new field will appear. Click the button next to “Path mappings”.  A new window will appear to let you create your mappings.
  • Vagrant shares a local directory with the VM. My Vagrant file is configured so a folder is called “django_shared” in our [VagrantFolder] locally, and “django_shared” in the home directory on the VM are the same. You need to enter the full paths of each of those in the “Edit Path Mappings” window. Click the + button to create a new mapping, then enter the values for the mapping on each side.

[learn_more caption=”I Hate Typing Paths”] I hate typing paths, so to make it easy I navigated to the local directory in the OSX Terminal and typed “pwd”, which spit out the full path. I copied that and pasted it into the left side of the Path Mapping Window.

Then I vagrant sshed into my vagrant box, navigated to the django_shared directory and executed “pwd” again. Copied that value and pasted it in the right side.

If you used my script, this value will always be “/home/vagrant/django_shared”.[/learn_more]

Edit_Path_Mappings

Click OK to save the paths.

Back in the configuration window.

  • Click the checkbox to turn on “Run browser:”. Doing this will open your browser to that location whenever you run the Django application and start debugging.
  • For Host: enter 0.0.0.0

This took me a long time to figure this one out. You have to set your host – which is what gets used by Django’s runserver command – to 0.0.0.0. If you leave it the default, you won’t be able to connect to Django from your machine.

Why? According to this blog post, which gave me the idea, it is “because Django webserver bounds the ip to 127.0.0.1, which is loopback in the Ubuntu guest”.

Click OK to save.

That, finally, is it. If you click run or menu Run->Run, your browser should open and you should see the default Django server message.

I just spent five hours writing this tutorial when I’d sat down to start working on my session_manager Django app. I did it because it took me forever to figure out how to make all this work and I wanted it to be easier for the next guy. Please give me feedback if you do find it useful and of course share it.

Setting Up Django on Vagrant

[box type=”warning”]Since I created this post things broke. Namely checking out the latest versions of the Chef cookbooks introduced a whole rat’s nest of dependencies. I spent a day trying to figure them out, but finally decided to just do provisioning via a shell script.

The repository has been updated so the latest version uses the new shell script. If you want the Chef version, it was tagged “v1WChef”.
[/box]

In a previous post I talked about how to do WordPress Development right by using Vagrant to create a virtual machine to do your development in.

I’ve been wanting to improve my Django skills and have a project I want to use it for, so I plan to use the same principle and do my Django coding in a VM using Vagrant.

Figuring out how to do this quickly, easily and reliably was more complicated than I expected. I owe a great debt to Smiley Creatives’ article “SETUP A DJANGO VM WITH VAGRANT, VIRTUALBOX, AND CHEF” and a comment by juanje on how to create a Chef cookbook to install Django.

My goal was to create something where I could run a script and have a whole new development environment for Django set up.

The Vagrant Django Script

If you don’t care how it works, but just want to use it, then do these steps.

1. Clone the repository to your machine.

I’ve created a git repository on bitbucket.org with all the files you need. Just execute this command.

git clone https://bitbucket.org/rondavis007/django-vagrant-creation-scripts.git

This will create a folder on your machine. This folder is not where you are going to run Vagrant from. Rather it is a source folder for creating another folder to run Vagrant from and do your Django development.

2. Create a Target Folder

Make a directory for your development. Note the path to that directory.For our purposes, we’ll assume you want this directory to be next to the directory just created by the git clone command in step 1.

mkdir VMTEST

3. Run the Create Django VM Script

In the folder that was created by git you will find a script ‘create_django_vm.py’. Execute this script with the path to the directory you created in step 2.

cd django-vagrant-creation-scripts/
python create_django_vm.py ../VMTEST/

If you look in the new directory you’ll see something like this:

-rw-r–r– 1 rondavis 2920 Feb 5 14:39 Vagrantfile
drwxr-xr-x 12 rondavis 408 Feb 5 14:39 cookbooks
drwxr-xr-x 2 rondavis 68 Feb 5 14:39 django_shared

You have a new Vagrantfile, a folder full of Chef cookbooks, and a django_shared folder that will be shared with the VM.

4. Move To Your New Directory and Vagrant Up

That’s it, you are ready to go.

cd ../VMTEST/
vagrant up

That’s it. There will be a bunch of Vagrant launching text, but when it’s finished you can:

vagrant ssh

You’ll find a directory in your home directory named “django_shared”. This is the same as the folder “django_shared” in the directory you just vagrant sshed from.

From here you can do all your django commands on your VM. Edit the files on your host OS, and view the app via your browser.

Running Django On Your VM

Let’s make sure our development environment is able to run Django by duplicating the first steps of the Django Tutorial.

Once you are logged in:

vagrant@lucid64:~$ cd django_shared/
vagrant@lucid64:~$ django-admin.py startproject mysite
vagrant@lucid64:~$ cd mysite
vagrant@lucid64:~$ python manage.py runserver 0.0.0.0:8000

First we move into our shared directory, then we create a new Django project with the django-admin.py script. Then move into the newly created app folder and run the server.

Now go back to your browser and point it to http://localhost:8000/ and you should get the standard Django welcome.

Welcome_to_Django

A special note about the runserver command on your VM. You can’t just use the default ‘manage.py runserver’ because the VM won’t let that be shared. Instead you must tell it to run the server on 0.0.0.0:8000 to let it be seen outside the VM

I think the code that makes all this happen is well documented, so I won’t go over it all here. Let me know if you use this script and how you like it.

Verbose Switch a Cleaning Up Code Example

There are few principle we here at Reactuate Software try to keep in mind when writing code. One is DRY, or “Do not Repeat Yourself”. Another is “Readability Counts”. So when I see code that looks like this, I cringe.

- (IBAction)dayButtonPressed:(id)sender {
    UIButton *b = (UIButton*)sender;
    theDay = b.tag;
    switch (b.tag) {
        case 2:
            monButton.selected = YES;
            tueButton.selected = NO;
            wedButton.selected = NO;
            thButton.selected = NO;
            friButton.selected = NO;
            satButton.selected = NO;
            sunButton.selected = NO;
            break;
        case 3:
            monButton.selected = NO;
            tueButton.selected = YES;
            wedButton.selected = NO;
            thButton.selected = NO;
            friButton.selected = NO;
            satButton.selected = NO;
            sunButton.selected = NO;
            break;
        case 4:
            monButton.selected = NO;
            tueButton.selected = NO;
            wedButton.selected = YES;
            thButton.selected = NO;
            friButton.selected = NO;
            satButton.selected = NO;
            sunButton.selected = NO;
            break;
        case 5:
            monButton.selected = NO;
            tueButton.selected = NO;
            wedButton.selected = NO;
            thButton.selected = YES;
            friButton.selected = NO;
            satButton.selected = NO;
            sunButton.selected = NO;
            break;
        case 6:
            monButton.selected = NO;
            tueButton.selected = NO;
            wedButton.selected = NO;
            thButton.selected = NO;
            friButton.selected = YES;
            satButton.selected = NO;
            sunButton.selected = NO;
            break;
        case 7:
            monButton.selected = NO;
            tueButton.selected = NO;
            wedButton.selected = NO;
            thButton.selected = NO;
            friButton.selected = NO;
            satButton.selected = YES;
            sunButton.selected = NO;
            break;
        case 1:
            monButton.selected = NO;
            tueButton.selected = NO;
            wedButton.selected = NO;
            thButton.selected = NO;
            friButton.selected = NO;
            satButton.selected = NO;
            sunButton.selected = YES;
            break;
        default:
            break;
    }
}

This code is in an iOS app I inherited and it handles this control.FrequencyControl

To me this violates both the concept of DRY and the concept of Readability. It is pretty obvious what the routine does and you can even guess how it works. But the devil is in the details. Yes you know it is turning off all the buttons but the day that is selected. What if one of those case statements was messed up and accidentally had two buttons set to true? The sheer number of repeated very similar values would make it a pain to find.

Plus its just ugly and inelegant.

Here’s the 30 second fixed version:

 
- (IBAction)dayButtonPressed:(id)sender {
    UIButton *b = (UIButton*)sender;
    theDay = b.tag;
 
    monButton.selected = NO;
    tueButton.selected = NO;
    wedButton.selected = NO;
    thButton.selected = NO;
    friButton.selected = NO;
    satButton.selected = NO;
    sunButton.selected = NO;
 
    switch (b.tag) {
        case 2:
            monButton.selected = YES;
            break;
        case 3:
            tueButton.selected = YES;
            break;
        case 4:
            wedButton.selected = YES;
            break;
        case 5:
            thButton.selected = YES;
            break;
        case 6:
            friButton.selected = YES;
            break;
        case 7:
            satButton.selected = YES;
            break;
        case 1:
            sunButton.selected = YES;
            break;
        default:
            break;
    }
}

See how much cleaner that is. There are situation where setting all the controls one way and then immediately setting them to on might cause a flicker, but iOS isn’t one of them.

Since I’m in this code anyway….

Here’s an even more cleaned up version

 
/* --------------------------------------------------------------------------------------------
 
	dayButtonPressed
 
	Description: 
		Action method for the day of the week frequency control.
 
	-------------------------------------------------------------------------------------------- */
- (IBAction)dayButtonPressed:(id)sender
{
    UIButton*   button = (UIButton*)sender;
    theDay = button.tag;
 
    monButton.selected = NO;
    tueButton.selected = NO;
    wedButton.selected = NO;
    thButton.selected = NO;
    friButton.selected = NO;
    satButton.selected = NO;
    sunButton.selected = NO;
 
    switch (button.tag)
    {
        case SUNDAY:
            sunButton.selected = YES;
            break;
        case MONDAY:
            monButton.selected = YES;
            break;
        case TUESDAY:
            tueButton.selected = YES;
            break;
        case WEDNESDAY:
            wedButton.selected = YES;
            break;
        case THURSDAY:
            thButton.selected = YES;
            break;
        case FRIDAY:
            friButton.selected = YES;
            break;
        case SATURDAY:
            satButton.selected = YES;
            break;
        default:
            NSAssert(false, @"Day button Tag and unknown value %d", button.tag);
            break;
    }
}

I had to define some constants but it is much more readable now. I also hate one letter variables, so the button variable got renamed as well.

Added an assert for something that should never happen, namely the value of the tag not being between 1 and 7. NSAsserts are only handled in non-release builds, so there isn’t a chance of it crashing the shipping app.

Also added is my “short” routine header for Objective-C code. It just tells what the method does . It doesn’t bother to tell what the parameters or return values are, because saying it is an “action method” tells an experienced iOS/MacOS programmer all that information.

Doing WordPress Development The Right Way

I’ve had a bad habit. Lots of time when I do WordPress development, I just work on the live site. I open Cyberduck and go to the wp-content directory, click the edit button and start working on the code.

This is the wrong way.

Recently a client mentioned that when I did some new fixes could I make sure I don’t leave things looking weird on the site. I then realized I’d been doing in this wrong. For a very long time.

The right way is to do development on another machine and just push tested final code to the live site. That’s the way we’re doing it going forward.

WordPress Local Development Made Easy

One of the reasons I did things the wrong way is how hard it is to set up a local WP install for development if you have multiple sites you need to work on. You have to set-up different database tables and prefixes, put each install in a different directory, and lots of other minutia. Then you will never have a completely clean environment because there are multiple installs in the same place.

Enter VagrantPress

I’ve started using Vagrant quite a bit in development. It lets you easily create virtual machines and configure them. You can also recreate these machines easily because all the create is scripted and saved via the Vagranfile.

VagrantPress is a Vagrant box that creates a linux VM, then installs everything you need to run WordPress. Then it installs WP in your vagrant directory so you can directly edit the WordPress install.

It is as easy to install as they say on their home page. Just three steps, assuming you already have Vagrant set up.

I did a couple of things different.

I did a git clone of VagrantPress since I don’t have wget installed on my laptop.

I also modded the vagrantfile slightly to use a 32 bit Linux base box I already had on my machine to avoid another long download.

Learning From Your Mistakes

Yesterday I was getting back to a client’s project that I’d worked on over a month ago. Because of this time gap, I couldn’t use a previous virtual machine I’d made to work on it. So I decided I would create a new one using Vagrant, since Vagrant is buzzword cool right now.

I created a vagrant file that created a new Linux server and installed php. Then to test it, I did this:

Created a index.html file with this in it.

PHP where are you?
<?php
 
echo phpinfo();

But the phpinfo block didn’t show up.

I then proceeded to spend four hours messing with the Vagrant file, the shell script that installs php/mysql/apache, the configuration of apache on the VM, and many other things. In the process I learned about how apache loads modules including php, which was different than how it happens on OSX. I learned about three ways to configure your virtual machine with Vagrant – shell script, Chef, and Puppet. I learned about installing mysql non-interactively.

Finally I gave up and decided I’d come back to it tomorrow and left the office. When I got downstairs and was walking to my car I realized why it wasn’t working.

Did you techies catch it?

It was my test file’s name. index.html. It should have been index.php. You can configure apache to handle php in a .html file, but it’s generally a bad idea and not the default configuration.

Sure enough I walked in this morning, renamed the file and it worked like a champ.

Now I need to look over the changes to the Vagrantfile and bootstrap.sh – I decided to us the shell script approach – to make sure my attempts at fixing didn’t do anything bad to my VM install.