Wednesday, January 27, 2016

We all make our mountains and we make them as tall as we need them

Putting things into perspective doesn't seem to work well as a coping mechanism.  If we have a problem, it doesn't matter that the problem isn't a big deal to anyone else.  It only matters how large a problem it is to us.  When other people try to minimize a problem (mountain) or put it in perspective, that doesn't help to make our mountain smaller to us, it just minimizes us compared to our mountain.

Friday, January 15, 2016

Aurelia, Firefox and Input fields

I had a mis-behaving checkbox input field on Firefox.  The checkbox was working fine on Chrome and Safari - likely IE as well.  But it wouldn't stay checked on Firefox - kept resetting itself to unchecked.  For the short version, @jsobell on Aurelia's Gitter channel told me that Firefox triggers input attributes based on alphabetical order.  I had a model.bind(), checked.bind(), and change.trigger() on the checkbox in question in that order.  The change.trigger() was running last with the other browsers and first in Firefox.

It was sloppy code on my part, I should have processed everything in the change.trigger function instead of a kludge of model.bind and checked.bind with a change.trigger that was really carrying the main functionality.

Saturday, August 29, 2015

Dynamic input list in Aurelia

I struggled with getting this dynamic form pattern figured out in Aurelia so I'm sharing it here for myself in the future and others that might find it useful.

The features needed are:
  • Automatically add new blank input fields at the end of the list as needed
  • Allow the removal of any items from the list at any point
  • Allow changing any items in the list of inputs

Given lots of help from the Aurelia Gitter channel especially by Jeremy Danyow (@jdanyow) and Io Sulfur (@iosulfur) who created several Plunkrs zeroing in on the right solution, we came up with this approach as demonstrated in the Plunkr below.

Anytime you change the blank item field at the end, it will add another empty input field at the end. If you click on the 'X' button, it will remove that item.

Demo Plunkr

The key things to note in the app.html listing below are that the 'items' in line 6 must be your actual array.  If this is in a nested repeat.for loop and items is made available from the parent repeat.for, this approach will not update the original array only the one that is local to the repeat.for context.

The change.delegate on line 8 adds the blank line using the current index value of the repeat and the change event.  You'll see how they are used in the app.js file below.

The click.delegate very simply removes the input field by splicing out that item from the items array.

app.html


The first thing to do is to add a blank item at the end of the items array before we even present it to the View in line 7 of app.js

The addBlank function first sets the current item (based on the $index value passed from the change.delegate function) to the event.target.value and then blanks out the event.target.value.  We blank out the event.target.value as otherwise that value gets added to the end of the list of input fields.

We then check to see if the last input field is empty or not, and if it returns true, we push an empty string onto items.  (Note: if anyone can explain why you get the event.target.value instead of the empty '' string unless we reset the event.target.value, please post it in the comments - because I've not been able to figure it out.

The removeItem is pretty self-explanatory - so that's all.  Thanks for reading, hope it is useful.

Added (2015-08-30) :  When I tried this with an object as the item, I didn't need lines 12 and 13 in app.js.

app.js

Friday, August 21, 2015

Logging in Aurelia

Just some quick notes on setting up logging in Aurelia

Add customer log appender

Create a file in ./resources/custom-log-appender.js and add the following class (or any other file as long as you change the import in the main.js file).

export class CustomLogAppender {
constructor(){} debug(logger, message, ...rest){ console.debug(`DEBUG [${logger.id}] ${message}`, ...rest); } info(logger, message, ...rest){ console.info(`INFO [${logger.id}] ${message}`, ...rest); } warn(logger, message, ...rest){ console.warn(`WARN [${logger.id}] ${message}`, ...rest); } error(logger, message, ...rest){ console.error(`ERROR [${logger.id}] ${message}`, ...rest); } }

Update your main.js

import {LogManager} from 'aurelia-framework';
import {CustomLogAppender} from './resources/custom-log-appender';

LogManager.addAppender(new CustomLogAppender());
LogManager.setLevel(LogManager.logLevel.debug);

export function configure(aurelia) {
  aurelia.use
    .standardConfiguration()
    // .developmentLogging()
    .plugin('aurelia-animator-css');

  aurelia.start().then(a => a.setRoot());
}

and in your View Model

# ViewModel
import {LogManager} from 'aurelia-framework';
let logger = LogManager.getLogger('viewmodulename');
logger.debug('me');

export class MyViewModel() {
  logger.info(“Hah”);
}

Tuesday, August 18, 2015

Enabling ES2016(ES7) Async functions in Aurelia

I was having a lot of problems getting the Javascript ES2016 (ES7) async functions working in Aurelia. I knew I had to update the config.js code in Aurelia by adding es7.asyncFunctions:

System.config({
defaultJSExtensions: true,
transpiler: "babel",
babelOptions: {
"optional": [
"es7.decorators",
"es7.classProperties",
"es7.asyncFunctions",
"runtime"
]
},

I then tried to add my async method function to an Aurelia ViewModel:

async activate () {
try {
this.results = await this.api.search();
console.log(`Search results: ${this.results.evidence}`);
}
catch (err) {
console.log(err);
}
}

and promptly got this error in my javascript console after the page loaded:

ERROR [app-router] ReferenceError: regeneratorRuntime is not defined

Jeff Bellsey (@jbellsey) on Gitter -> Aurelia/Discuss was kind enough to figure out the issue and share it with me.  I was not however smart enough to understand the answer at the time. He told me I had to add the runtime to the babel-options file (<AureliaProjectRoot>/build/babel-options.js):

module.exports = {
modules: 'system',
moduleIds: false,
comments: false,
compact: false,
stage:2,
optional: [
"es7.decorators",
"es7.classProperties",
"es7.asyncFunctions",
"runtime"
]
};

The “runtime” and “es7.asyncFunctions” lines need to be added to the file.

This was a LOT of fun to sort out.  However, I now have, for me, much more understandable code using the async, try, await, catch format.  Thank you Jeff and the other fantastic Aurelians!

 

 

 

Tuesday, February 3, 2015

Python SSH Tunnel Example

Notes for myself and hopefully others.
def createTunnel(localport, remoteport, identityfile, user, server):
    """Create SSH Tunnels for Database connections"""

    import shlex
    import subprocess
    import time

    sshTunnelCmd = "ssh -N -L %s:127.0.0.1:%s -i %s %s@%s" % (
		localport, remoteport, identityfile, user, server
	)

    args = shlex.split(sshTunnelCmd)
    tunnel = subprocess.Popen(args)

    time.sleep(2)  # Give it a couple seconds to finish setting up

    return tunnel  # return the tunnel so you can kill it before you stop
				   # the program - else the connection will persist 
				   # after the script ends


def closeSSHTunnel(tunnels):
    """Close SSH tunnels - given the process handles"""

    for tunnel in tunnels:
        tunnel.kill()

localport = 27018  # local port for MongoDB
remoteport = 27017  # remote server port for MongoDB
identityfile = '/home//.ssh/id_rsa.pem'
user = 'ubuntu'
server = 'example.com'

# Start tunnel
tunnel = createTunnel(localport, remoteport, identityfile, user, server)

try:
	# Example usage
	db_server = 'localhost'
	db_port = 27018
	client = pymongo.MongoClient('mongodb://{}:{}'.format(db_server, db_port))
	pydb = client.agencies

finally:
	closeSSHTunnel([tunnel])

Monday, January 19, 2015

Installation Notes for Flask App on Ubuntu 14.04 LTS using gUnicorn

Installation Notes for Flask App on Ubuntu 14.04 LTS using gUnicorn

Followed these excellent directions from Real Python and modified for python3 and Ubuntu 14.04.
Start with updating ubuntu and loading additional packages
sudo apt-get updatesudo apt-get install -y python3 python3-pip nginx mongodb supervisorsudo pip3 install virtualenv
sudo mkdir /var/wwwsudo chown ubuntu:ubuntu /var/wwwmkdir /var/www/flask-appmkdir /var/www/flask-app/logscd /var/www/flask-app

Setup virtualenv
virtualenv flask_env
source flask_env/bin/activate
pip install -r requirements.txt
pip install gunicorn

Setup nginx

sudo /etc/init.d/nginx start
sudo rm /etc/nginx/sites-enabled/default
sudo touch /etc/nginx/sites-available/flask-app
sudo ln -s /etc/nginx/sites-available/flask-app /etc/nginx/sites-enabled/flask-app
sudo vim /etc/nginx/sites-enabled/flask-app
Add the following to the nginx flask-app conf file being edited
server {
    location / {
        proxy_pass http://127.0.0.1:8000;
    }
    location /static {
        alias  /var/www/flask-app/flAsk-app/static;
    }
}

sudo service nginx reload

Setup gunicorn start file

Setup bash script to run gunicorn
cd /var/www/flask-app
touch gunicorn_start
chmod a+x gunicorn_start
vim gunicorn_start
Insert the following into the gunicorn_start bash script
#!/bin/bash

NAME="flAsk-app"
FLASKDIR=/var/www/flask-app
VENVDIR=/var/www/flask-app/flask_env
SOCKFILE=/var/www/flask-app/sock
USER=ubuntu
GROUP=ubuntu
NUM_WORKERS=3 echo "Starting $NAME" # activate the virtualenv cd $VENVDIR source bin/activate export PYTHONPATH=$FLASKDIR:$PYTHONPATH # Create the run directory if it doesn't exist RUNDIR=$(dirname $SOCKFILE) test -d $RUNDIR || mkdir -p $RUNDIR # Start your unicorn exec gunicorn runserver:app -b 127.0.0.1:8000 \ --name $NAME \ --workers $NUM_WORKERS \ --user=$USER --group=$GROUP \ --log-level=debug \

Setup Supervisor

This will run and restart the Flask application when the application files are changed.
cd /etc/supervisor/conf.d
sudo vim flask-app.conf
Insert the following into the flask-app.conf file:
[program:flask-app]
command = /var/www/flask-app/gunicorn_start
user = ubuntu
stdout_logfile = /var/www/flask-app/logs/gunicorn_supervisor.log
redirect_stderr = true

Start flask-app gunicorn:
sudo supervisorctl update
sudo supervisorctl status
You can use the following commands as well:
sudo supervisorctl start flask-app
sudo supervisorctl start all
sudo supervisorctl help|avail|stop|restart

Test that the application is running