javascript

Using Ghost's Public API to Select Five Random Posts

One of the changes I made recently to this site is the addition of the "Five Random Posts" area on the left sidebar. Here's a screenshot in case I decide to change my layout later:

A screenshot of this site, with the

I use Ghost as my blogging engine, and recently they've unveiled a public API that allows theme users to query Ghost's data to retrieve posts, tags, authors and such. I've long wanted a feature on my blog to highlight some of my previous work, and with the reveal of the API, I finally had the ability to build one. Here's hoping someone else out there finds this useful too.

NOTE: There's a BIG caveat to the following post: Ghost's public API is still in beta, and (at the time of writing) they explicitly warn against using this feature in production. I, of course, am ignoring them.

The Finished Code

I like to show results first. Here's the complete Javascript code that queries the Ghost API:

<script>  
    $(document).ready(function () {
        $.get(
            ghost.url.api('posts', {limit: 'all'})
        ).done(onSuccess);
    });

    function onSuccess(data) {
        var $result = $('#featured-posts-list');
        var sortedPosts = shuffleArray(data.posts);
        var displayPosts = sortedPosts.slice(0,5);
        $.each(displayPosts, function (i, post) {
            $result.append(
                '<li><i class="fa fa-star"></i><a href="http://www.exceptionnotfound.net' + post.url + '">' + post.title + '</a></li>'
            );
        });
    }

    function shuffleArray(array) {
        for (var i = array.length - 1; i > 0; i--) {
            var j = Math.floor(Math.random() * (i + 1));
            var temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
        return array;
    }
</script>  

Now, let's see how we got to this point.

Prerequisites

Before we can even use the Public API, we must enable it in Ghost by going to the Labs page of the admin site and clicking the appropriate checkbox:

A screenshot of Ghost's Admin site for my blog, with the

We now need to set the requirements for using the ghost.url.api(). The docs for this feature state the following:

There are 3 pre-requisites that are required in order to use this helper:

  1. {{ghost_head}} helper must be present in your theme.
  2. Public API access enabled in Admin-UI.
  3. You must not have disabled the ghost-frontend client (only accessible via the DB)

We've already accomplished #2, so ensure that #1 and #3 are true for your theme and you should be good to go.

Step 1: Getting All Posts from Ghost

The first and most critical step of this entire process is to get the posts from the Ghost API. We do this in the following function:

$(document).ready(function () {
    $.get(
        ghost.url.api('posts', {limit: 'all'})
    ).done(onSuccess);
});

The ghost.url.api() function takes two parameters. The first parameter states what kind of data we are querying for. In our case, we want 'posts' but this could be 'tags' or 'authors'. The second parameter is a collection of options for the query. Some of these options include:

  • limit: Specifies maximum number of items to return. Default is 15, can specify 'all' to return all items found.
  • order: Specifies a column by which to order the results. For example, you could say order: 'published_at' to return the posts in published order.
  • filter: Allows you to specify how to filter results. For example, you could get all featured posts, or get all posts in a specific tag. See the docs for more info.

For this operation, we only need the "limit" option, and we set it to 'all'.

Step 2: Randomizing the Posts

Now that we've got all the posts from the Ghost API, we need to select a random subset of them. For this demo, we want to display 5 random posts.

First off, we need a method by which we can randomize the order of the posts in the array. The best way to do this is via a shuffling algorithm, and the most common of these is the Fisher-Yates shuffle. The resulting function looks like this (modified from this StackOverflow answer):

function shuffleArray(array) {  
    for (var i = array.length - 1; i > 0; i--) {
        var j = Math.floor(Math.random() * (i + 1));
        var temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    return array;
}

Step 3: Displaying the 5 Random Posts

After randomizing the array of posts, I now need to select the first 5 and display them using JavaScript's slice() method and jQuery's each() and append() methods. That is what the following function does:

function onSuccess(data) {  
    var $result = $('#featured-posts-list');
    var sortedPosts = shuffleArray(data.posts);
    var displayPosts = sortedPosts.slice(0,5);
    $.each(displayPosts, function (i, post) {
        $result.append(
            '<li><i class="fa fa-star"></i><a href="http://www.exceptionnotfound.net' + post.url + '">' + post.title + '</a></li>'
        );
    });
}

This function shuffles the posts, takes the first 5 from the shuffled set, then appends them to a waiting <ul id='featured-posts-list'> element.

Summary

That's it! We now have a system by which we can display 5 random posts in our Ghost theme! If you want to see a demo, well, you're already looking at one (unless I've changed my layout, in which case, see the screenshot above).

One possible downside to this method is that I don't know how performant it will be given a large set of posts. The objects returned by the ghost.url.api() method can get fairly large, so performance could be an issue on some blogs.

Anyway, if this helped you out, feel free to share in the comments.

Happy Coding!

Using Gulp.js and the Task Runner Explorer in Visual Studio 2015

We're setting up a new ASP.NET 5 ASP.NET Core 1.0 project in Visual Studio 2015, and my team is trying to get used to the idea of "Task Runners" such as Grunt.js or Gulp.js. We're brand new to this idea, and my personal philosophy is What I don't understand, I cannot change, so obviously I need to understand these pieces of tech before I can hope to use them properly. Let's see if we can understand what a Task Runner is, what Gulp.js can do for us, and how using these makes building our applications a little bit easier.

What is a Task Runner?

In Visual Studio, a Task Runner contains collection of "tasks" that can be executed either on demand, or automated as part of a build process. Visual Studio includes native support for Grunt.js and Gulp.js task runners, and each of these is designed to make building client-side resources easier. For this project, we're using Gulp.js as our task runner, so the rest of this demo will use Gulp.js. But what is Gulp.js?

What is Gulp.js?

Gulp.js bills itself as the "streaming build system". Gulp.js provides a set of automation tools that allow us developers to automate common processes, such as bundling and minification of CSS and JS files. In short, Gulp.js makes these processes part of the build for your application.

(NOTE: because Gulp.js performs bundling and minification, I'd be willing to bet that the default MVC bundling and minification will be disappearing at some point in the future)

The Default Gulpfile.js

In ASP.NET Core projects, Gulp.js and Visual Studio use a file called gulpfile.js to define and bind tasks (we'll get into what "binding" a task means just a bit further down). Now let's look at this file, created by Visual Studio 2015 in an ASP.NET Core 1.0 Release Candidate 1 project:

/// <binding Clean='clean' />
"use strict";

var gulp = require("gulp"),  
    rimraf = require("rimraf"),
    concat = require("gulp-concat"),
    cssmin = require("gulp-cssmin"),
    uglify = require("gulp-uglify");

var paths = {  
    webroot: "./wwwroot/"
};

paths.js = paths.webroot + "js/**/*.js";  
paths.minJs = paths.webroot + "js/**/*.min.js";  
paths.css = paths.webroot + "css/**/*.css";  
paths.minCss = paths.webroot + "css/**/*.min.css";  
paths.concatJsDest = paths.webroot + "js/site.min.js";  
paths.concatCssDest = paths.webroot + "css/site.min.css";

gulp.task("clean:js", function (cb) {  
    rimraf(paths.concatJsDest, cb);
});

gulp.task("clean:css", function (cb) {  
    rimraf(paths.concatCssDest, cb);
});

gulp.task("clean", ["clean:js", "clean:css"]);

gulp.task("min:js", function () {  
    return gulp.src([paths.js, "!" + paths.minJs], { base: "." })
        .pipe(concat(paths.concatJsDest))
        .pipe(uglify())
        .pipe(gulp.dest("."));
});

gulp.task("min:css", function () {  
    return gulp.src([paths.css, "!" + paths.minCss])
        .pipe(concat(paths.concatCssDest))
        .pipe(cssmin())
        .pipe(gulp.dest("."));
});

gulp.task("min", ["min:js", "min:css"]);

This file allows us developers to use Gulp.js through the new Task Runner Explorer window, which looks something like this:

But, what does this file actually do? Let's break it down.

Visual Studio Bindings and Strict Mode

The first couple of lines of code in gulpfile.js are these:

/// <binding Clean='clean' />
"use strict";

The XML you see is Visual Studio's binding. Clean='clean' states that the 'clean' task should be run when Visual Studio executes a Clean.

The second line forces strict mode. Douglas Crockford explains:

"Strict Mode is an opt-in mode that repairs or removes some of the language’s most problematic features."

This is one of those features that, for now, I'm too wary of to mess with. Needless to say, read Crockford's post if you want to understand more of what strict mode is supposed to do.

Node Packages Required

The next block of code looks like this:

var gulp = require("gulp"),  
    rimraf = require("rimraf"),
    concat = require("gulp-concat"),
    cssmin = require("gulp-cssmin"),
    uglify = require("gulp-uglify");

This imports packages from Node Package Manager (NPM), similarly to how NuGet imports packages for server-side development. Currently we're importing five packages.

  • gulp: The gulp package itself.
  • rimraf: A deep deletion module which allows for deletion of files and folders.
  • gulp-concat: Concatenates files (AKA bundling).
  • gulp-cssmin: Minifies CSS files.
  • gulp-uglify: Minifies JS files.

We'll be using each of these packages in this default file.

Path Setup

The next block of code is relatively easy to understand:

paths.js = paths.webroot + "js/**/*.js";  
paths.minJs = paths.webroot + "js/**/*.min.js";  
paths.css = paths.webroot + "css/**/*.css";  
paths.minCss = paths.webroot + "css/**/*.min.css";  
paths.concatJsDest = paths.webroot + "js/site.min.js";  
paths.concatCssDest = paths.webroot + "css/site.min.css";  

All this is doing is setting up some paths we'll be using later. Note the "**" wildcard. The "**" wildcard specifies that any folder at that location is to be included in the path. This allows you to have as many subfolders under "js" or "css" folders as you want, and any JS or CSS files in the subfolders can still be targeted and processed by Gulp.js.

Clean Tasks

Now we start to see the implementations of the tasks themselves (after all, the tasks are what this thing can actually do). Here's the implementation for the Clean tasks.

gulp.task("clean:js", function (cb) {  
    rimraf(paths.concatJsDest, cb);
});

gulp.task("clean:css", function (cb) {  
    rimraf(paths.concatCssDest, cb);
});

gulp.task("clean", ["clean:js", "clean:css"]);  

Note that the first two tasks, "clean:js" and "clean:css", call the rimraf() function to delete certain files. The "clean" task removes the minified files if they exist. Further, showing good coding standards, the "clean" task simply calls the "clean:js" and "clean:css" tasks, and doesn't do any work itself.

Min Tasks

Now we get to a more complicated task. Here's the first "min" task:

gulp.task("min:js", function () {  
    return gulp.src([paths.js, "!" + paths.minJs], { base: "." })
        .pipe(concat(paths.concatJsDest))
        .pipe(uglify())
        .pipe(gulp.dest("."));
});

We're defining a task called "min:js" and specifying that it do three things:

  1. Concatenate (or bundle) all the files at the paths in the paths.js set, but that are not in the paths.minJs set into one file.
  2. Minify the resulting file.
  3. Place the resulting minified file at root location.

A similar set of things happens in the "min:css" task:

gulp.task("min:css", function () {  
    return gulp.src([paths.css, "!" + paths.minCss])
        .pipe(concat(paths.concatCssDest))
        .pipe(cssmin())
        .pipe(gulp.dest("."));
});

The only difference is that we're using cssmin() to minify CSS files rather than uglify().

And, of course, the final line of code combines both of these tasks into one call:

gulp.task("min", ["min:js", "min:css"]);  

In short, the "min" tasks bundle and minify your CSS and JS files.

Running Tasks

Now we can investigate how to run these tasks using Visual Studio. Take a look at the structure of a brand-new ASP.NET Core 1.0 RC1 Project in Visual Studio 2015:

Note the site.css and site.js files. What happens after we run the "min" task? You can run a task by right-clicking it and selecting "Run".

Now, we'll see that the site.css and site.js files each have a minified version.

It follows that running the default "clean" would remove those minified files.

Binding Tasks to Events

I mentioned at the beginning of this post that you can bind these tasks to run during certain Visual Studio events. Any task can run during any event, and each task can run during multiple events. The events we can bind Gulp.js tasks to are:

  • Before Build
  • After Build
  • Clean
  • Project Open

We can bind tasks to events by using the interface (right-click the task you want to bind):

You can also do it directly by modifying the binding code in the gulpfile.js:

/// <binding BeforeBuild='min, clean' Clean='clean' />

Summary

The integration of Gulp.js into Visual Studio 2015 makes managing JS, CSS, and HTML files much simpler. We can create tasks for common operations like bundling and minification, and those tasks can now automatically be run as part of events like Clean or After Build. All-in-all, Task Runners like Gulp.js bring Visual Studio and ASP.NET Core just another step closer to being the best IDE and project stack out there. Of course, I might be just a little biased.

Did this help, or did I miss something about Gulp.js or Task Runners that you found useful? Need to yell at me because you're frustrated and no one else is around? Sound off in the comments!

Happy Coding!

NOTE (1/25/2016): This post has been updated to reflect the naming change from ASP.NET 5 to ASP.NET Core 1.0

Wait, Pick, Learn, Ignore: Dealing with JavaScript Framework Fatigue

I'm mostly a server-side developer, but occasionally I delve into the wild world of client-side programming. Each time I do, I get bombarded with blog posts and tweets and advocates, each hyping their own preferred JavaScript framework.

Use Angular, they said. No, do Knockout, others proclaimed. Pssh, React is the way to go, a third group shouts. Ember.js is the one true way, or so says their adherents.

It seems like every day a new JavaScript framework appears, and every day that framework's proponents commence trying to convince everyone around them that their chosen framework has all the solutions they would ever need. Then, inevitably, a new system comes along and supercedes the old one, which disappears from the public eye, and the new one enjoys its short time in the limelight before eventually fading away. Seeing as how obsolescence is the ultimate fate of all software, you think we as programmers would be used to this rapid change, but the incredible proliferation of Javascript frameworks, to me, causes more worry and uncertainty to new learners like myself.

An image of a sticker, which reads

In fact, as a primarily server-side programmer, this whole debacle is rather discouraging. I just want to improve my JavaScript skills, dammit, not drown in a flood of frameworks. But I cannot seem to get started because I can't decide which framework will really be able to help me get better. All these competing voices, these disagreeing "experts", are really just hindering me, a JavaScript noob, from choosing to learn and code in the client-side world. It's almost like I'd rather not pick than choose incorrectly.

This is a classic problem: there's too many frameworks. The arguments put forth in the Paradox of Choice state that having so many choices is actually harmful to consumers; the more choices that exist, the more likely it is that you chose the "wrong" one. Since nobody wants to spend time learning the wrong thing, the amount of risk you carry when picking from many choices is rather large, and so many people simply won't decide at all for fear of being wrong.

Wait, Pick, Learn, Ignore

This is the situation I find myself in now: I want to do more JavaScript, and specifically I want to learn a framework, but which one should I pick? I arrived at that conclusion by following these steps, which I like to summarize as Wait, Pick, Learn, Ignore:

  • Wait: Give it a couple years. Most likely, any frameworks that still exist a few years after their debut will be worthwhile in that they will be able to solve a wide variety of problems.
  • Pick: Just pick one, even if it's a random selection.
  • Learn: Implement and understand that chosen framework thoroughly. I cannot stress this enough. If you're going to spend all that time and effort just selecting a framework that you will study, you'd better put effort into actually learning it. Understand its strengths and weaknesses, the kinds of problems it can solve and the kinds of problems it's not very good at solving, and when you are done, at the very least you will have another tool in your coding toolbox.
  • Ignore: all other frameworks until you're done with the first one. Don't confuse yourself by trying to learn everything at once. Stick with the framework you chose, learn it thoroughly, then move on to others. Having learned the first one inside and out, you'll most likely find that learning subsequent frameworks will take less time, since many of the ideas are transportable from one framework to another.

There are too many frameworks, but we shouldn't let that discourage us from learning Javascript. If we're stuck on choosing which framework to use, just wait a few years, pick one, learn that one, and ignore all others until you're done with the first one. In this way, we can learn a JavaScript framework without the baggage of wondering if we chose the "correct" one. Since, after all, there is no "correct" one, there's just a bunch of frameworks.

At the very least, this method will let you ignore the vocal adherents, and you may even find that doing so quells your mind and let's focus on you ultimate goal: making yourself a better developer.

(By the way, at least for now, I'm going with Angular, as it seems to have the biggest support and widest usage. Only time will tell if I made the right choice.)

Happy Coding!