Wednesday, December 31, 2014

Drop that Internet Explorer!

What about the globe icon?
Should you drop support for Internet Explorer 8? Why not? My comic never supported it in the first place.

Well, maybe that's a little unfair. My comic doesn't support it because it has no support for SVG images, which are a major part of the comic's design. Also, CSS3 support is not there, which makes things look pretty bad. I just don't have time to develop a completely different site design for the roughly 9% of Internet users still on the antique browser. I doubt they are my target audience anyway (actually, only about 10% of my comic audience uses any version of IE).

Yeah, that's the one I remember
Internet Explorer 8 is the last version available on Windows XP, which is itself no longer supported. I liked XP and kept it around for a really long time myself, but Windows 7 and 8 are just fine operating systems and both support decent versions of IE.

But for further reading, look at these:

Is it Time to Drop Support for IE8?

The Internet Explorer 8 Countdown

Microsoft IE8 browser support ending in 17 months

Now there are even rumors that Microsoft will replace Internet Explorer with a totally new browser in Windows 10! While you might think that retiring the frustrating surfing tool is great news, just wait until you have to support TWO DIFFERENT weird non-standards-compliant Microsoft browsers instead of just one! It's not like every computer on earth is going to suddenly upgrade to Windows 10. There will be people hanging around on Windows 7 for years, and I doubt they'll get this new browser.

In case you are wondering, it's not until Internet Explorer 10 that 100% of my comic is supported. It does look fine in IE9 though. I just don't know what happened to Internet Explorer. I remember it being much better than Netscape Navigator 4, but it lost its way somewhere around version 5. Nobody stays on top forever. I wonder when someone will come out with a better browser than Chrome...

Amphibian.com comic for 31 December 2014

Monday, December 29, 2014

Atomic Feed!

There just aren't enough ways to get my comic delivered to you. In the old days, a newspaper would be dropped off at your house each morning and would contain a page full of comics. You didn't have to go to the publisher and get them. But today, there are no newspapers (well, maybe a few are left) and I am my own publisher. You have to remember to go to amphibian.com every Monday, Wednesday, and Friday to see the comics. Who can remember to do that, with all the other stuff you have to remember every day?
How do you get your comics? (photo by cartese)

I had the idea that I should be supplying an RSS or Atom feed of my comic so those people who use readers like Feedly will be automatically given the new comics when they are available.

Since my comic server application is JavaScript on Node, of course I looked for a module on npm that would enable me to easily create feeds. The top one is simply called feed. And it worked fine. But I found a few limitations and quirks with it when I was creating Atom format.

First, the validation was a little off from the Atom specification. Link is not technically a required element of the feed element, but an error was generated if I didn't include it. It was also putting in a "hub" type link, but that's not an Atom thing - it's from the RSS 2.0 type. There was an option for "image" which mapped to the logo element, but there was not an option for specifying the icon element. The logo should be more like a banner (twice as wide as it is tall) and the icon should be square.

For individual entries in the feed, again link was mandatory when it doesn't have to be. It was also missing the published date option for entries.

Now don't get me wrong, it's a great package. The issues I found are relatively minor, so I forked the project on GitHub and made changes to correct them. My version is available at https://github.com/cwleonard/feed.

I am still playing around with how to structure the content and which optional elements make a difference to readers such as Feedly, so I don't recommend subscribing to the feed just yet. But here's an example of how I am currently using it:

var feed = new Feed({
    id: "http://amphibian.com",
    title: "Amphibian.com",
    description: "A web comic about frogs who run a technology company.",
    link: "http://amphibian.com",
    feed: "http://amphibian.com/feeds/atom",
    icon: "http://amphibian.com/simg/og_logo.png",
    copyright: "All rights reserved 2014, Casey Leonard",
    author: {
        name: "Casey Leonard",
        email: "casey at amphibian.com",
        link: "http://caseyleonard.com"
    }
});

var comics = loadComics(); // get the latest comics

for (var c in comics) {

    feed.addItem({
        title: comics[c].title,
        link: "http://amphibian.com/" + comics[c].id,
        description: "Amphibian.com comic for " + comics[c].pubDate,
        date: comics[c].pd,
        content: "comic html goes here"
    });

}

feed.updated = comics[0].pd; // date of the newest comic

var atom = feed.render("atom-1.0");

I'll be sure to announce when I think it's done and hope to get the comic listed in the "comics" category on Feedly as well.

Amphibian.com comic for 29 December 2014

Friday, December 26, 2014

What did you get for Christmas?

Another Christmas has passed and the new year will be here in a week. I still haven't had 2 minutes to write anything about the new JavaScript library I've been using off-and-on for the last few weeks, but hopefully that will change this weekend and all 3 of you that read this blog can be treated to that next week.

But let's take a moment to reflect on the gifts we've received for Christmas.

This year I got some coffee aged in oak barrels previously used to age Cabernet. I also got a stapler that looks like a frog.

When I was 12 years old, I got Visual Basic 3.0 (on floppy disks). That was an exciting Christmas. It was quite expensive at the time and I believe it might have been the only thing I got from my parents that year.

In 2001, I got two 15-inch LCD monitors from my girlfriend. I gave her an engagement ring that same year, and we've been married since September 2002. I would have given her the ring even if she hadn't given me the monitors.

Amphibian.com comic for 26 December 2014

Wednesday, December 24, 2014

Merry Christmas!

'Twas the night before Christmas...

Yes, it is Christmas Eve. I hope you have all your shopping done, if you're one of those people who buys gifts at Christmas.

My 5-part Christmas poem on amphibian.com wraps up today. I'll be spending this evening with my family in our traditional manner - pizza, Christmas Eve service at our church, and watching the Muppet Christmas Carol. The best film adaptation of Dickens' work, in my opinion.

The frogs will be back to their usual shenanigans on Friday, as will most of the world. I hope you have a Merry Christmas, and come back to read my blog when I return to writing about software development next week!

Amphibian.com comic for 24 December 2014

Monday, December 22, 2014

A Little More Daylight

Today there is more daylight than yesterday, but you probably didn't notice.

Yesterday, the 21st of December, was what people here in the northern hemisphere like to call "the shortest day of the year." I like to call it my father's birthday as well, but that's not the point. The point is, all days are actually the same length (unless they add a leap second or some crazy nonsense). Yesterday wasn't the shortest day of the year, but it was the day when the Earth received the smallest amount of solar radiation, due to the tilt of the planet's axis. Winter has officially started.

Today, the 22nd of December, will get slightly more sunlight. But just by a few seconds. Here in central Pennsylvania, we had 9 hours and 16 minutes of daylight yesterday. It won't be until the 26th when we can say we got 9 hours and 17 minutes.

But we're starting to pick up some momentum...we only have to wait until the 28th to get 9 hours and 18 minutes of sun, and we can add another minute on the 20th. At the beginning of January 2015, we'll be adding 1 minute per day, and by the end of the month we'll add 2 minutes per day.

By mid-March, we'll be enjoying around 12 hours of sunlight (when daylight and darkness hours are equal, Spring begins) and we are adding around 3 minutes daily. It slows down as we get closer to June 21, when we max-out at 15 hours and 5 minutes.

I like tables of this kind of information. Here you can find the daylight/darkness table for someplace close to where you live: Duration of Daylight/Darkness Tables

Amphibian.com comic for 22 December 2014

Friday, December 19, 2014

Finally Backing Up

I did something today that I should have done a long time ago - set up automatic backups of my amphibian.com database.

As you may or may not know (depending on how many of my blog posts you've read before), all of the data for my web comics is stored in a MySQL database. The words coming out of the frogs mouths, their positions in each cell, and the SVG images of the frogs themselves - all stored in simple tables in MySQL. I needed to be doing backups.

I set it up with cron and mysqldump. I wrote a simple bash script that will call mysqldump to create the complete backup of my amphibian database. It looks something like this:

#!/bin/bash
fn=/path/to/backups/amphibian-$(date +%d-%b-%Y).dump
mysqldump amphibian > "$fn"

To make sure each backup file gets a unique name, I create the fn variable which includes a section made out of the formatted date, in DD-Mon-YYYY format. To the Linux date command, that is %d-%b-%Y. Then I simply call mysqldump giving it a single parameter: the database name. I redirect the output to a file of the name I made above...and that's it.

Wait, why didn't it prompt for a user name and password to dump that database? Good question! That was my initial problem - I didn't want to put the password right in the script file. I found that if I make a file in my home area named .my.cnf and have it contain a section like this,

[mysqldump]
user = username
password = pw12345

...I can use mysqldump with a default user and without being prompted for a password. As long as the .my.cnf file has 0600 (read-write only owner) file permissions, it is reasonably secure.

I set my backup script to be called once per week by cron and I'm feeling much better now. I made an additional script that automatically copies the backup files offsite to a remote server for added safety. Being prepared for catastrophic data loss will really let me sleep better tonight.

Amphibian.com comic for 19 December 2014

Wednesday, December 17, 2014

You Know, That Christmas Poem

Okay, so my comic up until Christmas Eve is clearly a parody of "A Visit from St. Nicholas." You may know it better as "The Night Before Christmas", which is not actually its title but part of the first line.

This classic poem, written by Clement Clarke Moore way back in 1823, is largely responsible for our modern ideas about Santa Claus. My poem will most likely have no cultural impact whatsoever. But it is a chance for us all to think for a few minutes about the level of effort required for Santa's operation to make it on time every year. To meet his requirements, Santa can't be up there in his workshop hand-assembling dolls. He needs to be an expert at managing a production line of impossible size, and use the most advanced techniques for both planning and execution. Knowing the current wakefulness state of every child on earth, their position on the naughty/nice scale, and their desired gift list at any moment is clearly a monumental achievement of data science that is the envy of everyone from Google to the NSA.

And we haven't even gotten to the whole delivery phase of the operation yet. Amazon can experiment with flying delivery drones all they want, but only Santa Claus has a team of flying reindeer out delivering packages at speeds which imply some sort of controlled distortion of space-time.

There's no company on earth that wouldn't like to send someone to Santa for training - he's clearly mastered things that the rest of us don't even know exist yet.

I'm not the only one considering things of this nature. As part of your Christmas preparations this year, be sure to read Are Santa’s Reindeer Used for Propulsion or Navigation?, in which an MIT graduate student delves into the interesting question using Moore's original poem as evidence.

Amphibian.com comic for 17 December 2014

Monday, December 15, 2014

Making Some Pies

Shoofly Pie by Syounan Taji
My favorite kind of pie is Shoofly Pie, a molasses pie which originates among the Pennsylvania Germans. It is very similar to the English Treacle Tart, where golden syrup is used instead of molasses, which is also one of my favorite desserts when I'm in the U.K.

None of that has anything to do with the kinds of pies I made last week (before I got sick and spent several days barely awake). I made pie charts. You know, like doughnut charts, but without the hole in the middle. Both sound delicious but can't be eaten.

Also, don't confuse charts and graphs. It is my understanding that a graph is a type of chart that uses lines. Like a square is a type of rectangle.

Anyway, I wanted to take some of my data I capture from amphibian.com and plot it in a way that makes analyzing it easy. I am most interested in how many comics are accessed per day and which types of browsers people use to access the site. I have web services that generate the raw numbers from the server, so I wanted to find a JavaScript client library that could draw pretty pictures for me.

After experimenting with a few options, I went with Chart.js. Another popular recommendation was Data-Driven Documents, D3.js, but that was really overkill for my needs. I like to keep it simple.

Chart.js lets you take JSON data and render 6 different chart types using HTML5 Canvas. You can extend it to create your own additional chart types if you want, but the ones already included meet most needs: Line, Bar, Radar, Polar Area, Pie, and Doughnut. One of its best features is that it allows the charts to be responsive. When enabled, it resizes the canvas element as the parent element changes and the chart still looks good.

Using it on a web page is easy. First, just download the library and include it on your page:

<script type="text/javascript" src="/js/Chart.min.js"></script>

Now put a Canvas element, or elements, on your page where you want to draw the chart(s). There's a tricky thing about Canvases in that they really need width and height attributes, which is fine except when we want to have responsive charts that size with the browser. When going responsive, the trick is to just set the size very small but maintain the proportions, such as 80x60 instead of 800x600 and let Chart.js size it up for you - you won't even notice.

<div style="width: 50%">
  <!-- I really want this chart to fill the div automatically -->
  <canvas id="chart1" width="80" height="60"></canvas>
</div>

All that's left to do is organize your data in an appropriate way for the type of chart you'd like to draw, and pass it to the appropriate Chart function. Let's look at a simple line chart for example, but the Chart.js documentation has plenty of detail on all the other options plus more customizations.

var chartData = {
        "labels": ["A", "B", "C", "D", "E"],
        datasets: [
            {
                label: "Something",
                fillColor: "rgba(151,187,205,0.2)",
                strokeColor: "rgba(0,51,102,1)",
                pointColor: "rgba(151,187,205,1)",
                pointStrokeColor: "#fff",
                pointHighlightFill: "#fff",
                pointHighlightStroke: "rgba(220,220,220,1)",
                data: [33, 57, 42, 88, 101]
            }
        ]
    };

var opts = {
    "scaleGridLineColor": "rgba(0,0,0,.08)",
    "datasetStrokeWidth": 3,
    "responsive": true
};
 
var ctx = document.getElementById("chart1").getContext("2d");
var myChart = new Chart(ctx).Line(chartData, opts);

Line charts need a set of labels for the X-axis, and at least one set of data for drawing a line. You can actually draw more than one line on the same chart, useful when comparing data, which is why the datasets field is an array (even though in my example it only had one element).

Each dataset object should contain a label, color-related information for the lines and fills and points, and the set of values to plot on the Y-axis. Those values should in in an array given as the data field.

An options object can be used as well. There are a number of "global" chart options but each kind of chart has some specific options you can use. I customized the color of the scale grid lines, the stroke width of the data line, and made sure to set responsive to true.

All you need to do is pass the 2D context from the appropriate Canvas element into Chart and pass the the chart data and options to the chart type (see the final 2 lines above). You get back a reference to the chart, which you can use later to update the data and redraw if you want.

A chart that is always 50% of the page width

I was pleased with how simple it was to create and customize the simple charts I needed. I would recommend you check out Chart.js if you have similar needs on a project.

Sorry if you are disappointed by the lack of actual pie making in this blog. I know, I didn't even show how I made a pie chart! I did actually make some, after I made a line chart - but I'll let that be an exercise for the reader (or you could check out the amphibian.com repo on GitHub).

Amphibian.com comic for 15 December 2014

Friday, December 12, 2014

Sweet Dreams (of the 1980's)

Whenever I hear the word "logarithm" I can't help but think of this song...


I guess it's just because word "logarithmics" sounds so similar to "Eurythmics". You can understand my confusion, right? Plus, growing up in the 80's I must have heard this song 43,000 times - long before I ever knew what a logarithm was.

While I'm at it, here's a short list of more songs that were the soundtrack of my childhood.
Someday, we're going to look at these songs the same way we look at classical music today. You know, you'll take your flying car to the symphony to listen to them perform some Bon Jovi.

Until that day, we'll have to settle for symphonetic renderings of Edvard Grieg. Or, this...


Wednesday, December 10, 2014

The Christmas Song Lyrics You Never Knew

I'm feeling a bit under-the-weather today, so instead of a post about how to use some JavaScript technology or how to make a paper frog I'd like to help you with your Christmas songs.

Jingle Bells


I would have to suspect that you have never seen more than the first verse of this song. Few people know the rest of it.

Here are the complete lyrics:
Dashing through the snow
In a one-horse open sleigh
O'er the fields we go
Laughing all the way
Bells on bobtail ring
Making spirits bright
What fun it is to ride and sing
A sleighing song tonight!
(chorus)
Jingle bells, jingle bells,
Jingle all the way.
Oh! what fun it is to ride
In a one-horse open sleigh.
Jingle bells, jingle bells,
Jingle all the way;
Oh! what fun it is to ride
In a one-horse open sleigh. 
A day or two ago
I thought I'd take a ride
And soon, Miss Fanny Bright
Was seated by my side,
The horse was lean and lank
Misfortune seemed his lot
He got into a drifted bank
And then we got upsot. 
(repeat chorus) 
A day or two ago,
The story I must tell
I went out on the snow,
And on my back I fell;
A gent was riding by
In a one-horse open sleigh,
He laughed as there I sprawling lie,
But quickly drove away. 
(repeat chorus) 
Now the ground is white
Go it while you're young,
Take the girls tonight
and sing this sleighing song;
Just get a bobtailed bay
Two forty as his speed
Hitch him to an open sleigh
And crack! you'll take the lead. 
(repeat chorus)
The rest of the song isn't quite as catchy as that first verse, huh?

Good King Wenceslas


Now I'll share the words to one my favorite Christmas songs that isn't really even a Christmas song. Of course I'm talking about "Good King Wenceslas." You hear the music to this song all the time but rarely does anyone sing the words. It's not technically a Christmas song if you pay attention to the first line: "Good King Wenceslas looked out, on the Feast of Stephen." The Feast of Stephen takes place on December 26th, but I guess that's close enough. Also worthy of note, Wenceslas wasn't ever even a king in his lifetime - the title was bestowed on him after this death. Here are the words:
Good King Wenceslas looked out
On the feast of Stephen
When the snow lay round about
Deep and crisp and even
Brightly shone the moon that night
Though the frost was cruel
When a poor man came in sight
Gath'ring winter fuel

"Hither, page, and stand by me
If thou know'st it, telling
Yonder peasant, who is he?
Where and what his dwelling?"
"Sire, he lives a good league hence
Underneath the mountain
Right against the forest fence
By Saint Agnes' fountain."

"Bring me flesh and bring me wine
Bring me pine logs hither
Thou and I will see him dine
When we bear him thither."
Page and monarch forth they went
Forth they went together
Through the rude wind's wild lament
And the bitter weather

"Sire, the night is darker now
And the wind blows stronger
Fails my heart, I know not how,
I can go no longer."
"Mark my footsteps, my good page
Tread thou in them boldly
Thou shalt find the winter's rage
Freeze thy blood less coldly."

In his master's steps he trod
Where the snow lay dinted
Heat was in the very sod
Which the Saint had printed
Therefore, Christian men, be sure
Wealth or rank possessing
Ye who now will bless the poor
Shall yourselves find blessing
I don't understand why more people don't sing the words. I've always liked them.

Amphibian.com comic for 10 December 2014

Monday, December 8, 2014

Frog Paper Craft

Today, we're not going to build anything out of code. We're going to use a more primitive medium.

You may notice that my frogs are constructed out of nothing more than simple geometric shapes. I used to draw them by hand this way, but now I draw them as Scalable Vector Graphics in Inkscape. Since SVG defines the shapes using mathematical formulas you might say that the frogs are made out of math. Yesterday my daughter wanted to make a craft, and I came up with this idea: making frogs out of paper!

I made up some templates for the parts of one of my frogs and printed them out on colored paper. We cut and glued the paper together with some cardboard and had some fun. Now I'm going to share the fun with you!

Step 0.

You'll need some thick cardboard, scissors, glue, a marker, and colored paper. Print out the templates of the different pieces on colored paper (see links below). Print the body template on green paper, the bellies template on yellow, and the eyes on white. Or use other colors if you want some more interesting frogs!

Frog Body Template
Frog Belly Template
Frog Eyes Template

Frog Body - Print on Green Paper

Frog Bellies - Print on Yellow Paper

Frog Eyes - Print on White Paper

Step 1.

Cut out all the frog parts. Get a fun color paper for your background (I use purple in my example). Glue the back eye, arm, and leg on your paper something like the following picture.


Step 2.

Glue one of the body pieces on top of the eye, arm, and leg. Glue one of the belly pieces on top of that.


Step 3.

Cut out some cardboard pieces and glue them on as pictured below. The thicker the cardboard, the better. It will give the frog some depth.


Step 4.

Glue the other body piece on top of the cardboard. Glue the other belly piece on top of that. In the picture below, I have the body slightly transparent so you can see the placement.


Step 5.

Glue the second eye, arm, and leg on top of the body and exposed cardboard pieces. Again, I use transparency in the picture to help show the placement of the parts.


Step 6.

Almost done! Get your marker and draw in the eyes and mouth. Your frog craft is complete!


Here are the ones I made with my daughters, but that was before I wrote these nice instructions. Yours should turn out even better.


Send me pictures of your complete crafts!

Amphibian.com comic for 8 December 2014

Friday, December 5, 2014

Your Internet Thermometer

Photo by Bernard Gagnon
It's amazing what access to the Internet on phones has done to us. When I wake up in the morning, I look at my phone to check the outside temperature. I remember when I was a kid and we had to use antiques like thermometers stuck on the outside of our windows!

In today's comic, the frog is trying to get the body temperature reading from another frog. He fails to realize that frogs, being ectotherms, will basically have the same body temperature as that of their environment. His phone shows him (and you, the reader of the comic) the actual temperature from my town here in central Pennsylvania. Look at the comic today and you'll see today's temperature. Come back in 6 months and you'll see a warmer temperature (I hope). Whatever temperature it is here when you load the page, that's what you'll see.

Getting weather data for my comic is not difficult. I basically do the same thing that the weather app on your phone is doing - accessing a server somewhere that has been collecting data from weather stations around the country and getting the latest information for a certain location. I have used both Weather Underground and OpenWeatherMap to get data, as both have APIs that anyone can use. In the case of my comic, I wrote a simple module for Node that I use to manage the API calls.

var http = require('http');

module.exports = function(conf) {

    var currentTemp = 50;  // start with some reasonable value

    var apiKey = conf.weatherApiKey || "";
    var location = conf.location || "";

    function pullWeatherData(cb) {

        // OpenWeatherMap
        var url = "http://api.openweathermap.org/data/2.5/weather?id="
                + location + "&units=imperial&APPID=" + apiKey;

        // Weather Underground
        //var url = "http://api.wunderground.com/api/" + apiKey
        //        + "/conditions/q/" + location + ".json"; 

        http.get(url, function(res) {

            var wData = "";
            res.on("data", function(chunk) {
                wData += chunk;
            });
            res.on("end", function() {
                try {
                    var data = JSON.parse(wData);
                    cb(null, data);
                } catch (e) {
                    cb(e);
                }
            });

        }).on("error", function(e) {
            cb(e);
        });

    }

    function weatherTimer() {

        pullWeatherData(function(err, data) {
            if (err) {
                console.log(err);
            } else {
                try {

                    // OpenWeatherMap
                    if (data.main.temp) {
                        currentTemp = data.main.temp;
                    }

                    // Weather Underground
                    //if (data.current_observation.temp_f) {
                    //    currentTemp = data.current_observation.temp_f;
                    //}

                    console.log("Setting current temp to " + currentTemp);

                } catch (e) {
                    console.log("Unable to update temp");
                    console.error(e.stack);
                }
            }
            setTimeout(weatherTimer, 1800000); // call again in 30 minutes
        });

    }

    weatherTimer(); // run now, and then every 30 minutes

    return {
        temperature: function() {
            return currentTemp;
        }
    };

};

The goal of this module is to give me access to the current temperature value and update that value automatically on a timer. Temperature doesn't usually change that quickly so every 30 minutes is fine for my application.

There are really only two functions - one that calls the API to get the data and the timer that asks for new data and updates the temperature. In my code above, I show it using the OpenWeatherMap API with the Weather Underground stuff commented-out, in case you want to try that one as well.

The first function I called pullWeatherData. It simply calls http.get for whichever service you want to use and reads the response stream, in JSON format, into a string which is parsed into an object at the end. I had to make sure error conditions were handled gracefully, because when you call external APIs you sometimes get garbage back. That's the point of the try/catch around the JSON.parse call - the response might not actually be JSON (it could be a plain text HTTP 500 page, for example) and you don't want to crash your whole app. So if there is any kind of error, the callback function given to pullWeatherData is called with the error as the one and only parameter. In the case of a successful call and parse, the callback function is called with a null as the first parameter to indicate no errors and the weather data object as the second parameter. The exact format of this object will depend on which service you called.

The weatherTimer function simply calls pullWeatherData and then does something with the data before setting a timeout for calling itself again in 30 minutes. All it really does with the data is take the temperature value out of it and store it in a variable. If everything works, that variable will be automatically updated every half hour.

After defining these two functions, my module calls weatherTimer in order to get the data for the first time and start the timer for the next call. The return value is an object with a function simply called temperature which will return the current value of the temperature variable. Here's an example of how the module would be used in an app:

var express = require("express");
var app = express();

/*
 * OpenWeatherMap uses their own location ids.
 * Id 5184309 is near Reedsville, PA.
 * 
 * Weather Underground can use ZIP codes.
 * ZIP 17084 is Reedsville, PA.
 */
var weather = require("./weather")({
    weatherApiKey: "your_key_here",
    location: "5184309"
});

app.get("/", function(req, res, next) {
    res.send("weather demo");
});

app.get("/temp", function(req, res, next) {
    res.send("current temperature is: " + weather.temperature() + " deg F");
});

// ------------ start listening
var server = app.listen(3000, function() {
    console.log("listening on port %d", server.address().port);
});

Just like the comic, this sample app will tell you the temperature here in Reedsville if you point your browser at the /temp path. As you can see, it's cold in Reedsville as I write this.


If you want, you could modify this module to get different weather data or more data and store it in a more complex object. You could really just store off the entire response object and access different values each time. You might even apply this pattern to other non-weather APIs as well. Be creative!

Amphibian.com comic for 5 December 2014

Wednesday, December 3, 2014

What's Not on my Christmas List

I don't want a smart watch for Christmas. I don't want a new iPad. I don't want a new video game system. Or a 1TB SDD. Or pretty much anything else you would probably guess.

Much to the displeasure of my family, I rebel against the commercialism of Christmas. Corporate America does a pretty good job of convincing all of us that Christmas is about buying things for people. It gets more extreme every year. A few years back, I'd just had enough of it. I don't need anything. No one in my immediate family needs anything. There are many people in the world that do need things, but we don't know many of them. We spend all our time and money worrying about the people near us who most likely have few material needs.

No, I'm not some kind of Ebenezer Scrooge, sitting in my office counting stacks of coins and complaining about the Christmas season. I love Christmas, I just don't feel the need to celebrate it with consumerism. I want to celebrate it in a more appropriate way.

Here are my guidelines of Christmas gifts.
  1. When giving a gift, it should be something that the recipient either can't or won't buy for themselves. This could mean making something to give as a gift, but it can also mean buying something that the recipient would like to have but is too impractical for them to purchase on their own.
  2. Choose quality over quantity. My grandfather lived by this rule in everything. But at Christmas, a single thoughtful gift is worth more than a whole mountain of inconsequential junk.
  3. Try presence instead of presents. Is the giving of a gift the best way to show our love for another person? It depends on the person. Some people really respond to gifts, but others feel love more through quality time spent with them or acts of service done for them.
These are not just for me to follow when thinking of others - I want people to try this when they think of me as well.

Studies have shown that materialistic people are less happy (here, here, here, and here, for starters). Try my guidelines in your family and see if you have a happier and more meaningful Christmas.

Amphibian.com comic for 3 December 2014

Monday, December 1, 2014

Authentication with Passport

Since today's comic is about unauthorized access to a computer system, it seems like as good a time as any to talk about how I do authentication in my Node web apps. I use the Passport authentication middleware, which integrates nicely with the Express framework. It can be a little tricky to get everything configured - I can't remember how many hours I spent on it the first time. Here I'll give you an example of the simplest possible way to set it up.

First, you have to make sure you have all the required dependencies. While Passport won't complain without things like body-parser, it won't actually function properly. To use Passport, you'll need express-session as well, and express-session requires cookie-parser. Additionally, you'll need to grab whatever particular authentication strategy you want to use with Passport. In my example, I use passport-local which is the simplest thing to demonstrate. There are lots of others though - once you see how this works you can experiment with them.

So here's what my package.json looks like for this demo app:

{
  "name": "passport-demo",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "4.10.4",
    "cookie-parser": "1.3.3",
    "body-parser": "1.9.3",
    "express-session": "1.9.2",
    "passport": "0.2.1",
    "passport-local": "1.0.0"
  }
}

I also have 2 really simple HTML pages that go along with this. One is just the index.html that shows some generic welcome text. The other is the login form, form.html. That one is how you can submit your login credentials. Here is the main page:

<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Hello</title>
</head>

<body>

<p>Thanks for visiting.</p>

</body>

</html>

and here is the login form:

<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Login</title>
</head>

<body>

  <form action="/login" method="post">

    <input type="text" name="username" />
    <input type="password" name="password" />
    <input type="submit" />

  </form>

</body>

</html>

Now for the good stuff. It starts out like any other Node app with all the requires. Then the Express App is created.

var express = require("express");
var bodyParser = require("body-parser");
var cookieParser = require("cookie-parser");
var session = require("express-session");
var passport = require("passport");
var LocalStrategy = require("passport-local").Strategy;

var app = express();

We've come to the Passport-specific items. You have to tell Passport which authentication strategy you want to use. I am going to use the LocalStrategy, which means I will handle all user authentication myself. Typically you do this by looking up users in a database or something like that. When you create a new LocalStrategy, you have to supply it with the function it will call to perform your particular type of authentication. Here's my example:

var auth = function(username, password, done) {

    var err = false; // if an error occurs while you are checking
    var credsGood = true; // if user credentials are good

    if (err) {
        done(new Error("data access error"));
    } else {
        if (credsGood) {
            done(null, { userid: username });
        } else {
            done(null, false, { message: "Invalid credentials" });
        }
    }

};

// --- set up login strategy
passport.use(new LocalStrategy(auth));

As you can see, my simple auth function always returns a positive authentication. In reality, you'd use some mechanism to determine if the supplied username and password are good instead of just assuming they are. There are three possible ways to return from the function. First, if you run into some kind of error (your database is down) you can call done and pass it an Error. If the supplied credentials are good, call done passing in a null for the first argument (no error) and a user object for the second parameter. Finally, if the supplied credentials are not acceptable, call done with a null as the first parameter (no error), a false as the second parameter (authentication failed) and an object containing more information about the failure as the third parameter.

The next things you need to set up are the Passport serializer and deserializer. Basically, the serializer needs to return a unique key given a user object and the deserializer needs to return a user object given that unique key. The user objects need not be in some specific format - they are your objects, whatever you returned from the auth function supplied to the LocalStrategy. In my example, I just use the userid as the unique key returned from the serializer and then recreate the object with that one field in the deserializer. My user objects are so simple I don't need to do anything else, but your application might store more data in them. In that case you probably would want to put your users in some sort of cache when serializing and pull from there when deserializing. So here's my code:

/*
 * this serializer doesn't do much, just returns the userid
 */
passport.serializeUser(function(user, done) {
    done(null, user.userid);
});

/*
 * this deserializer actually performs no user lookup, just
 * recreates the user object. my user object is not really
 * much of anything at this point, just the userid.
 */
passport.deserializeUser(function(id, done) {
    done(null, { userid: id });
});

The rest of the app is fairly standard. I just set up all the middlewares, making sure to do them in the right order. The last ones you do should be passport.initialize() and passport.session(). Here is the code for the remainder of the app:

// used on resources that you have to be authenticated to use
function ensureAuthenticated(req, res, next) {
    if (req.isAuthenticated()) {
        return next();
    }
    res.sendStatus(403);
}

app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser()); // required before session.
app.use(session({ secret: "some secret", resave: false, saveUninitialized: false }));
app.use(passport.initialize());
app.use(passport.session());

app.post("/login", passport.authenticate("local", {
    successRedirect: "/",
    failureRedirect: "/form.html"
}));

app.get("/unprotected", function(req, res, next) {
    res.send("anyone can see this");
});

app.get("/protected", ensureAuthenticated, function(req, res, next) {
    res.send("you have access");
});

// ------------ static content (index.html and form.html)
app.use(express.static("public"));

// ------------ start listening
var server = app.listen(3000, function() {
    console.log("listening on port %d", server.address().port);
});

I set up 3 routes in this app, one for the login credential processing, one unprotected route for an example, and one protected route for an example. The login route is what the form in form.html POSTs to. It simply calls the passport.authenticate function specifying the strategy to use, "local" in this case, and an object defining the success and failure redirects. If authentication is successful, I redirect back to the main page, and if it is unsuccessful I redirect back to the login form page.

You'll note that I have to create a middleware to protect my protected route. My ensureAuthenticated function can simply call req.isAuthenticated(), a function provided by Passport, to see if the user has access. If the user is good, I can just call next(), but otherwise I return a 403 Forbidden. All you have to do to protect any route is add this function to the middleware chain, just like I did when I defined the /protected route.

Put this all together and give it a try. You shouldn't be able to access /protected until after you've been to /form.html and submitted some credentials. I hope this example saves you a few minutes when you are setting up authentication, and make sure you check out the comic link below!

Amphibian.com comic for 1 December 2014

Friday, November 28, 2014

Get Hooked


Have you heard about microservices yet? There's been no small amount of discussion about their benefits recently. Even Martin Fowler says that the microservices pattern of enterprise system architecture has seen many positive results among his colleagues in the past few years (read his whole article here).

If you didn't click on the link, I'll summarize it for you here: the microservice architectural style is an approach to developing a single application as a suite of small services that communicate through very lightweight mechanisms such as HTTP APIs. What does that mean? It means you break your big monolithic system down into a bunch of small parts that each do a single job and are deployed independently.

I decided to play around with a popular microservice framework based on Node.js, hook.io. It is a very interesting premise. You set up a public Gist at GitHub, which the hook.io framework executes when called. For my experiment, I created a hook which can give me the date for Easter for any given year.

The first step is to create a public Gist for your hook. You can find mine here, but it's basically just the following code:

module['exports'] = function easter(hook) {
 
  var year = hook.params.year;
  hook.debug("i am going to calculate the date of Easter for " + year);
  
  // using the "Meeus/Jones/Butcher" algorithm
  var a = year % 19;
  var b = Math.floor(year/100);
  var c = year % 100;
  var d = Math.floor(b/4);
  var e = b % 4;
  var f = Math.floor((b + 8) / 25);
  var g = Math.floor((b - f + 1) / 3);
  var h = ((19*a) + b - d - g + 15) % 30;
  var i = Math.floor (c/4);
  var k = c % 4;
  var L = (32 + (2*e) + (2*i) - h - k) % 7;
  var m = Math.floor((a + (11*h) + (22*L)) / 451);
  var month = Math.floor((h + L - (7*m) + 114) / 31);
  var day = ((h + L - (7*m) + 114) % 31) + 1;
  
  hook.debug("month is " + month);
  hook.debug("day is " + day);
  var monthNames = [ "January", "February", "March", "April", "May", "June",
    "July", "August", "September", "October", "November", "December" ];
    
  hook.res.end("Easter is " + monthNames[month-1] + " " + day + " in " + year + "\n");
 
};
 
module['exports'].schema = {
  "year": {
    "type": "number"
  }
};

On the first line, you define your module's exported function. Since every hook is passed a Hook object as its first argument, I define that as the lone parameter.

What will that Hook object look like? According to the hook.io documentation, there are a number of fields available on it. For my simple purposes, the most interesting will be Hook.debug and Hook.params. The debug field is a function that you can call to dump information out to a debug console. It is actually quite nice because all your debug ends up as an array of objects with time, data, and ip fields. But I'm getting ahead of myself...

On line 3, I use the year param to get the year that will be passed in when my hook is called (more on how that happens a little later). On lines 4, 22, and 23 I output some debug, but the rest of the function is just the algorithm for calculating Easter's date. It's quite an interesting problem, but in the end I come up with a friendly result string which I return to the caller on line 27 by passing it to the Hook.end function.

Now that I've made a Gist, I can make a new Hook. You can too! Just go to http://hook.io/new and give your hook a name and the URL of your Gist. I named my hook "easter". For the Hook Theme, I just chose "simple" but that doesn't matter a whole lot in my example. That was it - my hook was easy to create.

Now what? How do you actually run the thing? Your hooks can be accessed by URL at
http://hook.io/<your userid>/<hook name>
and the parameters can be passed along as parts of the query string. For example, if I want to run my Easter date hook and pass in a year of 1918, I would just go to the following URL:
http://hook.io/cwleonard/easter?year=1918
Note how the parameter is named year, which matches the field name I was looking for on line 3.

There's one minor gotcha here, and that is how the framework processes requests based on the HTTP Accept header. If I go to my hook's URL from my web browser, it won't actually run. Instead, it gives me a nice human-readable form to fill out and push a "run" button. This is because browsers generally set "Accept" to "text/html" and hook.io is trying to help me out by giving me a friendly form. To get around this, just put "&run=true" on the end of the URL. Don't worry, if you call the URL by other means (from JavaScript, curl, etc.) the "Accept" header won't be set to "text/html" (unless you want it to be) and it will run automatically. See the documentation for more details.

Now that I've covered all those details, try going to this URL:
http://hook.io/cwleonard/easter?year=1918&run=true
You should see a successful result and finally learn that Easter was on March 31st in 1918! Scroll down to the bottom of the results page and you can see the debug output as well.

A cool feature here is that if I find a bug in my code or just want to change my algorithm, all I have to do is edit the Gist. I don't have to do anything on the hook.io site, it just picks up the change next time I run.

One final thing I should explain is how hook.io is able to make that nice HTML form for you to enter the year if you forgot to add "&run=true" on the end. Note lines 31-35 of the Gist. They specify a schema for the hook, which allows forms to be automatically generated. My schema is extremely simple since I only have one field.

There are many interesting ways to use a microservice framework such as this. Imagine if you decomposed your big do-everything application into a bunch of small hooks and then tied them all together from a lightweight container. Even in my simple web comic, I have dozens of routes which could be broken out into microservices - one that just processes the images, one that serves comic data, one that updates the stats, etc.

Using curl to find Easter
You can even use command-line tools such as curl to access these microservices. The possibilities are endless! Use your imagination and come up with some cool new ideas!

I hope this simple example was helpful if you are going to try making some microservices of your own. There are a lot more features to the hook.io framework that you should check and and experiment with. Read the documentation (it's not that long!) to discover all the options. Remember, when you think big, think small.

Amphibian.com comic for 28 November 2014

Wednesday, November 26, 2014

Celebrating Thankfulness

Tomorrow is Thanksgiving Day here in the United States. An official holiday only since 1863, but celebrated for as long as European colonists have inhabited the continent. Traditionally, we trace the Thanksgiving celebration back to the Pilgrims of Plymouth, Massachusetts. Their first celebration of thanksgiving for the harvest (and their survival after a year in New England) took place in 1621, but most likely earlier in the year. The date of the feast was possibly September 29th, according to one historian. That date coincides with Michaelmas, which incidentally is celebrated as Goose Day here in central Pennsylvania.

Well, now that we've covered the background, let's talk about today. How do we celebrate our thankfulness for living in the richest nation on earth in the richest time in history? Many of us celebrate by overeating and falling asleep in the afternoon. We'll need our rest because at midnight we get up and go out to the shopping malls and attempt to kill each other in order to get a slight discount on some junk that we probably don't really need.

Huh? Does that sound like the behavior of people who are truly thankful for what they have?

Our behaviors around this time of the year seem more like celebrations of gluttony and greed than thankfulness. Now, I like turkey and mashed potatoes as much as the next guy (maybe even more so - we cook a turkey here at least 5 or 6 times per year) but mass consumption of the native fowl does little to show how thankful we are.

What does true thankfulness look like? To be honest, I'm not entirely sure. Maybe I'm not thankful enough, or at least don't show it enough. I believe that a person who is really thankful would display qualities such as contentment, joy, and charity. If I asked my family and friends, would they say that I exhibit those traits?

Perhaps what we need is for some modern-day Charles Dickens to write a Thanksgiving Carol and restore the true meaning of the holiday. I'll start working on it. The main characters will all be frogs.

Amphibian.com comic for 26 November 2014

Monday, November 24, 2014

So, about those cookies...

Today I am finally going to talk about cookies. Yes, the tagline of the blogs does read "Software, and frogs, and cookies" but if you've been reading it for a while you've probably noticed a distinct lack of cookies. I actually did talk about cookies in my review of the iPhone game Cookie Dozer back in 2010, but that's been the only time. Four years! And I've never before shared any information about the kind of cookies you can actually eat. Keep reading.

If you've ever read my comic you'll know that it's mostly bad technology puns. That has never been more evident than today, where we find the frogs handing out chocolate chip cookies to track potential customers instead of using the web browser key/value storage mechanism.

You might have not given it much thought before, but just how does Facebook know to put up an ad for some item I was just looking at on Amazon? The answer is because both sites use third-party cookies to track your web activity. It works something like this...
You view a product page on Amazon. Somewhere on that page is an invisible element, often an image, served by a third-party website. Let's say it comes from example.com, for example. Since it gave you some content, it can also set a cookie that only example.com can read. It will generate some unique identifier for your activity and store that in a cookie on your browser. On the server site, they put the cookie id in a database along with information about the page you were on when you got the cookie.
The next day you visit Facebook to share some cute picture of your cat. Facebook also serves you an element from example.com, in this case probably an ad banner. But since example.com can read any other cookies set by example.com, they pull the id out of the cookie you got when you visited Amazon the day before. They quickly look up in their database what page you were on when you got the cookie and then respond with an ad banner for that specific product.
You say to yourself, "Oh, yeah. I wanted to buy that yesterday but I forgot." Then you click on the ad and buy the item. 
Some people get really bent out of shape about this practice. Yes, it's true that if enough sites on the Internet serve you content from example.com, they could build up quite a lot of data about you and your browsing habits. For the most part, they just want to use it to more precisely target advertising at you. There is always the potential, however, that this data could be used for something more nefarious. Fortunately, most modern browsers make it easy enough for you to block third-party cookies (meaning when you go to Amazon.com, you can only get cookies from Amazon.com - not from example.com) and you're fairly safe.

Over the years, the use of cookies in this way has given them a bad name. Really, there's nothing wrong with a site like amphibian.com setting a cookie in your browser when you are actually on amphibian.com. Websites legitimately do this to overcome the stateless nature of web applications.

To make you feel better about cookies, today's comic will set a harmless cookie in your browser. It can only be read by the comic website, and it doesn't contain any information about you. But it does contain a recipe for chocolate chip cookies!

If you want to see it, open up your browser's developer console and type the following command:
$.cookie('frog-tracking-cookie');
Just hit enter, and you should see the cookie recipe. I kept it short, but you should be able to understand it.

If you've gone as far as entering that command, you may also be interested to learn why it works. I use the jquery.cookie plugin for reading and writing cookies from the comic. On some future date, the comic will attempt to read back the cookie it sets today...you'll just have to wait and see what happens!

Amphibian.com comic for 24 November 2014

Friday, November 21, 2014

Assessing Your Assessments

I don't know where you work. Maybe you don't even have a job. But if you have a job, I will bet that you get a performance review at least once per year.

It's that time of year right now where I work. Unfortunately, this type of process is extremely pervasive - almost all companies do it in some form. It is theoretically possible that you like it. Perhaps you always get good reviews and your boss loves you. I usually receive good reviews, but there are some parts of the system that still bother me.

We use a process in which employees are asked to submit written evaluations of the people they worked with closely that year. In addition to that, we submit a self-assessment. Our managers use this information to come up with a summary of our performance.

The first problem is that people just don't like writing reviews of their coworkers. We all drag our feet every year and miss the deadlines to turn in the reviews. Then our managers prod us until we do it.

The other problem with this is that it relies on everyone being very objective. Human beings are very bad at being objective. And for the most part, people don't like to write critical things about the people they have to work with every day. Even when the inputs we provide are confidential, word gets around and people have a way of finding out who said what. This tends to artificially inflate ratings. Maybe the guy in the cubicle next to you has some legitimate issues that need to be addressed, but you don't want him to be in a bad mood after getting his evaluation because you have to sit next to him.

You know, if you don't have anything nice to say, don't say anything at all. People might sometimes tell others verbally that you do a terrible job, but most people don't want to put that in writing with their signature on the bottom.

The last problem (that I'll mention today) is that when you write your self-assessment, you are probably saying really great things about yourself. You are awesome. You should be employee of the year. You are personally responsible for $12.4 million in sales this year. You can leap tall buildings in a single bound. What are the chances that you'll mention anything negative? How is that useful at all to your manager? Best case is that she agrees with you, but worst case is that she has to shatter your superlative self-image with the cold, hard hammer of reality. Nobody likes that.

For the record, I tend to be more critical of myself than most people. One year, let's call it 2007, my self assessment was due on a day that was going very badly. Things were broken, We were way behind schedule. Terribly over budget. Our project could be cancelled any day. How was I to assess myself for that year? I filled out the form with a single sentence: "This year I accomplished nothing of any value." The next day, my manager called and politely asked me to update the document.

If you agree (or not) with any of this, you might also want to read this article: Get Rid of the Performance Review. It's from 2008, but not much has changed.

And have fun writing those reviews!

Amphibian.com comic for 21 November 2014

Wednesday, November 19, 2014

One Pepperoni

Is the word pepperoni singular or plural? Or both?

I admit that I didn't know before I tried to write about a pair of pepperoni pizzas that were appointed to be a frog's marketing team. It makes more sense if you've heard about Amazon's 2-Pizza Teams. Or maybe it makes less sense.

But anyway, it turns out that our American word pepperoni is just how we messed up peperoni, which is the plural of an Italian word for a bell pepper. I don't really know how that happened, since most people think of spicy meat (but from what animal?) when they think of pepperoni - not sweet peppers. So is it plural then? Not really...

The general consensus is that pepperoni is plural only when you're actually saying peperoni and you're speaking Italian. When you're speaking American English about the zesty red circles on your pizza, it is singular. It's pretty much a totally different thing, even though it probably sounds the same. Hey, it's not the first word we've hijacked.

This means that I wrote pepperonis when referring to more than one of them. Even though that looks weird. Lots of words look weird when you write them down I guess. Like vacuum. And bologna. And phlegm.

Amphibian.com comic for 19 November 2014

Monday, November 17, 2014

Cupcake Clicking

It has finally arrived! Today is the first of my comics which includes an interactive component. This was one of my goals when I set out to create a web comic that was really part of the web almost 9 months ago. It's been a long time coming, but I wasn't going to make comics that you click just for the sake of clicking - it had to be part of the joke.

If you haven't seen it yet, go look at it now and come back: Baited Breadth, Amphibian.com for 17 November 2014.

The more times you click on the "click bait" cupcake, the more success the frog can report to his vice president in the last cell. And the more clicks he captures, the more he is rewarded. The highest reward comes at 150 clicks, so your finger might get a little tired earning the frog that honor.

Obviously, there's a JavaScript component here. It's not the most complicated thing in the world, but it did have to work with the general framework on the comic. It's not actually the first JavaScript component either, the 3D comic last week had JavaScript which enabled turning on and off the stereoscopic version of the final cell.

The basic premise is this: when you load the comic page as a whole, which means you type "amphibian.com" directly into the browser or follow a link, any special JavaScript required by that comic is downloaded with all the rest of the JavaScript. But if you navigate around using the "Previous" and "Next" comic links, the whole page doesn't reload. In those cases, just the HTML of the comic cells is sent to the browser via AJAX. Any custom JavaScript is loaded dynamically at that time. If you go back and forth, it means that custom JavaScript can be loaded multiple times on what is essentially a single page. I had to be mindful of that fact.

Here is a stripped-down version of the cupcake clicker code.

$(function() {

    // put variables here that need to persist between clicks 
    var clickCounter = 0;

    $("#cupcake").unbind("click"); // in case this gets executed more than once

    $("#cupcake").click(function() { 

        // this is where you do stuff for the click

        clickCounter++;
        if (clickCounter > 30) {
            // do something special
        }

    });

});

First, note how the whole thing is an anonymous function given to the jQuery $() function. This means the code inside will get executed after the document is first ready (in the case of the JavaScript being pulled down on an initial page load) or immediately if the document is already done (in the case of the dynamic loading).

Because this can get called multiple times, I make sure to remove all existing "click" bindings from the cupcake on line 6. If I don't do this, jQuery will keep adding additional "click" bindings on for each call - meaning that clickCounter will be incremented multiple times for each click and other such weird behavior!

Line 8 adds the "click" listener. Because it creates a closure containing the clickCounter variable, it can be accessed with each click and maintain its value.

Hopefully this is just the first of many interactive frog & technology jokes that will appear. I'm going to try to make some that use more advanced features available to the browser, especially on mobile. Things like geolocation and the accelerometers. Keep reading!

Amphibian.com comic for 17 November 2014