Entries Tagged as 'Miscellaneous'

ncline-screenshots: New ncline Module For Managing Screenshot Files

Miscellaneous , Node.js No Comments »

When I'm observing a multi-step process during a debugging exercise, I often take screenshots at each breakpoint.  It gives me a record of the data values and behavior at particular points in the process, sometimes recording something I didn't realize I needed to pay attention to until after the fact.

On Windows computers, I use the freeware application Greenshot to take my screenshots.  One of the features of Greenshot is that it lets you use date and time parameters in the filename settngs, so I have it set to save each screenshot with a name and folder location based on date and time.  If I were to take a screenshot on June 16, 2016 at 3:30pm, I'd end up with a file named "screenshot-15-30-00.png" in a "2016-06-16" subdirectory of my root screenshot directory.

I recently started using a Mac laptop as my primary personal laptop, and I was disappointed to learn that there is no Greenshot version for Macintosh.  OS X's native screenshot implementation is less than ideal:  it saves the screenshot file on the desktop and provides no control over the naming of the screenshot (though it does include the timestamp in the name).  I looked at what Mac-compatible screenshot programs were out there and found many of them had a workflow geared towards manipulating the screenshot right after taking it.  I just wanted to take the screenshot, move on to the next one, and review all of them later.

So I decided to solve my Mac screenshot problem myself.  First, I followed the instructions for changing the location where OS X saves the screenshots (it's not a setting you can change in the OS X UI anywhere) so the files would be created somewhere other than the desktop.  Then I wrote ncline-screenshots, a new, separately-downloaded command module for my Node-powered ncline project.  It uses the chokidar npm module, an enhancement on the native Node file watcher library, to watch the directory where the screenshot files are created.  When a new file is found in that directory, it gets processed by a series of processing rules that rename and/or move the file.

The command module creates the watcher task, provides private functions that implement the processing rules, and includes a few commands for changing configuration settings and rule processing behavior.  Creating additional rules is simply a matter of writing additional functions that manipulate the file and pass on the results of the change to the next rule in the sequence.

Even though it was written to solve a Mac-specific gap in my workflow, ncline-screenshots works on Windows systems as well.

Task-based web browsing: Grunt and the grunt-open plugin

JavaScript , Miscellaneous No Comments »

Lately I've been playing around with Grunt, which is a JavaScript-based task runner similar to Ant and Gradle. Generally, these tools are used to automate software builds, but they can be utilized in other ways.

While browsing through the large collection of Grunt plugins, I came across one called grunt-open. The grunt-open plugin lets you create a task that opens a URL in your web browser. It looks like it was created with the idea of opening a web page at the end of a chain of Grunt tasks to check the result of a software build. But I thought it might be useful in automating everyday web browsing tasks.

I like to start off my work day with a bit of Internet humor and I have certains sites I visit for that. Dilbert has a new strip every day, while JoyOfTech and XKCD get published every Monday, Wednesday, and Friday, and the XKCD "What-If" article is published once a week, usually by Thursday. Rather than manually opening the appropriate bookmarks for the appropriate day of the week, I figured I'd program a task to take care of opening the appropriate sites.

I won't reinvent the wheel here trying to explain the basics of Grunt: the Grunt "Getting Started" guide does a good job of explaining how to install Grunt and generate the files needed (the package.json and the Gruntfile.js files). Here's what my Gruntfile looks like:

 
module.exports = function( grunt )
{
    grunt.initConfig({
        open: {
            joyoftech: {
                path: 'http://www.geekculture.com/joyoftech/',
                app: 'Chrome'
            },
            dilbert: {
                path: 'http://www.dilbert.com/',
                app: 'Chrome'
            },
            xkcd: {
                path: 'http://www.xkcd.com/',
                app: 'Chrome'
            },
            xkcdwhatif: {
                path: 'http://what-if.xkcd.com/',
                app: 'Chrome'
           }
        }
    });

    grunt.loadNpmTasks( 'grunt-open' );

    grunt.registerTask( 'webhumor', 'Load the funnies!', function() {
        var today = new Date();
        var dayOfWeek = today.getDay();

        switch( dayOfWeek ) {
            case 1: //Monday
            case 3:
            case 5:
                grunt.task.run( [ 'open:joyoftech', 'open:dilbert', 'open:xkcd' ] );
                break;
            case 0:
            case 6:
            case 2:
                grunt.task.run( [ 'open:dilbert' ] );
                break;
            case 4:
                grunt.task.run( [ 'open:dilbert', 'open:xkcdwhatif' ] );
                break;
        }
    })
};

So in the directory where this Gruntfile.js lives, I just have to type "grunt webhumor' in the command, and Grunt will open the appropriate sites. Not a huge timesaver, but kinda cool.

On Today's Experience Installing Ionic for Android Development (on Windows 8.1)

Miscellaneous No Comments »

This is one of those posts that's likely to become obsolete very quickly as the install process for Ionic changes, but if it helps someone else resolve the (minor) install issues I ran into today, then it's worth doing.

The installation instructions that start at http://ionicframework.com/docs/guide/installation.html are pretty good considering the number of technologies involved in getting up and running (npm, the Java JDK, the Android SDK, Cordova, etc.).  But as those technologies changes so does the install process, even if the change is slight.

The first issue I ran into occurred when I tried to run the "ionic start" command from the command line.  I got a two-part error message in the console.  The latter message, cast in red text, suggested that my version of Cordova was outdated:  not likely since I'd installed it 30 minutes ago.  The earlier message said "could not create work tree dir" and cited a nested folder under my user account folder called "plugman".  Fortunately, someone had posted the solution (the need to create the missing directory) in the Ionic forums:  http://forum.ionicframework.com/t/unable-to-add-plugins-perhaps-your-version-of-cordova-is-too-old/3807/10.

The next issue came up at the conclusion of the install process when I tried to run "ionic emulate android".  Again, the final error message wasn't that helpful; the earlier message stated that "abd" was not recognized as a command.  The reason that happened was because the instructions say to declare the ANDROID_HOME Path variable to point to the "tools" directory of the Android ADT bundle, but Google moved the adb executable from the "tools" directory to the "platform-tools" directory (they left a note in the tools directory about the move).  For Ionic to work you actually need pointers to both the "tools" and "platform-tools" directories in the path.

The last issue was particular to my machine.  I had a pre-existing Android Virtual Device (AVD) configuration from an earlier install of the Android Eclipse plugin, and when Ionic tried to use that AVD it complained that a kernel file was missing.  I ended up launching the AVD manager from the command line ("android avd") and using the "Repair" option to repair the AVD, and that apparently fixed the problem, and the emulator opened at its typical glacial pace.

 

Quick Tip: Returning a Substring From an Oracle/MySQL Text Field (From the Start Or the End)

Miscellaneous , Oracle 2 Comments »

In addition to the normal aggregate functions (Count, Sum, Avg, etc.), most database implementations of SQL also include functions for manipulating string values.  One of those is substr(), which allows you to extract a substring of the text value in a database field by specifying the start position and the number of characters. 

So if you wanted to extract the first 5 digits of a 9-digit postal code from your "zipCode" field.  You could do it like so:

select substr(zipCode,1,5) as mainZip...

...where 1 is the starting position and 5 the number of characters.  If you knew that the format of all the zip codes was "xxxxx-xxxx", then you could also retrieve the last 4-digits by moving the starting position and adjusting the character count:

select substring(zipCode,6,4) as extendedZip...

But what if you had strings where you wanted to extract the last 4 letters in the string, but the string length varied, like these values:

Professor - GVPT
Assist. Professor - LTSC
Prof. Emeritus - ENGR

You could use the length() function to get the full length of the string and then subtract one less than the number of characters you want from it to get the starting position:

select substr(nameDept,length(nameDept)-3,4) as dept...

...but Oracle and MySQL provide a simpler method:  you can designate the starting position from the right end of the string by using a negative number:

select substr(nameDept,-4,4) as dept...

Tip on Combining Column Filtering and Column Hiding with jQuery DataTables Plugin

jQuery , Miscellaneous , Web development 1 Comment »

I'm a big fan of the jQuery plugin DataTables.  I use it in a number of projects to enhance HTML tables, making them sortable and searchable.

DataTables provides a number of advanced options and functions that let you customize the table's functionality.  One of these functions is fnFilter(), which you can use to filter the table contents based on the presence of a value in a particular column.  The most common use case for this function is to add text inputs to the header or footer of each column, and bind the use of the function to the keyup event, as in the official DataTables example:

$("tfoot input").keyup( function () {
    /* Filter on the column (the index) of this element */
    oTable.fnFilter( this.value, $("tfoot input").index(this) );
} );

 

...The "oTable" is a reference to the DataTables-enhanced table, the first parameter is the value of the input box in the footer, and the second parameter is the index value (position) of the column in the table.

DataTables also provides the fnSetColumnVis() function for showing/hiding columns, which is helpful if you have a table with a lot of columns and want to let the user get rid of the columns they don't need at that moment.  It takes two parameters, the index position of the column you want to hide and true or false to set the visibility state of the column:

oTable.fnSetColumnVis( 1, false );

 

Both are very useful functions, but you have to be careful when you enable both column filtering and column hiding on a single table. Hiding a column takes it out of the DOM, and therefore it changes the index position of every column to the right of the hidden one. While that makes sense, the problem is that DataTables keeps internal track of the original DOM positions and will execute the filter against the column with that original index value.

So say you have a table with columns A, B, and C going left to right.  DOM index positions start with 0, so column A's position is 0, column B's is 1, etc..  If you hide column A, then column B now has index position 0 and column C has position 1.  When you try and filter column C, fnFilter gets the current position of column C (1) and runs the filter against the column that originally had index position 1...which is column B.  So the table gets filtered based on column B even though the user entered their filter term in the footer of column C, which is obviously not what you want.

You can work around this issue by recording the initial index position of each column in an attribute in the input tag, and use that value as the second parameter for fnFilter().  So if you used an attribute like "colPos":

...
    <tfoot>
        <tr>
            <th><input type="text" value=" colPos="0"/></th>
            <th><input type="text" value=" colPos="1"/></th>
            ...
         </tr>
    </tfoot>
...

 

...then you'd rewrite your keyup bind function like so:

$("tfoot input").keyup( function () {
    /* Filter on the column (the index) of this element */
    oTable.fnFilter( this.value, $(this).attr("colPos") );
} );

 

That will keep the column filters tied to their original columns.