A piggy bank of commands and fixes.

Ruby debugging gems

A list of debugger add-ons for ruby:
  • debug (old, built-in)
    • ruby -rdebug foo.rb
    • # works
    • # but can't view code around current line automatically like Perl's  {{v
  • pry (no step-through)
    • require 'pry' # at the top
    • binding.pry # in code to define a breakpoint
    • ruby foo.rb
  • pry with step through:
  • pry-byebug (requires ruby >= 2.2.0)

Ruby iterator cheat sheet

A list of many useful iterators in Ruby for hashes and arrays:
  • each - Do block for each item. Works on hash or array. Can use 'break'. Return nothing.
    • each_index - Same as each but pass in the index to the block
  • collect (aka map) - Do block for each item. Return array. Same as Perl map.
    • collect! / map! - Same as map, but modify array in place
  • select (aka find_all) - Do block for each item. Return array if block is true. Same as Perl grep.
    • select! (aka keep_if) - Same as select but modify array in place
    • reject - Opposite of select. Return if block is false.
    • reject! (aka delete_if) - Same as reject but modify array in place
  • inject(xyzzy) (aka reduce) - Do block for each pair of items, starting with xyzzy & first item.
  • compact - Return array with nil items removed
    • compact! - Same as collect but modify array in place
  • delete(foo) - Remove all items that are equal to foo
  • clear - Remove all items
  • empty? - Return true if array has no items
  • count - Return number of items
  • length (aka size) - Return number of items
  • flatten! - Modify array to be 1-dimensional
  • shuffle! - Shuffle in place
  • sort! - Sort in place
  • bsearch - Find item using binary search
  • combination(3) - Return all combainations of length 3
  • permutation(4) - Return all permutations of length 4
  • sort_by! - Sort in place using keys in block
  • transpose - swap rows and columns
  • zip - step through two arrays at the same time

(sourcesource, source)

URLs open in Safari instead of Chrome from Mac OSX Terminal

To set the correct default browser on OSX, go to System preferences | General | Default web browser.

If the default browser is correct but Terminal is still not behaving correctly, then change the default browser, and change it back again.



Detailed Ruby notes

A list of somewhat advanced Ruby topics:
  • Scope of constants, and namespace: link, link.
  • Keyword arguments: link.

How to apply a stash by name in git

1) In git it's easy to retrieve stashes by number like this:

git stash apply stash@{5}

But that number changes as you add more stashes to the list.

2) Instead you could perform some jiggery-pokery in bash to look up the stash by name:

git stash apply $(git stash list | grep "$NAME" | cut -d: -f1)

You can apply multiple stashes as long as they don't overlap.

3) Or you could save the commit and refer to it by tag:

git commit -m'The usual changes to aid debugging'
git tag foo
git reset --hard HEAD^
git cherry-pick -n foo

(-n is --no-commit)

This way if they overlap you can use git's merging/conflict mechanism.


Adventures in Scala 1.0

Mac OSX:

brew install sbt@1

Try the "Hello, World" example from

object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello, world!")

Run it:

Go [15:46:  learning-scala$] sbt run
[warn] No sbt.version set in project/, base directory:~alt/learning-scala
[info] Set current project to learning-scala (in build file:~/alt/learning-scala/)
[info] Compiling 1 Scala source to ~/alt/learning-scala/target/scala-2.12/classes ...
[info] Non-compiled module 'compiler-bridge_2.12' for Scala 2.12.4. Compiling...
[info]   Compilation completed in 11.024s.
[info] Done compiling.
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by (file:~/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.0/protobuf-java-3.3.1.jar) to field java.nio.Buffer.address
WARNING: Please consider reporting this to the maintainers of
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
[info] Packaging ~/alt/learning-scala/target/scala-2.12/learning-scala_2.12-0.1.0-SNAPSHOT.jar ...
[info] Done packaging.
[info] Running HelloWorld 
Hello, world!
[success] Total time: 16 s, completed 9 Feb 2018, 15:47:03

Well, it worked. But that's a helluva lot of warnings for very little code!

A colleague informs me how to set up another Hello World that's known to work:

sbt new
(enter name: hello)
cd hello
sbt run

...but I still get the warnings, although the code does output "hello" as expected.

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by (file:~/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.0/protobuf-java-3.3.1.jar) to field java.nio.Buffer.address
WARNING: Please consider reporting this to the maintainers of

It seems I've just joined Scala at a time when 1.0 has recently been released, and lots of existing code does not exactly make the compiler happy yet.

Note: The warning was not displayed the second time I ran the program. Somehow it remembers.

To do:
  • Find out how to avoid the warning.
  • Find out how to repeat the warning!

Why should you validate parameters to all subroutines?

A list of reasons:

  • If you don't catch bad data immediately, it will be propagated onwards and the program may fail in an unexpected way
    • The problem and may not even be caught by your tests if the bad data is passed on to an external system and not checked by you

Use MooseX::Params::Validate, or Params::Validate.

Moose questions

These points are not covered in the Moose documentation:

Question: If you set an attribute in the constructor, does it override the builder?
Answer: Yes, the builder is not run.
perl -MMoose -le'package Quxx; use Moose; has "qux" => ( is => "ro", builder => "_build_qux" ); sub _build_qux { die "duck" }; package main; my $q = Quxx->new( qux => "cat" ); print "qux = #".$q->qux."#"'
qux = #cat#

Question: How do you compose a writer subroutine? (aka setter / mutator method).
Answer: You don't need to, Moose creates it for you. Just define it in the attribute and then call it.
perl -MMoose -le'package Foo; use Moose; has "foo" => ( is => "rw", writer => "_set_foo" ); sub set_foo { "bar" }; sub bar { my $self = shift; $self->_set_foo(2); }; package main; my $f = Foo->new; $f->bar(4); print "foo = #".$f->foo."#"'
foo = #2#

What is DevOps?

I've heard a lot of waffling definitions of DevOps that left me with more questions than answers. A colleage explained it to me like this:

DevOps is where:
  • Developers are responsible for getting code into production, and keeping it running. They receive problem alerts from production.
  • Member of the Operations team attend Development team standups.
  • When a problem arises, the two teams work closely together to resolve it
In contrast, seperate Dev and Ops is:
  • The development team is treated like a third-party supplier, who delivers a fully working and documented application.
  • When the software breaks they are still treated like a third-party supplier

Bash shortcuts for faster editing

To open the command line in vi:

set -o vi
# type or navigate to a long command
# press [escape]
# navigate the line using vi keyboard commands
# press 'v' to open the command in vi

bash default parameters

# Set dir to $1, or "foo" if $1 isn't set

Don't grep, but filter terminal output and colour certain lines (and print all other lines too)


acoc source with dependency of term-ansicolor gem.


tail -f /some/log | perl -MTerm::ANSIColor -ne'print color("red") if /error/i; print $_; print color("reset");'


tail -f /some/log | perl -MTerm::ANSIColor -pne's/foo/color("cyan")."foo".color("reset")/e;'


tail -f /some/log | perl -MTerm::ANSIColor -pne's/(error|bar)/color("green").$1.color("reset")/eig;'

Non-functional checklist

When writing a user story or writing a spec for a piece of development work, consider the following non-functional aspects:

  • Authentication
  • Session management
  • Access control
  • Input validation
  • Output encoding/escaping
  • Encryption
  • Error handling and logging
  • Data protection
  • Communication security,
  • HTTP security features
  • Monitoring
    • Logging of every significant code path
    • Metrics for stats of usage and throughput
    • Performance: Page response time must be e.g. <2 seconds

This is especially useful when building new systems like a new app or API.

Can't connect to London underground WiFi after changing device


You just changed your phone/tablet/device. Now you can't connect to the free Virgin Media wifi at London underground stations.


  • Call your phone company's tech support (For EE it's 150, the 1, 3, 4)
  • Tell the level 1 support person that you've already tried all the troubleshooting steps including a network refresh
  • Ask to be put through to level 2
  • Ask the level 2 tech to un-register you from London underground wifi, wait 24 hours and re-register.
  • After that, follow the process for a new WiFi password (for EE it's texting EEWIFI to 9527).

Many different ways to resolve an IP to a hostname in Perl

Some different ways to look up hostnames.


  • getnameinfo() gets a hostname and a service name, so it's not exactly the same as gethostbyaddr()
  • although the docs imply you need to enter a port, that's just to get the local service name. If you set it as undef then you can ignore any service

# 1, old deprecated way

use Socket; # gethostbyaddr, inet_aton, AF_INET

sub ip_address_to_host {
    my ( $self, $ip_address ) = @_;
    my ($hostname) = gethostbyaddr(
    return $hostname;

# 2, newer better way

use Socket qw(AF_INET inet_pton getnameinfo sockaddr_in);

sub ip_address_to_host {

     my ( $self, $ip_address ) = @_;

    my $port = undef;
    my $socket_address = sockaddr_in($port, inet_pton(AF_INET, $ip_address));

    my $flags = 0;
    my $xflags = 0;
    my ($error, $hostname, $servicename) = getnameinfo($socket_address, $flags, $xflags);

    return $hostname;

# 3, best way - that only uses DNS and not /etc/hosts first

use Net::DNS;

sub ip_address_to_host {

     my ( $self, $ip_address ) = @_;

    my $res = Net::DNS::Resolver->new;
    my $target_ip = join('.', reverse split(/\./, $ip_address))."";
    my $query = $res->query("$target_ip", "PTR") // return;
    my $answer = ($query->answer)[0] // return;
    my $hostname = $answer->rdatastr;

    return $hostname;

# 4, the code golf way (no validation) - by Mark B

 perl -le 'use Net::DNS::Resolver; print ((Net::DNS::Resolver->new()->query("","PTR")->answer)[0]->ptrdname);'

Run only one subtest with Test::More

Apply this patch to Test/, version 1.302075:

<     return if exists $ENV{SUBTEST} && $ENV{SUBTEST} ne $_[0];

Here's that patch again, in unified format:

$ diff -u Test/{,.orig}
--- Test/      2017-07-27 13:23:56.000000000 +0100
+++ Test/ 2017-07-27 13:19:00.000000000 +0100
@@ -804,7 +804,6 @@

 sub subtest {
     my $tb = Test::More->builder;
+    return if exists $ENV{SUBTEST} && $ENV{SUBTEST} ne $_[0];
     return $tb->subtest(@_);

Run the test like this:

SUBTEST="put name of subtest here" /usr/bin/prove -v test_file.t

Why it's important to merge upstream at the end of every sprint

The whole idea of agile/scrum is to provide some value to the business every two weeks.

If you can tag your feature branch and deploy it to a Testing or Production environment for users or other stakeholders to see then that's great, you probably don't need to merge to master or trunk.

But say your release cycle is every two months so the new feature won't actually reach production for some time. If you don't merge the sprint's work to master, then it weakens all the good habits of agile working. Every sprint the developers will feel slightly less motivated to complete the work on time because it won't really matter, nothing is being done with the work. In this case, merging to master is a proxy for deploying to production.

If master/trunk doesn't work for you, designate a common feature branch, release branch or any other kind of special branch to merge each set of changes to. The point is that branch should not be under control of the developers anymore. Any new changes require a new issue/branch to be created, reviewed and merged separately.

There are many benefits to keeping branches small and merging upstream frequently:
  • The whole team gets to review the code, even (especially) people who haven't seen it yet
  • Smaller branches are easier for the team to review
  • Mistakes are caught earlier so less work is needed to rectify them
  • The new code can be more easily tested in continuous integration, which probably already runs off master
  • Reduces conflicts with the rest of the codebase
  • Makes it easier to work on new features in parallel (the alternative is using a common feature branch and sub-branches)
  • Keeps you honest by "publishing" your work, prevents feature creep, makes iterations clear, etc.
  • Makes it more efficient to work on and think about, you keep fewer things in your head because it's not all "up in the air"

General API design principles

API principles

Status: Draft
Working notes:
  • Read the Heroku HTTP API design guide
  • And the The twelve-factor app methodology for building SaaS
  • Use
  • Consider JSON PATCH
  • Endpoints are all nouns, use the HTTP actions as verbs.
  • All endpoint nouns must always be singular, to match with database tables. Or plural (explanation). The point is they should remain consistent with other APIs from the same team, organisation or whatever.
  • Article: Your API versioning is wrong. Conclusions:
    • Use the headers for versioning.
    • Provide a special "override" URL path for humans, that sets the header appropriately. This will make development easier
      • i.e. /api/v2/nodes --> redirects to --> /api/nodes and automatically sets header: api-version: 2
      • or /api/nodes?version=2 --> redirects the same as above
    • Don't overload the content type.
  • Also provide a similar parameter for the Accept headers. This is to make it easy to send a URL to someone that works without any special software like curl or browser plugins: /api/nodes?accept=application/json
  • Return 2xx status code to indicate the success of the HTTP request
  • Return a status field in the content body to indicate the progress of the business domain request
    • Use a "status" field, not a "state" field. Status refers to a progression.
APIs should conform to previously created APIs where that doesn't contradict the principles above.
Where legacy APIs don't follow the principles above, they should be updated to conform as a pre-requisite to any changes.


Success response:
    "data" : [{
    "type" : "xyz",
    "id": "dw12345",
    "attributes" : {
        "status": "requested",
    "links": {
        "self": "" 
Failure response:
  "errors": [{
      "status": "400",
      "source": { "pointer": "/data/attributes/environment" },
      "title":  "Invalid Attribute",
      "detail": "Environment must be int, uat or prod."


  • The response should be valid JSON API. But must the request be JSON API too?


More stuff

Some of the key takeaways:
  • An api should be easy to learn, easy to use, hard to misuse
  • Continue to write to the API early and often
  • Example programs should be exemplary - this code will end up being copied everywhere
  • When in doubt, leave it out
  • Be consistent - same word means the same thing across the api 
  • Documentation matters - reuse is something is easier to say than to do - doing it requires both good design and good documentation
  • Do what is customary - obey standard naming conventions - it should feel like one of the core APIs, know the common pitfalls for the language and avoid them

Perl one-liner to serve a directory over HTTP

plackup -e 'use Plack::App::Directory; Plack::App::Directory->new({ root => "/opt/dweb/packages" })->to_app;'


Git show untracked stash files

Git stash can save untracked files like this:

git stash --all

But to see untracked files in the stash you need this special ^3 syntax:

git show stash@{1}^3
git show stash@{2}^3
git show stash@{99}^3

This shows stash numbers, 1, 2 and 99 respectively. The number 3 always remains 3.


Jenkins email notifications

The default Jenkins email notifications are almost unusable, they contain a complete output of the build log. How to set up custom notifications:
Subject: [CI] Nightly "master" test results - Build #${BUILD_NUMBER} - ${BUILD_STATUS}


Nightly master tests



Result: ${BUILD_STATUS} - ${TEST_COUNTS, var="fail"} failures
${TEST_COUNTS, var="pass"} out of ${TEST_COUNTS, var="total"} tests passed (${TEST_COUNTS, var="skip"} skipped).


${FAILED_TESTS, showStack=false, showMessage=false}

Elasticsearch basics

Warning: Your Elasticsearch / ELK stack based logging solution may take a huge amount of disk space, and indexing of large amounts of data may also take so long that it can't keep up with the logs being generated.


curl '{endpoint}/_search?q=title:jones&size=5&pretty=true'

List indexes:

curl -s '{endpoint}/_cat/indices?v' | sort

Git: diff branch against master

Compare feature branch changes with upstream:

git diff $( git merge-base master HEAD )

Thanks Bill!

Surprising behaviour of the perl debugger for wantarray()

When you are running a perl script in the debugger, do not expect wantarray() to return the same thing it would if not running in the debugger.

If you break execution at a breakpoint, wantarray() seems to return 1 when it wouldn't otherwise.

Show the full patch for a git merge commit

Use the undocumented -m and -p options to show the main commit ID.
Why are these not in the help!?

git show -m -p 25172c1

Specify git config temporarily for one command

Use -c after the git call, before any other options:

e.g. to override your core.pager="less -r" setting:

    git -c 'core.pager=cat' log -n 3 --abbrev-commit


How to apply a named stash in git

git stash apply stash^{/foo1}

where foo1 is the full name of your stash (keep names short, with no spaces).


How to find who/what is changing a file in linux

A list:
  • inotifywait - simple, attended
  • auditctl - powerful, old school
  • file system monitoring - more involved, more complete


Reasons to use "git pull --rebase" by default

Rebase is a really sharp knife. Sometimes a really sharp knife is what you need, but it's also possible to accidentally injure yourself.

(thanks to Bill Blunn for the imagery).

Reasons to use "git pull --rebase" and not "git pull" (which merges by default):
  • Prevents massive weird conflicting-with-yourself problems if anyone rebases that branch later, e.g. just before merging to master
  • Keeps all the commits together in a bunch at the top of the tree so you can easily identify them visually
  • Allows "git diff master..HEAD" to work without including unrelated changes
Reasons not to use "git pull --rebase" and stick with "git pull" (using merges):

  • It's the default, so most people will use it, and their merges will clash with your rebases

Comparison table:

issue git pull (default: merge) git pull --rebase
git log, in a feature branch pro: you always see when master was merged
con: your commits will be interleaved with others commits
(less of a problem for short-lived branches)
pro: root of feature branch is transparently moved to head of master
pro: all your commits are kept bunched together
con: you don't know when master was merged
con: you have to notify everyone downstream
diff feature branch against master con: it's more difficult to diff against master pro: "git diff master" just works
mixing rebase and merge con: you can't rebase without weird conflicts con: you can't merge downstream without weird conflicts
(not sure about this one)

(thank you

ssh debug3: Incorrect RSA1 identifier debug3: Could not load ".ssh/id_rsa" as a RSA1 public key

When testing ssh with -vvv, you see this in the log:

    debug3: Incorrect RSA1 identifier
    debug3: Could not load ".ssh/id_rsa" as a RSA1 public key

This is not an error.
This is not the problem you're looking for.
Your problem is something else.


See also another ssh non-error.

debug2: key_type_from_name: unknown key type '-----BEGIN'

SSH debug output can be quite misleading.
When you see the following output, it does NOT indicate a problem.

debug2: key_type_from_name: unknown key type '-----BEGIN'
debug3: key_read: missing keytype
debug3: key_read: missing whitespace
debug2: key_type_from_name: unknown key type '-----END'
debug3: key_read: missing keytype
debug1: identity file /home/foo/.ssh/id_rsa type 1

The final line tells you that the key was read successfully.


See also another ssh non-error.

Whitespace woes

One very annoying aspect of software development in a team is whitespace. Many people use it differently. In a way it's unimportant, not least because it's literally invisible and so often overlooked. Yet it continues to cause problems which repeatedly waste time, and is especially distracting and troublesome for the many detail-oriented developers among us.

Clearly the only sane way to use whitespace in Perl is:

  • 4 character indent
  • only use spaces, never use tabs
  • never allow any trailing whitespace

One of the worst violations of these rules is an indent composed of mixed tabs and spaces. This is a list of reasons why that's very wrong:

  • When someone gets fed up and changes that whitespace to make it conform, it introduces noise into the code diff (they don't create a separate branch due to the overhead costs in a bureaucratic, audited environment).
  • Different text editors and web interfaces all display the indent differently, making the code hard to follow.

So, you're thinking of switching to git...

I've written a few things I learnt about using git:

I've also gathered a few links over the years, many relating to the philosophy and strategy of git. Some highlights are:

The git features I like best are:
  • git stash
    • Quickly store a copy of all your unsaved changes, so you can work on them later
    • You can use it like: git stash save "halfway through debugging issue foo"
  • git branch
    • Unlike other version control systems, git makes it incredibly easy, fast and fun to create a new branch for some changes you've made
    • Everything is stored locally until you decide to "push" it to the main repo
    • To get the most out of git, frequently commit all your work to some branch or other (at least once a day)
  • git reflog
    • See a history of (almost) all the git commands you issued
    • In case you badly mess up you can save your life here
  • git rebase -i
    • Interactively squash together several commits
    • Change commit messages
    • Even split out a large commit into several different commits
  • git log --color --stat=200,200 --decorate --abbrev-commit --relative
    • Improved log

I publish my git config.

Git cache password over https

error: cannot run git-credential-cache--daemon: No such file or directory
fatal: unable to start cache daemon: No such file or directory

# fix:
sudo rpm -i git-daemon-
git config --global credential.helper cache
git config --global credential.helper 'cache --timeout=28800' # 8 hours

Oracle clients

A short list:
  • dbVisualizer
  • Toad
  • Oracle SQL Developer

Jenkins admin notes

Some things to help on your Continuous Integration journey with Jenkins:

  • Use  docker pull jenkins  for a quick start
  • Security can be a bit fiddly to set up. Try these easy settings:
    • Jenkins’ own user database
    • Allow users to sign up
    • Logged-in users can do anything
  • There is a Role-Based Strategy plugin for more advanced use
  • If you accidentally lock yourself out, edit  $JENKINS_HOME/config.xml  to say false and restart Jenkins
  • To see a list of failing tests, configure a Post-build action for  "Publish JUnit test result report"

-- Jenkins ver. 1.642.2

Simplest easiest quickest web server script

cd ~
python -m SimpleHTTPServer 8080


PHP, pecl, phpize PDO, mysqli for Perl devs

"pecl" is the same as "cpan"
"phpize" is the equivalent of "perl"
"PDO" is PHP's DBI equivalent (i.e. general purpose RDBMS connector)
"mysqli" is the more modern version of the mysql-specific connector

Simple perl webserver


(contrast with python one-liner)


# /versions/perl-5.8.7/bin/perl -MHTTP::Server::Simple

use strict;
use warnings;

package MyWebServer;

use HTTP::Server::Simple::CGI;
use base qw(HTTP::Server::Simple::CGI);

my %dispatch = (
    '/hello' => \&resp_hello,
    '/' => \&resp_index,

sub handle_request {
    my $self = shift;
    my $cgi  = shift;

    my $path = $cgi->path_info();
    my $handler = $dispatch{$path};

    if (ref($handler) eq "CODE") {
        print "HTTP/1.0 200 OK\r\n";

    } else {
        print "HTTP/1.0 404 Not found\r\n";
        print $cgi->header,
              $cgi->start_html('Not found'),
              $cgi->h1('Not found'),

sub resp_hello {
    my $cgi  = shift;   # object
    return if !ref $cgi;

    my $who = $cgi->param('name');

    print $cgi->header,
          $cgi->h1("Hello $who!"),

sub resp_index {
    my $cgi = shift;
    print $cgi->header;
    open(my $fh,'<','index.html') or die "can't open index.html";
    local $/ = undef;
    my $out = <$fh>;
    print $out;


# start the server on port 8080
my $pid = MyWebServer->new(8085)->background();
print "Use 'kill $pid' to stop server.\n";


Change quoting level in Outlook 2007

When replying to an HTML email, the original message has a blue bar to the left (which replaces the traditional > symbol). I've seen in previous versions of Outlook that pressing enter on this line takes you to a new line without the blue line ("paragraph unindent") and allows you to write a reply inline. That's fine and what I'd expect.

But currently that doesn't work and I can't easily get rid of the blue line, meaning all my inline replies look like they're part of the original message! Quite odd. Here's how to work around the problem:

  • Change the message format from HTML to Rich-text
  • Type Ctrl-Q where you want to add a reply
  • Type your reply

Why not to use a pop-up window

If you're using a pop-up window on your website, you're doing it wrong.
It's akin to using nested tables for display, fer chrissakes!

Reasons why it's wrong:

  • It's a poor user experience because the user doesn't get any of the usual browser functionality in the pop-up window, like the back button or the icon that tells you the page is loading.
  • I'm sure I'll think of some more later

Testing Catalyst


Catalyst::Test - you get a context object back

Test::WWW::Mechanize::Catalyst - you don't have to run a separate server

Change timezone in Linux

Change this link:

$ ls -l /etc/localtime

lrwxrwxrwx    1 root     dwebadm        34 Dec  2 01:12 /etc/localtime -> /usr/share/zoneinfo/Asia/Singapore*

Cannot install Java JDK: semicolon found in selected path

There's no semi colon in the install path. But you still get the error:

        semicolon found in selected path

Solution is to move the install .exe to c:\ and run it again.

Possibly with command line switches: /v"/L c:\install.log"

(Search results show that this same issue has existed for 11 years!)

Why not to require javascript

Reasons not to require javascript for critical features of your website:
  • It can slow down the user experience.
  • Standard HTML form and browser controls are the way they are for a reason: They work. Subvert or reinvent them at your peril.
  • It's much more likely Javascript code won't work as expected in all browsers, than HTML.
  • When some part of it breaks, forcing the user to disable it, they won't be able to use the rest of the website
  • Not all browsers have javascript, for example Lynx (text only) and JAWS (i.e. screen readers which blind people use).
  • It often makes automated testing harder
  • When the user gets logged out due to inactivity, the AJAX calls may behave unexpectedly

Disclaimer: This is my opinion only, based on personal experience.

Select sub-section of putty buffer only

Are you sick and tired of waiting for your terminal to scroll down when dragging to select a large portion of the scrollback buffer in putty?

The putty developers already thought of that!

  1. Go to Putty Configuration window
  2. Choose "Selection" from category on the left of the window.
  3. Under 'Control use of mouse', choose 'Compromise (Middle extends, Right pastes)' if not already chosen.


Use Data::Compare to compare data outside a test

Need to compare data outside of a test, without generating "ok" / "not ok" TAP output? Use Data::Compare.

Perltidy docs / example config


Reference for all the possible perltidy options
More detailed explanation of some the options

Team perltidy options

4 column indentA lot of the legacy code has 2 column indent.
"horizontal tightness" 2 - no space for parenthesis tokens
 if ( ( my $len_tab = length( $tabstr ) ) > 0 ) {  # -pt=0
 if ( ( my $len_tab = length($tabstr) ) > 0 ) {    # -pt=1 (default)
 if ((my $len_tab = length($tabstr)) > 0) {        # -pt=2
ensure there's always space to the left of =>
...and always to the right of =>
always break before a closing token (default)i.e. non-block curly braces, parentheses, and square brackets docs
Lines should be no more than 79 characters wide
no "outdenting" for long comments - indent them properlyBy default, full-line (block) comments longer than the value maximum-line-length will have their indentation removed ("outdented"). This rule prevents that. docs
no "outdenting" for long quotes/stringsdocs
stack opening tokens - same as -sop -sohb -sosbPut opening parentheses, braces, etc. on the same line docs
stack closing tokensPut closing parentheses, braces, etc. on the same line docs

Options that were removed

line up parenthesesSee docs
define opening vertical tightnessPut opening parentheses, braces, etc. on the same line. Related to -lp docs
always break a line after opening tokendocs
-st -se
use as a filterInstructions to the parser docs
'for' loop semicolon spaces
The default is to place a space before a semicolon in a for statement, like this:
    for ( @a = @$ap, $u = shift @a ; @a ; $u = $v ) {  # -sfs (default)
If you prefer no such space, like this:
    for ( @a = @$ap, $u = shift @a; @a; $u = $v ) {    # -nsfs
then use nsfs.
No blanks before commentsBy default, a blank line will be introduced before a full-line comment. This rule prevents that.