Gulp + Environment Variables Setup

I’m deciding to make a blog post because, like most things node, there’s just a plethora of options out there and I wanted something minimal and clean. Clean such that I’m taking an asynchronous task runner and making it synchronous by the only means necessary:

gulp.task('two', ['one'], function() {
  // task 'one' is done now
});

Here’s the basic formula
* One separate config file that has attributes specific to each environment (dev, stage, prod).
* Specify the target environment in my gulp command (ie, gulp –env dev).
* Simple string replace mechanism. I’ve noticed the convention of using ‘@@varName‘ which I will use here.
* Keep the variables in my ES6 js files and compile to my build directory.

Config file: environment-config.json

{
    "apiUrl" : {
      "dev": "https://dev-api-url",
      "stage": "https://stage-api-url",
      "prod": "https://prod-api-url"
    }
}

In gulpfile.js

var replace = require('gulp-replace'); // Simple string or regex replacements
var argv = require('yargs').argv; // The contemporary library of choice for parsing command arguments (in this case flags)
var fs = require('fs'); // Read a file

gulp.task('replace', ['babel'], function() {
  var settings = JSON.parse(fs.readFileSync('environment-config.json', 'utf8'));
  var env = argv.env;
  var targetApiUrl = settings.apiUrl[env] ? settings.apiUrl[env] : settings.apiUrl['prod'];
  if (targetApiUrl) {
    gulp.src(['./www/js/app.js'])
      .pipe(replace('@@apiUrl', targetApiUrl))
      .pipe(gulp.dest(function(file) {
        return file.base;
      }))
  }
});

In this case, ‘babel’ is copying over my ES6 to my build directory along with the environment variables, when that is done, my ‘replace’ task is parsing my environment-config.json and reading the environment variables. I’m then targeting my build files (in this case, I’m only targeting app.js which is where most configuration should go for an Angular project), running my replace, and then performing a workaround that allows me to build to the same destination as my source file. Most of the tutorials I’ve seen has neglected to account for modularization of gulp tasks and assume that the source will compile to the destination and you’re done. By compiling to the same file, you’re not interfering with the other asynchronous gulp tasks that may alter the same file. Other tutorials have also asked to create a file for each environment which I think is kind of silly and overkill.

Yeoman Reveal.js Generator: Quick Reveal Presentations

A while back I gave a Reveal.js presentation on how to create a Reveal.js presentation using the scaffolding tool Yeoman. It’s been pretty helpful for building quick presentations and distributing them through my github account for anyone to see and I keep going back to that presentation when I start my next one, so I thought it should be in a blog post.

If you want to view the actual presentation, give it a whirl.

Let’s start with the basics. Node.js comes with npm so let’s make sure we have the most up-to-date node installed on our system.

node

sudo npm cache clean -f
sudo npm install -g n
sudo n stable
npm -v

Update npm

sudo npm install npm -g

yeoman

Update yeoman

npm install -g yo

Update the Reveal.js Generator

npm install -g generator-reveal

yo yeveal | Build a new presentation

Navigate to a new directory

yo reveal

Follow the instructions. Be sure to include your github account name in order to deploy your presentation to a github page.

yo reveal:slide "Slide title" --markdown

Boom, this builds you a new slide in your slides directory.

No just run grunt serve to see your presentation (with live reload) in your browser:

grunt serve
  • Grunt is a task runner
  • You can build your own tasks for things like compiling CSS.
  • Or you can rely on others’ grunt files. For Reveal’s generator, this already includes:
    • Node server
    • Autoreload
    • Lint
    • Deploy (to github in this case)

Super handy.

Bonus: Scaling Images on your slides

I ran into this issue a few times when I would insert an image and it just wouldn’t scale to the slide right. I think this is very important if you’re giving a talk. To fix, just use some quick CSS magic to help you.

<div class="big-image-container">
    <img class="big-image" src="myimage.jpg"/>
</div>

For the container class:

.reveal div .big-image-container {
    max-height:80%;
    max-width:70%;
    margin:0 auto;
}

And the image class:

.reveal img.big-image {
    height:auto;
    margin:0 auto;
}

Twitter REST API with geocode lookup

Recently I was given the task of building a search feature that will find all geolocations of Tweets with a keyword and/or hashtag.

The Twitter REST API has the following limitations:

  • User must enable Location for their Tweet — This removes a lot of the sample size
  • A Tweet REST API query can only target specific geo coordinates, along with a radius
  • The Search API’s recent index only spans for the last 6-9 days

Using my Drupal 8 Twitter module as a template, I was easily able to make this work for finding locations within the intercontinental United States.

$params = array(
 "q" => $this->keyword,
 "count" => $this->num_results,
 "result_type" => "mixed",
 "lang" => "en",
 "geocode" => "39.8,-95.583068847656,2500km",
 );
 $tweets = $twitter->get("search/tweets", $params);

Basically, I’m polling for all tweets with a radius of our country starting in roughly the middle. Thanks to ThoughtFacet for the tip.

So this is a nice workaround if you are looking for Tweets that have geolocations.

Log in to WordPress without admin credentials!

I found this answer here (http://wordpress.stackexchange.com/a/166375/10099) and thought I’d reiterate since it may come in handy if you are developing and don’t want to deal with managing login information.

function my_autologin() {
    if (!is_user_logged_in()) {
        $uid = 'autologinuser';
        $user = get_userdatabylogin( $uid );
        wp_set_auth_cookie($user->ID);
        wp_set_current_user($user->ID);
        do_action('wp_login', $user_login);  // optional
    }
}
add_action('init','my_autologin');

Enjoy.

Managing dependencies with Composer Manager

Prerequisites:

  • Drupal 8
  • Composer

Use Case:

  • You want to add dependencies to your custom module using composer update at the root of your site directory.
  • Updating Drupal core removes any custom dependencies added to the root composer.json, potentially breaking any modules that require them.

Quick run-down:

First, download composer manager to your site’s modules directory and run the init.php command to register the ‘drupal-update’ command. This command is what will look through your module’s composer.json files and update the main composer.json file at your site’s docroot.

cd mysiteroot
drush dl composer_manager
php modules/composer_manager/scripts/init.php
composer drupal-update
In your sites directory, add a composer.json file and make sure you have both the name and requirements:
{
  "name": "drupal/mymodule",
  "require": {
    "mailchimp/mailchimp": "2.0.6"
  }
}

Now, run composer drupal-update again and you should see your dependencies in the vendor directory.

See also:
https://bojanz.wordpress.com/2015/09/18/d8-composer-definitive-intro/
https://www.drupal.org/node/2405811

Drupal 8 Console: Building a Twitter feed module

I took this tutorial, Tutorial for Converting a Module from Drupal 7 to Drupal 8, and worked my way through it but used Drupal Console for many of the scaffolding, which makes things a lot easier. So thanks to Unleashed Technologies for the bulk of the work.

In your Drupal 8 root, generate a custom module:

drupal generate:module

This will prompt you for details about the module and build an info and module file.

Our first step will be to create a settings page that will have a form for our Twitter API Oauth credentials. You’ll need to create an application in your twitter developer console. Create a new app. If you want to use this for local testing, use ‘http://127.0.0.1‘ for the URL.

Now, we’ll create a new form:

drupal generate:form:config

This will build out your form class, your input fields, and update your router file. Your settings page is built, however, we also need to build a menu link for it to live, and we do this in a YAML file!

Add {yourmodule}.links.menu.yml to the root of your module directory.

twitter_pull.settings:
  title: 'Twitter settings'
  description: 'Twitter settings for your site'
  parent: system.admin_config_services
  route_name: twitter_pull.twitter_settings_form
  weight: 100

Now if you navigate to /admin/config/, you’ll see your new Twitter Settings link under the Web Services menu.

From here, you have options: Where do you want your Twitter feed to display. For this example, I’m building a block.

drupal generate:plugin:block --module=twitter_pull

Add the block class name, and any input fields you may wish to include and generate the plugin.

Now you have the luxury of OOP at your disclosure to pull in your Twitter feed and spit it out to a Twig template for rendering.

Loading your configuration is as easy as:

$config = \Drupal::config('twitter_pull.twittersettings_config');

Just pull in your form id into Drupal’s config method and you have access to your settings. At the top of our block file, we’ll autoload the Twitter OAuth PHP library, but first we have to install that library.

One option here is to add our own composer.json file to our module, declare a dependency for a Twitter OAuth library, and then use Composer Manager to compile our dependencies to our composer.json at root. I’ll save this for another post, but for now, I’ll simply include the Twitter OAuth library at our root composer.json manually.

"require": {
  "composer/installers": "^1.0.21",
  "wikimedia/composer-merge-plugin": "^1.3.0",
  "abraham/twitteroauth": "^0.6.1"
},

Be sure to run composer update at your docroot after making this edit. Also note that if you update Drupal, it will override this file and your dependency declaration will be lost, hence the need for Composer Manager. Then we can autoload the library in our block file.

You can download my example here. OOP is your oyster.

https://github.com/alxvallejo/drupal8-twitter-feed

Htaccess: Backreferences and multiple conditions and rules

Two rules I want to highlight today:

  1. Only the variables from the last-matched RewriteCond are available as back-references. 1)https://www.webmasterworld.com/forum92/5888.htm
  2. Conditions only apply to the very next rule. You’ll need to repeat the condition in order for it to apply to another rule. 2)http://serverfault.com/questions/264076/rewrite-conditions-backreferences-passed-to-all-rules

This came in handy when I was testing out the following rules for a Drupal htaccess. Basically, I wanted all the admin pages to be routed to the .com English version of the site. If the editor tried logging into example.co.uk/uk/user, they would route to example.com/user. In the first example, I don’t have access to the HTTP_HOST backreference. It doesn’t matter in this case because i’m defining it in the rule. In the second and third rules, I’m repeating the condition so I can access the same back reference for two different rules.


    #keep admin pages on .com instance (uk only for now) (PROD)
    RewriteCond %{HTTP_HOST} ^(.*\.)?example.com$ [NC]
    RewriteCond %{REQUEST_URI} ^/(uk)/(user|admin)/?(.*)? [NC]
    RewriteRule ^(.*)$ https://www.example.com/%2/%3 [R=301,L,NC]

    #keep admin pages on .com instance (uk only for now) (DEV / STAGE)
    RewriteCond %{HTTP_HOST} ^(dev|stg).example.(co.uk|de|fr|nl)$ [NC]
    RewriteRule ^uk/user/?(.*)? https://%1example.com/user/$1 [R=301,L,NC]
    RewriteCond %{HTTP_HOST} ^(dev|stg).example.(co.uk|de|fr|nl)$ [NC]
    RewriteRule ^uk/admin/?(.*)? https://%1example.com/admin/$1 [R=301,L,NC]

References   [ + ]

A quick glance at Backbone.js: Todo MVC

I had been on and off with Backbone for some time but had finally come around to getting somewhat of an understanding of the basics of how Backbone is organized and how it can help with your JavaScript-robust applications. I’ve read numerous tutorials and hadn’t quite gotten the picture. They were either outdated, too bloated, or not enough. I’ll attempt at helping to clarify the basics in this tutorial and perhaps write more advanced topics later.

Some immediate things-to-consider from my approach sofar:

  • I’m not using require.js, which I know a lot of front-enders depend on
  • I’m relying on Yeoman’s Backbone Generator which saves an enormous amount of leg work for getting the structure of your application ready. If you haven’t tried Yeoman yet, give it a go. It’s super great for getting projects off the ground.
  • Basically, what got me to this point was a combination of reading Addy Osmani’s Backbone tutorials and this tutorial which uses Yeoman to speed up the process.

Let’s dive in. First, we’ll look at our main.js file.

The init method immediately calls upon a view from our Todos colllection. In views/todos-view.js, our view defines most of the logic for interacting with our app, but we will frequently call on various methods defined in the Collection that it renders.

(function(){
    
    'use strict';
    
    window.backboneApp = {
        Models: {},
        Collections: {},
        Views: {},
        Routers: {},
        init: function () {
            console.log('Hello from Backbone!');
            new this.Views.TodosView({
                collection: new this.Collections.TodosCollection()
            });
        }
    };

    $(document).ready(function () {
        backboneApp.init();
    });
})();

For example, after completing the bare necessities of a Todo app, I wanted to add a Clean button that will clear all checked off items on the Todo Collection.

We’ll start by modifying our todos.ejs template and adding a ‘Clean’ button.


<form class="input-append">
<input id="new-todo" type="text" placeholder="What do you need to do today?" /> 
<input class="btn" type="submit" value="Submit" />
</form>

<form class="filter"><input id="clear-completed" class="btn" type="button" value="Clean" /></form>
<ul><!-- Where our To Do items will go --></ul>

We’ll now jump to our parent view, todos-view.js and add an event listener for our Clean button:


events: {
    'click #clear-completed': 'clearCompleted'
},

// Clear all completed todo items, destroying their models
clearCompleted: function() {
    _.invoke(this.collection.completed(), 'destroy');
    return false;
}

The clearCompleted method uses Underscore’s invoke method to get a list of the completed items and then use Backbone’s native destroy method to not only remove them from the view, but delete their models as well. The list of completed items is obtained from the collection’s completed method:


backboneApp.Collections.TodosCollection = Backbone.Collection.extend({
    
    completed: function() {
      return this.filter(function(todo){
        return todo.get('completed');
      });
    }

});

We’re using Backbone’s other native method, get, to obtain a list of models that have the completed attribute. Opening our todo-model.js file, we see that our model defaults to ‘false’. The ‘toggle’ method toggles this attribute. Backbone keeps track of these ‘states’ for each model.


(function(){
    
    'use strict';

    backboneApp.Models.TodoModel = Backbone.Model.extend({

        defaults: {
            title: '',
            completed: false
        },
        
        toggle: function() {
            this.save({
                completed: !this.get('completed')
            });
        }
    });

})();

A quick staging environment setup with LAMP stack, virtual hosts, and installation tips on Ubuntu 14

I’ve joined the bandwagon on Digital Ocean’s cheap SSD VPS hosting options ($5/mo say what!) and have been very delighted to jump into a fresh Ubuntu shell with no issues. If you’re like me and haven’t been installing servers for years then you might find some of these tips helpful. I may add more tips later on so let me know if I can help with some general tips (not too fancy!?) for the future.

For starters, let’s start with everyone’s favorite: users and permissions. I always find it somewhat annoying when I add a user or settle on root and I have to continually type ‘sudo’ for simple things like writing to a file or adding a directory.

adduser myuser

This will create a /home/myuser directory. Now to grant all the sudo privileges to that user, it’s as easy as

usermod -aG sudo myuser

The -a makes sure that the sudo group is added and does not replace other groups assigned to the user.

Password Protect A Public Directory

Recently, I needed to create a staging site for a client but I wanted to keep the directory password protected and prevent it from being crawled by search engines.

You can password protect a public directory using the htpasswd command and .htaccess file.

You need to make sure a few things are in place.

First, make sure that your .htaccess is read by Apache by navigating to your site’s httpd.conf file (now referred to as site.conf in recent versions of Linux).

Navigate to /etc/apache2/sites-available.

cp 000-default.conf seojeek.conf // copy default settings
vi seojeek.conf
ServerName seojeek.com
ServerAlias www.seojeek.com
 
ServerAdmin me@seojeek.com
DocumentRoot /var/www/seojeek

<Directory "/var/www/seojeek">
Options Indexes FollowSymLinks MultiViews
AllowOverride ALL
Order allow,deny
allow from all
</Directory>

The AllowOverride ALL was added so that Apache would read and obey nested .htaccess files. Once I added .htaccess to the staging directory, I wanted to ensure that this directory was not visible to search engines (robots.txt) and password protected (.htpasswd file).

Robots.txt

We’ll start with the preventative measure of making sure search engines aren’t crawling my staging site.

At the site root, touch a robots.txt file and add the following:

disallow: /client_site_directory/*

Somewhere on the server, we’ll create a .htpasswd file that will store all of our username and encrypted passwords. We generate this with the htpasswd command. You may need to install apache utils first.

htpasswd .htpasswd username

Now, to password protect the directory, you’ll modify .htaccess:

AuthType Basic
AuthName "restricted area"
AuthUserFile /var/www/seojeek/client_site_directory/.htpasswd
require valid-user

Be sure to restart apache.

apachectl restart

Now, when you navigate to yoursite.com/client_site_directory, you’ll receive a login prompt!

Loading WordPress in a Static HTML page for performance comparison

Recently, I was trying to figure out what’s the best way to load a Recent Posts feed onto a static HTML site and I came across a couple of resources that suggested loading wp-blog-header.php onto the static page and using the WordPress API to query your latest posts.

require('wp-blog-header.php')

I was expecting a larger page load, but only saw a 1kb increase in page size and 150ms increase in load time (average). So I proceeded to build a query for the Recent Posts query, which I found on WorldOWeb.

<?php 
 $args = array( 'numberposts' => 6, 'post_status'=>"publish",'post_type'=>"post",'orderby'=>"post_date");
 $postslist = get_posts( $args );

 foreach ($postslist as $post) : setup_postdata($post); ?>
 <div class="events">
 <p><strong><?php the_date(); ?></strong></p>
 <p><a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a></p> 
 </div>
<?php endforeach; ?>

I also tried using PHP’s DOMDocument class. So, on average, including WordPress in your static HTML will only increase the load time by ~150ms.

Is that a lot? Share your thoughts.