Category Archives: Software Development

This category contains all my posts about software development. Tutorials, solutions to problems I’ve met, interesting things I’ve stumbled over, code samples, stuff I don’t want to forget, et cetera.

Heroku deploy sub-directory

Going through a couple of Udemy courses, and figured it was nice to keep all the code I wrote while doing them in a single github repository.

Was working nicely until I started the Node with React where part of the course is to deploy the app to Heroku which wants a full repository pushed to a certain git repo it creates for deployment. But in my case the app I want to deploy is of course a sub-directory…

Turns out there’s a git command called subtree one can use here:

# Setup
heroku login
heroku create

git remote add heroku/some-name https://git.heroku.com/{created-heroku-app-id}.git

# Deploy
git subtree push --force --prefix path/to/app heroku/some-name master

The subtree command needs to be run from the top-level directory of the git repository, but one can add a script command to the apps package.json, for example like this:

{
  "scripts": {
    "deploy": "cd ../../.. && git subtree push --prefix path/to/app heroku/some-name master"
  }
}

Unfortunately, subtree push doesn’t support --force, but a workaround for that is running it nested like this:

git push heroku/some-name `git subtree split --prefix path/to/app master`:master --force

Which unfortunately doesn’t work on Windows… but you can do it in two steps instead:

git subtree split --prefix path/to/app master
git push heroku/some-name {id-from-previous-command}:master --force

Stop Git from messing with my newlines

I’m sorry, but I just hate when Git is messing with my files…

warning: CRLF will be replaced by LF in some/file.ext.
The file will have its original line endings in your working directory.

That’s just not its job! If I want to enforce a certain new line regime, there’s linting tools, pre-commit hooks, etc.

The following should stop it, but frankly, sometimes it feels like git just keeps inventing new ways of reverting to its hyper annoying default…

# First run this to see where it's configured now
> git config --list --show-origin

# Then run whichever works...
> git config --global core.autocrlf false
> git config --system core.autocrlf false
> git config --local core.autocrlf false

Enable SQL logging in Play 2.5+

Just a note to self on how to get (in my case) IntelliJ to spit out all SQL statements happening during unit tests.

  1. Add the following to application.conf
    db.default.logSql=true
  2. Add the following to logback.xml
    <logger name="org.jdbcdslog.ConnectionLogger" level="OFF"  />
    <logger name="org.jdbcdslog.StatementLogger"  level="INFO" />
    <logger name="org.jdbcdslog.ResultSetLogger"  level="OFF"  />

Source: StackOverflow

JavaScript: In-browser export to CSV

This function will turn an array of data into a CSV file and get the browser to “download” it. It should also be wrapping and escaping values properly and, because of the BOM, be read correctly when opened in e.g. Excel.

It’s written in TypeScript ("lib": ["ES2017", "DOM"]), but should be easy to “downgrade” to regular JS if needed…

type GetValue <T> = <I extends T>(item: I) => any;
type FieldName <T> = keyof T;

export interface Columns<T> {
  [s: string]: FieldName<T> | GetValue<T>;
}

const COLUMN_SEPARATOR = ';';
const ROW_SEPARATOR = '\r\n';
const UNICODE_BOM = '\uFEFF';

const wrapValue = (value: string) => `"${value}"`;
const escapeValue = (value: string) => (value || '').replace(/"/, '""');

const toHeaderRow = <T> (columns: Columns<T>) => Object
  .keys(columns)
  .map(escapeValue)
  .map(wrapValue)
  .join(COLUMN_SEPARATOR);

const toRow = <T> (columns: Columns<T>, item: T) => Object
  .values(columns)
  .map(field => typeof field === 'function' ? field(item) : item[field])
  .map(String)
  .map(escapeValue)
  .map(wrapValue)
  .join(COLUMN_SEPARATOR);

export const exportToCsv = function <T> (data: T[], columns: Columns<T>, filename: string): void {
  const rows = [];

  rows.push(toHeaderRow(columns));

  for (const item of data) {
    rows.push(toRow(columns, item));
  }

  const csv = UNICODE_BOM + rows.join(ROW_SEPARATOR);
  const uri = `data:text/csv;charset=utf-8;header=present,${encodeURIComponent(csv)}`;

  const link = document.createElement('a');
  link.setAttribute('href', uri);
  link.setAttribute('download', filename);
  link.addEventListener('click', () => link.parentNode.removeChild(link));
  document.body.appendChild(link);

  link.click();
};

Usage

const users: User[] = [{id: 1, name: 'Alice', isCool: true}, ...];
const columns: Columns<User> = {
  'Id': 'id',
  'Name': 'name',
  'Is cool': user => user.isCool ? 'Yes' : 'No',
};
exportToCsv(users, columns, 'users.csv');