Tuesday, November 5, 2013

How to take FrontRunner from Provo to SLC airport

To get from Provo to the Salt Lake City airport by train, expect it to take about an hour and 15 minutes on weekdays (on weekends, up to 2 hours). If you need help, you can tweet to @RideUTA - they're very good about responding.

  1. Go to the Provo Intermodal Station. Currently there is free, long-term parking in that parking lot.
  2. Go to a ticket booth at either end of the boarding platform and print your ticket(s). You may use a credit card. Your ticket includes any mode of UTA transportation until the expiration date and time.
  3. Board the next FrontRunner departing Provo northbound. See the FrontRunner schedule.
  4. Get off at the North Temple Station in Salt Lake City (not the Salt Lake City Central Station). Go up the escalators or elevator to the TRAX station on the bridge. This takes about 3-5 minutes.
  5. Board the Green Line TRAX going west. Its final stop is the airport.

That's all! It's quite an enjoyable trip since FrontRunner has free Internet that works pretty well.

The reverse trip is the same: print your ticket at the airport, get on the light rail around the corner, and then switch to the southbound FrontRunner at the North Temple bridge, which you stay on until Provo or wherever you're going.

How was your experience? Feel free to comment.

(Post last updated September 2015.)

Monday, October 14, 2013

Parse large CSV files with Javascript efficiently

Wouldn't it be nice if users could use CSV files on your web app without having to upload it to a remote server? If the file just stays on their own computer, the privacy concerns basically disappear.

Local or remote, though, CSV parsing is hard. There's not a strict standard, so each implementation is a little different. And when I say CSV, I don't necessarily mean literal "comma-separated values" -- the delimiter could be any character. It's also common to see tabs, pipes, and semicolons as separators.

I work with a lot of delimited text data, and too often I find CSV files that are malformed. Usually quotes are not escaped, or quotes are missing around a field that has special characters like newlines, delimiters, or another quote.

But typical CSV parsers are guilty, too. We can do better.

So here's a way to easily parse CSV with Javascript while keeping the process efficient and error-free as possible, even if you encounter malformed content.

Meet Papa Parse

Papa Parse is the result of many hours of effort from several contributors to bring a fully-featured, powerful delimited text parser to the world of Javascript. Most parsers just split on commas or don't handle large files well. Papa Parse is robust and easy to use.

Get Papa Parse from GitHub or try a demo on papaparse.com.

Here's a simple example of parsing a CSV / delimited text string with some custom settings:
results = Papa.parse(csvString, {
    delimiter: ",",
    header: true,
    dynamicTyping: true
});
The second argument (the config) is optional. Papa Parse can automatically guess the delimiter if you don't specify one. Dynamic typing automatically turns numeric values into numbers for you. Here, we've also specified a header row, so data will be keyed to the field names.

To access a value, say, if you're iterating the resulting rows:

results.data[i]["Field Name"]

To parse a file:
$('input[type=file]').parse({
    config: {
        complete: function(results) {
            console.log("Parse results:", results);
        }
    }
});
To stream and parse potentially very large CSV files:
$('input[type=file]').parse({
    config: {
        step: function(data, file, inputElem) {
            console.log("Row data:", data.results);
            console.log("Row errors:", data.errors);
        },
        complete: function() {
            console.log("All done!");
        }
    }
});
Visit PapaParse.com for full documentation and demos.

Papa Parse will always report errors and do its best to handle malformed CSV content.

Monday, September 30, 2013

Behavior-driven testing in Go with GoConvey (BDD in "golang")

First: the built-in Go testing tools

Few things bring sweeter peace to the soul than making changes to Go code, then:

$ go test
...
PASS
ok

Go's built-in testing tools are good, but their structure doesn't easily allow for us to convey behavior. When we make an assertion, we assert that the opposite is not true instead of asserting that what we want is true:

if (a != b) {
    t.Errorf("Expected '%d' but got '%d'", a, b)
}

This has been very nice, but as projects get bigger, tests get more numerous, and structuring them so they are readable, avoid repetition, and clearly test specific behavior rather than just results becomes difficult to do idiomatically. (Plus, Go's default test output is hard to read.)

Enter GoConvey.

GoConvey is a library for behavior-driven development. It implements the same go test interface you're used to, and plays nicely in both default and verbose (go test -v) modes. GoConvey structures your tests in a way that makes them more readable and reliable. GoConvey has easy-to-read output that even uses colors (and if you're on Mac, Unicode easter eggs) for fast comprehension.

Finally: Your first GoConvey tests

Simply install GoConvey with:

$ go get github.com/smartystreets/goconvey

Then open your new test file. Be sure to name it something ending with "_test.go". It should look familiar to start (I'll be recreating this example file):

package examples

import (
. "github.com/smartystreets/goconvey/convey"
"testing"
)

func TestSpec(t *testing.T) {
}

Notice that we import the "convey" package from GoConvey with the dot notation. This is an acceptable practice for our purposes, as you will see, since this is all for testing your production code and none of it actually gets built into your executable.

To start testing, let's begin to fill out that TestSpec func:

func TestSpec(t *testing.T) {
Convey("Subject: Increment and decrement", t, nil)
}

This sets up our first test harness. We pass in a string which acts merely as a comment, then the testing.T object (but only our top-level Convey() call should have it! -- you'll see in a minute), then nil. For now, this means "nothing to see here, move along" -- and that test will be skipped. In order to be useful, we must replace nil with a func():

func TestSpec(t *testing.T) {
Convey("Subject: Increment and decrement", t, func() {
var x int

Convey("When incremented", func() {
x++
})
})
}

As you can see, any nested calls to Convey() shouldn't have "t" passed in.

Within your functions, you can set up your test at the appropriate scope. Above, we've defined a function to test the subject ("Increment and decrement") and given it an int to work with (x). You may want to avoid using := notation at higher scopes until you're done nesting Conveys. (Figuring out why is left as an exercise for the reader.)

Our second level of Convey, then, tests various paths or situations that the subject may encounter. The harness we've specified tests the paths when x is incremented. Now it's time to make assertions. We'll make two:

func TestSpec(t *testing.T) {
Convey("Subject: Integer incrementation and decrementation", t, func() {
var x int

Convey("Given a starting integer value", func() {
x = 42

Convey("When incremented", func() {
x++

Convey("The value should be greater by one", func() {
So(x, ShouldEqual, 43)
})
Convey("The value should NOT be what it used to be", func() {
So(x, ShouldNotEqual, 42)
})
})
})
})
}

The nested structure is incredibly helpful as projects grow.

Feel free to make several assertions in a row, within one convey, or in a loop. Check marks will be placed at the end of the verbose output to indicate that each one has passed (or an X if it didn't pass).

Oh -- did I mention that you can now run your tests by doing:

$ go test

I usually prefer verbose mode:

$ go test -v

And if you want your tests to run automatically when you save your test files:

$ python $GOPATH/src/github.com/smartystreets/goconvey/scripts/idle.py

Similarly, if you want verbose mode, tack a -v on to the end of that.

Available functions / assertions

This primer ends here, but that should get you started with BDD in Go. Be sure to check out the README for another once-over, and the Godoc documentation on the assertions and methods you can use, since I didn't cover most of them here. Also see the examples folder for even more that aren't yet documented, such as a Reset() function and similar things.

GoConvey is a new library but has lots of promise for writing more robust and test-documented code. As you encounter certain needs that aren't yet met by the library, open an issue or fork it and contribute.

Friday, September 6, 2013

How to set up dynamic DNS in 5 minutes

With a dynamic DNS service (don't worry, there are free ones), you can use your domain name to point it to your home server, even if your home IP address changes sometimes. That way you can type yourdomain.com in your browser and connect to your home network, or any other machine you configure.

All the guides I found for this simple task were actually really hard and required installing and configuring obscure/old software. I'll show you how to do it without installing anything and you can be running in about 5 minutes.

Of course, I assume you're on a Mac or Linux computer. If you're running Windows, you'll probably have to install something. Also, I used Namecheap's free, built-in dynamic DNS service, and it's the only one I've tried so far.

I bet you can get this done in 2 easy steps!

  1. Enable dynamic DNS (I'll show you how with Namecheap)
  2. Set up automatic updates so your domain points to your home (or whatever)

1. Enable dynamic DNS

A dynamic DNS service is one which has access to modify your domain name and update the host (IP address) it points to.

I mainly use Namecheap for my domain registrations. Conveniently, they also have a free dynamic DNS service. (There are plenty of paid ones out there, but you may not need their features. I don't.) What follows is how I do this on Namecheap, but any registrar with a similar service probably works similarly.

Click on the domain name in your Namecheap account and make sure the domain name's nameservers are pointed to Namecheap. Then click "All Host Records" in the menu. I want my entire domain to point to my home server, so I set @ and www like so:

Your host records might look something like this
You may choose to use only a subdomain, in which case you'd just fill out a new row below in a similar fashion with the sub-domain (host name) of your choice.

Either way, the IP address you enter isn't too important right now. I just chose 127.0.0.1.

Save those settings, then click "Dynamic DNS" near the bottom of the menu:

Namecheap's built-in dynamic DNS works just fine
Make sure the "enable" option is checked and click the button to actually enable dynamic DNS.

Almost done! Keep that tab open, because we'll need the password given on that page.

2. Set up automatic updates

Now we just need to have "E.T. phone home" occasionally, so to speak. This means your home server will call out to your dynamic DNS provider and say "I'm here!" and give it your current IP address.

Most guides will tell you how to install and configure a dynamic DNS client -- but we're just going to use cURL and a cron job for this. (Okay, so if you don't have cURL already installed for some reason, then install it.)

If you're on Mac or Linux, this is easy enough with crontab. All you have to do is make a web request every so often. For example, you might add this to your crontab, if you're using Namecheap:

@hourly curl "https://dynamicdns.park-your-domain.com/update?host=HOST_NAME_HERE&domain=YOUR_DOMAIN_HERE&password=YOUR_PASSWORD_HERE&ip=`curl -s echoip.com`"

(NameCheap's documentation.) You can choose how frequently to have it run. Now let me explain the URL which you need to customize:
  • HOST_NAME_HERE - This would be the host name from the "All Host Records" page. If you're using the whole domain name (yourdomain.com), use @. If you're using the www prefix (www.yourdomain.com), then add a cron job that's exactly the same except with the host of www. Otherwise, this value would be the sub-domain you chose to use and made the A record for.
  • YOUR_DOMAIN_HERE - This is your actual domain name, e.g.: yourdomain.com
  • YOUR_PASSWORD_HERE - This is the password that Namecheap gives you on their "Dynamic DNS" page. 
The last parameter actually specifies the IP address to use. This little trick gets your public IP address using echoip.com.

It's important to note that anyone who has your password can set any host name on your domain with an A (Address) record to any IP address they want, effectively hijacking your domain name.

If you use another dynamic DNS provider or registrar, I'm not sure what the request URL would be for you. That's something you'd have to find out from the organization.

So there you go! Give it a few minutes (maybe a few hours) to propagate, and then your domain will be pointing to your own server.

Wednesday, August 14, 2013

How to customize your terminal prompt (with colors)

At work, the Go projects we're working on have workspaces that get pretty deep and one of the directory names is repeated down the tree. I ended up having no room to type commands in my terminal that didn't wrap to the next line. Annoying!

I could have just limited the prompt to show me the name of the current directory and use pwd when needed, but that was kind of a pain. Then I watched this talk and saw his terminal, and got inspired.

So here's my terminal and prompt now:

Customized terminal prompt -- the red "root" text blinks as a warning/reminder
Doing this took a while to figure out (especially the colors, and getting the root user to share the look). It's a simple idea: edit your ~/.bash_profile file and specify the PS1 variable.

Here's how I did it (this works in both my Mac and Raspberry Pi):

Decoration1="\[\e[90m\]╔["
RegularUserPart="\[\e[36m\]\u"
RootUserPart="\[\e[31;5m\]\u\[\e[m\]"
Between="\[\e[90m\]@"
HostPart="\[\e[32m\]\h:"
PathPart="\[\e[93;1m\]\w"
Decoration2="\[\e[90m\]]\n╚>\[\e[m\]"
case `id -u` in
    0) export PS1="$Decoration1$RootUserPart$Between$HostPart$PathPart$Decoration2# ";;
    *) export PS1="$Decoration1$RegularUserPart$Between$HostPart$PathPart$Decoration2$ ";;
esac

Each segment that looks like \[\e[90m\] changes the color and attributes of the following text, according to an enumeration like this one. The following text keeps those settings until you change it again or reset it. The number before the m is the color. Sometimes there's a number, semicolon, and another number. The second number in that case specifies underline, bold, blink, etc. You can omit both entirely to reset the text to its default or previous style.

To get the root user to share the style, you can, if on a Mac, put that code in your /etc/bashrc file, or on any Linux system (including RPi), you can have the root user's .bashrc source your own .bashrc file. A simple line similar to the following should do the trick (include the dot at the beginning of the line; or you can replace it with source):

. /home/pi/.bashrc

You may also like to have a blank line before each prompt to make it feel a little more "roomy" and less cluttered. You can easily do this by adding \n at the beginning of the $Decoration1 value.

I've really enjoyed this new prompt style. I have plenty of room to type, can clearly see my user, hostname, and current path at a glance. My prompts don't get lost in lots of output (like a disastrous g++ compile).

You're welcome to use it and customize it however you'd like. I'd be interested to see what you come up with.

Friday, August 9, 2013

Automatically make a Raspberry Pi with wifi support

Okay, I love my Raspberry Pi, but setting it up just the way I want got so involved I was afraid I couldn't do it again if I had to. So I wrote a script to automate it, and decided to publish it.

So stop baking your Pi manually. MakeMyPi automates the process. It even configures the WiFi for you (assuming you're using a dongle that's supported by the OS, Raspbian Wheezy).

Visit the project on GitHub

or

View an asciicast of the script in action
MakeMyPi in action

(Sorry, but right now, it only works from a Mac.)

All you have to do is:
  1. Make sure your own public key is in ~/.ssh/id_rsa.pub (which is the default location)
  2. Follow the easy configuration instructions
  3. Run the script and follow instructions; it will ding at you when it needs your attention
MakeMyPi can do the following for your Raspberry Pi:
  • Download an operating system image for you, if necessary
  • Write the operating system image file to the SD card
  • Install its own public/private key pair that you provide it
  • Authorize your own public key to log into it
  • Configure network & wifi (assuming you use supported hardware)
  • Create useful aliases
  • Install helpful and necessary packages
  • Run your own custom provisioning script
Basically, it takes the pain away. No more forgetting how to do certain foundational things. In about 5 minutes, I have a working Raspberry Pi, configured just the way I want, on a brand new SD card.

Have fun. Contribute. Go crazy. Enjoy your pi.

Wednesday, July 31, 2013

Easily get your external IP address using the terminal or command line

I want my little Raspberry Pi to "phone home" once in a while so I know where it's at. I wanted a very simple web service that simply regurgitated the requester's IP address in plain text. Nothing more, nothing less.

About to write it myself, I typed in echoip.com and found exactly what I wanted. It's been a good day!

To get your external IP address from the command line or Linux terminal:

$ curl echoip.com

To save it to a file:

$ curl -s echoip.com > myip

Or, if you prefer wget:

$ wget echoip.com --no-cache -qO-

To get super-nerdy and make your life even easier:

$ alias echoip="curl echoip.com"
$ echoip

(You could use any of the variations above for your alias' command.)

In the end, this is the command I added to my Raspberry Pi's crontab (I've changed sensitive parts):

$ curl -s echoip.com | ssh user@mysite.com "cat > /www/mysite/ipaddress.txt"

As a side note, if you want JSON output, there's jsonip.com, or for XML, there's xmlip.com (which actually does XML, JSON, and plain-text, but I feel like the server is a bit slower -- and the homepage doesn't actually serve XML. *facepalm*)

I don't know who made echoip.com, but props to them for choosing the same name I would have. (For some reason, I thought that the output at plainip.com would be... well, plainer.) Memo to self: if echoip ever goes offline, write my own to replace it.

Tuesday, July 30, 2013

Using Vagrant and cross-compiling Go (golang)

This is mostly a memo-to-self about how to write Go code in my Mac environment, compile it there for a Linux environment, and run it in a production-like Linux environment using Vagrant.

If using Homebrew...

My Go installation was home-brewed. If you used Homebrew to install Go, you need to make sure you did with the --cross-compile-common flag. If not, no problem, just run these:

$ brew update
$ brew upgrade go --cross-compile-common

Or do --cross-compile-all for all supported platforms.

If not using Homebrew...

If you're not using a Homebrew install of Go, you need to make Go support cross-compilation yourself. First you just need to tell Go to put the package files for the other architecture(s) in its pkg directory.

$ cd /usr/local/go/src
$ sudo GOOS=linux GOARCH=amd64 CGO_ENABLED=0 ./make.bash --no-clean

For GOOS, you can specify linux, darwin (for Mac), windows, and freebsd. For GOARCH, you can specify amd64, 386 (for 32-bit), and arm. As of Go 1.1, cgo is disabled by default, so the CGO_ENABLED part isn't necessary if you're setting it to 0, but to enable it you'll need to set it to 1.

Cross-compile your Go program

Once the make is finished, you can change into your Go program's directory and build or install like this:

$ GOOS=linux GOARCH=amd64 go build -o myprogram.linux64 prog.go

myprogram.linux is what I'm calling the output file in this case to remind me that the binary is for 64-bit Linux systems. For Windows, you'd want to call it something ending in .exe. Finally, prog.go is the name of my source file.

You can also do an install instead of just a build using the same technique.

Set it up with Vagrant

Coming soon.

Monday, June 3, 2013

Reducing memory usage in Go programs ("golang")

For a particular task, I needed to write a program in Go that reads an 800 MB CSV file, which simply contains a table of numbers. It serves as an index. These numbers represent integers that point to locations in ordered lists stored in other files (an efficient way to index large amounts of data where there is moderate-high duplication).

Loading the table into [][]int used up about 9 GB of memory. But it turns out that most of the six fields only needed one byte of space to store their values, not 8 entire bytes like the int type uses. See Go's documentation on integer types. Very important to know their bounds.

By creating a struct with specialized int8 and int16 and, in just one case, int32 types, and loading the CSV file into []MyStruct instead of [][]int, my program now uses 1/3 the memory: about 3 GB, and it's just as fast. So where one row was using about 48 bytes of space (times 46 million -- that's a lot), each row now uses only about 16 bytes, which is -- that's right -- 1/3 the size.

Friday, May 24, 2013

Writing a Go ("golang") Web App with nginx, FastCGI, MySQL, JSON

Want to write a web app in Go ("golang") like you write a PHP app? Go is cool since it's kind-of multi-threaded and has some other neat advantages over PHP. I've had fun setting up a small web app in Go. In my case, it's a simple API which accepts JSON input, decodes it into a struct, does a MySQL database request, and returns a JSON response.

NOTE: Go's built-in net/http web server is production-ready, and I recommend just using it. You shouldn't need to use nginx in front of your Go application in most cases. But this guide will show you how to use JSON and MySQL with your Go app, which is good to know regardless of your server configuration!

Install Go, nginx, and MySQL if not already installed

I will assume you already have these installed and added to your $PATH. If you don't need a database, then don't install MySQL. On my Mac, homebrew was the easiest way to install these -- trust me. (brew install go, brew install nginx, etc.)

If you're using MySQL, then you'll also want to install the Go-MySQL-Driver for Go. Install it simply by doing:

$ go get github.com/go-sql-driver/mysql

Configure nginx/FastCGI

This is actually pretty easy. I assume you already have some experience configuring nginx.conf. (Each install seemingly has different defaults as to the conf file's location, and contents, so I won't go over it here. Mine is in /usr/local/etc/nginx.)

I assume too that you've configured PHP with FastCGI before. If not, you may still understand what's happening.

All you have to do is tell nginx to pass certain requests, or maybe all of them if you wish, to FastCGI on a certain port. Our Go program will have a FastCGI handler listening on that same port. If you need a reference, my entire server { ... } block looks like this:

server {
        listen 80;
        server_name go.dev;
        root /Users/matt/Sites/go;
        index index.html;
        #gzip off;
        #proxy_buffering off;

        location / {
                 try_files $uri $uri/;
        }

        location ~ /app.* {
                include         fastcgi.conf;
                fastcgi_pass    127.0.0.1:9001;
        }

        try_files $uri $uri.html =404;
}

Notice that the server_name is go.dev. I added this to my hosts file with the loopback IP address, 127.0.0.1. So when I type http://go.dev in my browser, it resolves to my own box and nginx receives the request.

Also notice that the fastcgi_pass port is not the default 9000, but is 9001. I did this because I already have php-fpm listening on port 9000. My Go app will listen on 9001.

Further notice that the fastcgi_pass is in a location ~ { ... } block such that any page under /app of go.dev will be redirected to my Go program. You can change this path to whatever you'd like or even replace it with the location / { ... } block above if you want your Go program to be executed out of the root of the site.

Let's see it working

Make a .go file (I'm calling mine app.go) with these contents (though I encourage you to study why/how it's working):

package main

import (
"net"
"net/http"
"net/http/fcgi"
)

type FastCGIServer struct{}

func (s FastCGIServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
resp.Write([]byte("<h1>Hello, 世界</h1>\n<p>Behold my Go web app.</p>"))
}

func main() {
listener, _ := net.Listen("tcp", "127.0.0.1:9001")
srv := new(FastCGIServer)
fcgi.Serve(listener, srv)
}

Here's what's happening: The main() function creates a network listener at localhost on port 9001, which is our FastCGI pass-thru port from the nginx config. Then we make a new FastCGIServer and serve requests that pop into that port. The fcgi package has one function: Serve, which blocks and waits for incoming requests. The interface is defined in the net/http package, which is where the ServeHTTP function comes from.

Notice that it receives a ResponseWriter and a Request. From just these, you can write out to your response, and get everything about the incoming request. In this case, all we're doing is writing a simple HTML string.

So, type this in your terminal:

$ go run app.go

Your Go program is now waiting for requests. If you set up nginx.conf like mine, go to http://go.dev/app and you should see, in all its glory:


That was pretty easy!

Now let's receive some simple input from GET/POST

This depends how you want to receive input. If the request was form-URL-encoded (Content-Type: application/x-www-form-urlencoded), like when a form is submitted on a web page, then you can get the form values like this, inside your ServeHTTP function:

fieldValue := req.FormValue("field_name")

This, and more, is documented in the net/http package. Note that fieldValue will be a string unless you convert it to another type yourself. This will nab the field from either POST or GET, but POST takes precedence over GET.

Getting the raw POST request body

But what if you want to get the raw contents of a POST request, like a JSON string? That's also pretty easy. First, make sure a request body exists, then read it. You'll need to import "io/ioutil" since the request Body is a stream. 

if req.Body == nil {
return
}
body, err := ioutil.ReadAll(req.Body)
req.Body.Close()

Decoding JSON

Decoding the JSON is also easy, but it depends on how you want to do it. Don't forget to import "encoding/json". You'll want to make a type to hold your deserialized JSON data. You can use JSON-to-Go to quickly convert JSON into a Go type definition, or make your own manually:

type UserInput struct {
SomeField string
AnotherField int
LastOne string
}

Then you can decode the JSON and write out a value, like so:

var inp UserInput
json.Unmarshal(body, &inp)
resp.Write([]byte(m.SomeField))

Simple!

The nice thing is that you don't need to include all the JSON fields that the JSON input has. Any fields you don't include in your struct, or that don't match the JSON input's structure will be ignored.

Encoding (serializing) JSON

Now let's JSON-encode a struct to output.

bytesOfJSON, _ := json.Marshal(myStructOrSlice)

Here I'm throwing away the error, which isn't smart, but for this example, whatever. The encoded string is now contained as a []byte (byte array, or more technically, a byte slice) which conveniently can be passed right into the ResponseWriter's Write function:

resp.Write(bytesOfJSON)

Tada!

A simple MySQL database request

Now let's do something with the database. Assuming you ran the "go get" line at the very beginning of this article and have the driver's package installed, import these two packages:

"database/sql"
_ "github.com/go-sql-driver/mysql"

I chose the Go SQL Driver over MyMySQL because the Go SQL Driver uses the native database/sql package and is a little newer. But both packages are excellent and work without any real problems.

Making the connection (under the hood, it doesn't actually make the connection until needed, from what I understand) is simple:

conn, err := sql.Open("mysql", "/test")
defer conn.Close()

if err != nil {
fmt.Println("Oh noez, could not connect to database")
return
}

Let me explain the first line. The first argument is the type of database, in our case, "mysql" will do. The second argument is the connection string, which, if you're familiar with PEAR DB, works almost the same way. In my case, I simply specified "/test" because the default user is "root" and I have no password on my dev machine's MySQL server, so those all go away because of defaults, and the name of my database is "test". See the docs for a brief overview of the format for the connection string (it's really quite easy).

The defer line ensures that the connection is closed when we're through with it.

Then to query a row and get a single result, I do this:

var zipcode string
sqlerr := conn.QueryRow("SELECT ZipCode FROM Cities WHERE CityName=? LIMIT 1", "Las Vegas").Scan(&zipcode)
switch {
case sqlerr == sql.ErrNoRows:
    fmt.Printf("No rows")
case sqlerr != nil:
    fmt.Println(sqlerr)
default:
    fmt.Printf("ZIP code is %s\n", zipcode)
}

Now, this only gets one ZIP code for Las Vegas, but there are actually many. How do we get the rest of them?

Easy:

var zipcodes []string

rows, err := db.Query("SELECT ZipCode FROM Cities WHERE CityName=?", "Las Vegas")

if err != nil {
fmt.Println(err)
return
}

for rows.Next() {
var zip string
if err := rows.Scan(&zip); err != nil {
fmt.Println(err)
return
}
zipcodes = append(zipcodes, zip)
}

if err := rows.Err(); err != nil {
fmt.Println(err)
return
}

Ahhhh... much better. Writing that out to the response (as a JSON array, of course) gives me a list of ZIP codes in Las Vegas:


["87701","87745","89044","89054","89101","89102","89103","89104","89105","89106",
"89107","89108","89109","89110","89111","89112","89113","89114","89115","89116",
"89117","89118","89119","89120","89121","89122","89123","89124","89125","89126",
"89127","89128","89129","89130","89131","89132","89133","89134","89135","89136",
"89137","89138","89139","89140","89141","89142","89143","89144","89145","89146",
"89147","89148","89149","89150","89151","89152","89153","89154","89155","89156",
"89157","89158","89159","89160","89161","89162","89163","89164","89165","89166",
"89169","89170","89173","89177","89178","89179","89180","89183","89185","89191",
"89193","89195","89199"]

So... now what?

Well, now that we can read requests, send responses, use MySQL, encode/decode JSON, and all through nginx and FastCGI which is available even on shared hosting, I'd say the possibilities are nearly endless. Ready? Set... Go!

Wednesday, April 10, 2013

Install PECL extensions with Homebrewed PHP on Mac OS X Mountain Lion

I needed to use the HTTPRequest class that comes as a PECL extension to PHP. This was easier than I thought. If you followed my instructions for installing PHP with Homebrew, then installing the PECL extensions should be even easier. Here's how:

Open up the Terminal and do:

$ pecl list-all

just as a sanity check to make sure you have PECL installed from your Homebrew installation of PHP. You will see a list of all available PECL packages, and, potentially after some "Warnings" from bugs in the PEAR code, you'll notice that "pecl/pecl_http" is in the list.

Before you can install anything with pecl, you need autoconf. It helps to build in the environment with which we're working. Just do:

$ brew install autoconf

Then install your favorite PECL extension (in my case, pecl_http):

$ pecl install pecl_http

Then a whole bunch of code garbage will fly past the screen. In a minute or two, some config options will appear. Press "return" to accept the defaults in brackets. When it's finished, you'll know.

If you got a fatal error during install, try running under sudo. If you still get an error, wait a day or two and maybe they'll push a bug fix?

Then just restart your web service (I just reboot my computer) and you're done!

Friday, April 5, 2013

Running Inkscape in Mac OS X Mountain Lion (finding X11)

OS X 10.8 (Mountain Lion) doesn't come with X11 (an X.org window server) installed like previous versions do. But getting Inkscape to run is still very easy.

Download the latest XQuartz and install it.

Next time you run Inkscape, it will ask you, "Where is X11?" It probably won't be in that list, so browse for it in your "Applications" folder, then go inside "Utilities" and choose XQuartz.

Then give it maybe several minutes to run the first time. And there you have it!

Tuesday, March 12, 2013

PHP output buffering on nginx

Simple trick: if you're trying to use output buffering with PHP on nginx, and it should be working, but the whole response still comes down all at once to the client, try disabling gzip compression:

nginx.conf

gzip off;

That should do it! Sure, your "fast" pages might become a little slower, but your slow pages will appear to be loading instead of hanging.

(This works because, of course, nginx can't compress the output until it's all ready to go first!)

Thursday, March 7, 2013

Install nginx / PHP / MySQL on Mac OS X Mountain Lion with Homebrew

UPDATE: If you're looking how to install just nginx on Mac OS 10.10 (Yosemite), see this newer post about that.

Last time I wiped my Macbook Pro, I used Macports to install my web development environment. Doing it that way was really hard compared to using Homebrew. I now fully recommend Homebrew for all Mac package management needs. This is much easier than the Macports way. Trust me.

Here's how to install nginx, PHP, and MySQL using Homebrew on your Mac. It's actually quite easy. I did it on Mountain Lion (10.8) but it probably works for Lion too. I followed the tutorial here on EZUnix.org, but my version fixes some typos and explains some steps along the way.

Disclaimer: This blog is a memo to self for when I need to do this again. If you encounter any errors, I may or may not be able to help...



Estimated Time: 10-20 minutes



Got Command Line Developer Tools?


You're gonna need them. Finally, Apple provides the command line tools without needing to install the nearly-2GB-Xcode from the App Store. Go to their Developers Downloads page and download the latest "Command Line Tools" for your version of OS X, then install them.


Install Homebrew


In case you haven't already, install Homebrew by following the instructions at the bottom of this page.

Homebrew's most legit PHP "tap" (package source) is by Jose Gonzalez. Make sure to install it:

$ brew tap josegonzalez/homebrew-php

We also need a tap for a PHP 5.4 dependency, zlib:

$ brew tap homebrew/dupes

Install MySQL

$ brew install mysql

It'll chew on that for a few minutes, then we need to get it to run as our user account:

$ unset TMPDIR
$ mysql_install_db --verbose --user=`whoami` --basedir="$(brew --prefix mysql)" --datadir=/usr/local/var/mysql --tmpdir=/tmp

I got a "Warning" during this operation, and while I don't think it's critical, I did this and things have seemed to work fine... if you got a warning during the last step, then you could do this:

$ sudo mv /usr/local/opt/mysql/my-new.cnf /usr/local/opt/mysql/my.cnf

Then, to launch MySQL at startup:

$ cp `brew --prefix mysql`/homebrew.mxcl.mysql.plist ~/Library/LaunchAgents/
$ launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist

Done! Next: the web server.


Install nginx

$ brew install nginx

Let that stew, then run this commands to have nginx run as root at startup (so we can listen on port 80, the default, instead of 8080 which is less convenient for development):

$ sudo cp `brew --prefix nginx`/homebrew.mxcl.nginx.plist /Library/LaunchDaemons/

That takes care of the startup part; make your .plist file look like this to run as root (if it doesn't work for you, then the path in ProgramArguments may need adjustment):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>homebrew.mxcl.nginx</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <false/>
    <key>UserName</key>
    <string>root</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/opt/nginx/bin/nginx</string>
        <string>-g</string>
        <string>daemon off;</string>
    </array>
    <key>WorkingDirectory</key>
    <string>/usr/local</string>
  </dict>
</plist>


Then create a log file... this allows us to view server logs in the Mac Console, which is really convenient, but isn't required:

$ sudo mkdir /var/log/nginx/

(Don't forget to tell nginx to put the log file there in nginx.conf: error_log  /var/log/nginx/error.log;)

Done! Next up: PHP.


Install PHP

$ brew install --without-apache --with-fpm --with-mysql php54

Make sure to change "php54" to whatever version you want. At time of writing, PHP 5.4 is the latest stable, but PHP 5.5 is in alpha. I assume 5.5 would be php55, etc. Be sure to adjust any following commands with the proper version number.


Quick note: Yes, OS X does come with PHP pre-installed. But we don't want to use that. We need an install we can use with nginx and FastCGI Process Manager (fpm). Plus, we want the latest version, and I'm just not that into compiling from source.

To run php-fpm at startup:

$ sudo cp `brew --prefix php54`/homebrew-php.josegonzalez.php54.plist  /Library/LaunchAgents/
$ sudo launchctl load -w /Library/LaunchAgents/homebrew-php.josegonzalez.php54.plist

Done! Next up: configuration.



Finishing up

I want all php commands to be using the latest version, not the default PHP binary. So I use this little trick to create a symlink from the default PHP binary to the new one... I do this for both php and php-fpm. If you're confused about which versions are where, use the "whereis" command, like: "whereis php".


$ php-fpm -v
$ sudo mv /usr/sbin/php-fpm /usr/sbin/php-fpm.bak
$ sudo ln -s /usr/local/Cellar/php54/5.4.11/sbin/php-fpm /usr/sbin/php-fpm
$ php-fpm -v

Notice that the version went from 5.3 to 5.4 (in my case). Now for the php binary:

$ php -v
$ sudo mv /usr/bin/php /usr/bin/php.bak
$ sudo ln -s /usr/local/bin/php /usr/bin/php
$ php -v

I also added /usr/local/sbin to the PATH by adding that directory to the /etc/paths file, then restarting Terminal. You can see your current PATH by typing echo $PATH.

Important config files:

/usr/local/etc/nginx/nginx.conf
/usr/local/etc/php/5.4/php.ini
/usr/local/etc/nginx/fastcgi_params

You'll probably want to change these for your own system.

The nice thing about Homebrew installations is that you generally don't need sudo to use or manage these services, since they're in /usr/local.

Alright. Well that did it for me. Enjoy your new dev environment!

You can stop nginx with nginx -s stop, and start it again with just nginx. You can also just reload the conf file with nginx -s reload.

I installed MySQL Workbench and was able to make a connection to the localhost MySQL server by adding a connection to host "localhost" with no password. The only thing I typed was that hostname and everything worked like a charm.

I did use my nginx.conf file from my previous install; you can view a sample conf file if you need it, in my other post about using Macports to do this (link at top of this post).

Saturday, February 16, 2013

PHP's PECL extension (for HttpRequest) worked on website but not command line (CLI)?

I'm running PHP on my Mac under nginx and FastCGI... and that's great and fine. I used Macports to set that all up.

Well, Macs have PHP in another, default location: /usr/bin/php, not Macport's /opt/local/bin/php. When I installed the PECL extensions using Macports (after installing PHP), it installed them to the PHP at /opt/local/bin/php, not /usr/bin/php.

I have a PHP script that makes HTTP requests, and it may be a long process, so I spin up PHP on the command-line to do it in the background. Took me forever to figure out that that PHP binary was different from the one used by nginx when I loaded up my dev site.

The CLI version of PHP which I was running for this didn't have PECL installed. A quick, dirty way to fix this:

  1. sudo mv /usr/bin/php /usr/bin/php.bak
  2. sudo ls -s /opt/local/bin/php /usr/bin/php

That's right! Make php a symlink! Why? Well, I'm not sure. I tried simply doing the "mv" to kind of "hide" the binary from sh, but it was giving me "php - command not found" errors, even though /opt/local/bin is was in the PATH. Why didn't it look there? I have no idea. I'm not very proficient with unix stuff. But the symlink was enough to trick it and it's working great now.