tag:blogger.com,1999:blog-74538728572196054752024-03-05T05:51:25.241-08:00CodiformGeoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.comBlogger86125tag:blogger.com,1999:blog-7453872857219605475.post-44122698930793970652018-04-11T06:53:00.001-07:002018-04-11T06:53:31.556-07:00Managing SSH Whitelists on AWS with awswlIf you have EC2 instances on AWS, it is common for them to be layered behind firewalls implemented with VPC Security Groups. That means that if you need to access these servers directly, you may not be able to unless you take measures to make that happen.<br />
<br />
In an enterprise AWS account, there are lots of good solutions to this problem. Firstly, if you are far enough down the containerization path, you may argue that directly accessing the instances via SSH is to be avoided, that you should only be building and deploying containers. Alternately, if you do need to access these servers, you can likely do so with a VPN, either hardware or software.<br />
<br />
However, for a smaller AWS account, like a small project or small business, these solutions may be more complicated than you desire. I find myself needing to access small AWS accounts from a variety of places, as I move around a fair bit meeting with clients and working, and I need to be able to access EC2 instances on AWS while I do, so I found myself wanting a tool that would allow me to quickly add and remove my current external IP address or particular networks (expressed through CIDR blocks) to an AWS security group.<br />
<br />
So I built a little open-source tool. Since the most-popular AWS client library is <a href="http://boto3.readthedocs.io/en/latest/">boto</a>, and since python is a reasonable choice for a simple cross-platform cli tool, I built it in Python. I called it awswl (aws whitelist) and I've been refining it, adding tests, documentation, making sure it works with both python2 and python3. Now it's finally ready to release to the wild.<br />
<br />
You can find it <a href="https://pypi.python.org/pypi/awswl/1.0.0">on pypi</a> if you want to install it, <a href="https://github.com/geoffreywiseman/awswl">on GitHub</a> if you want to read the source or contribute, and you can browse the documentation on either one.Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-9781310895809073322018-04-05T08:47:00.002-07:002018-04-05T08:47:37.019-07:00Automatically Add Taxes to Invoice on New FreshbooksI migrated to the "New Freshbooks" almost two years back, I think -- and for the most part it went fairly smooth. The design is a little cleaner, although there are a few features of the old system that aren't present.<br />
<br />
Initially, when finding a feature gap, I assumed it was coming. Some of those features, however, still haven't come, and I'm tired of waiting. In particular, the ability to add taxes automatically to all the lines of an invoice is a real thorn in my side because for some of my clients I have very detailed timesheet invoices.<br />
<br />
On the latest example of this, I had 87 lines. Going through 87 lines one by one and clicking "Add Taxes", then a checkbox, then save is ... repetitive, boring, and just the sort of work that computers are great at and humans are less great at. And yet I've done this on a whole ton of invoices because there wasn't an automatic way to do this on the new FreshBooks.<br />
<br />
But no more. I gave up waiting for FreshBooks to do it and wrote a little piece of JavaScript that I can run with Tampermonkey:<br />
<br />
<pre>// ==UserScript==
// @name Add HST to FreshBooks Invoice
// @namespace http://codiform.com
// @version 1.0
// @description Go through a FreshBooks Invoice and add HST to each line
// @author Geoffrey Wiseman
// @match https://my.freshbooks.com/*
// @grant none
// @run-at context-menu
// ==/UserScript==
(function() {
'use strict';
let popovers = document.querySelectorAll("div.js-tax-picker-popover");
for( var popover of popovers ) {
let hst = popover.querySelectorAll("td.js-taxes-popover-checkbox")[1];
hst.querySelector("input").click();
popover.querySelector("button.button-primary").click();
}
alert( "Done adding HST to Invoice." );
})();
</pre>
<br />
Now I just edit an invoice, right click, select "Tamper Monkey" and then "Add HST to FreshBooks Invoice" and wait for it to run. Way, way better than three clicks per line.<br />
<br />
If you have the same problem, feel free to adopt my solution. You might need to adjust the index of the popover checkbox if you don't want to add the second-defined tax (I used to have PST and GST), now I just have HST -- but other than that it should probably work for you.Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-85950263553634409552018-01-29T07:42:00.004-08:002018-01-29T07:42:43.633-08:00ServiceWorkers coming to SafariIf ServiceWorkers are <a href="https://medium.com/@firt/pwas-are-coming-to-ios-11-3-cupertino-we-have-a-problem-2ff49fd7d6ea">coming to Safari for macOS and iOS</a>, it looks like progressive web applications will be pretty plausible <a href="https://caniuse.com/#search=service%20workers">across all major modern browsers</a> -- both desktop and mobile. You'll still have a problem with older browsers (and thus older machines that haven't updated), but this can still get you a single codebase across desktop and mobile, which is appealing to a lot of companies. It'll be curious to see how much of an impact this has on React Native traction.Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-20208940681689627672018-01-25T10:00:00.002-08:002018-01-25T10:00:45.953-08:00Configuring Firewalld on CentOS 7 with AnsibleI've been working on the configuration of a new server with Ansible; this client has CentOS servers, so I'm configuring it for the current release of CentOS 7.x, which comes with <a href="http://www.firewalld.org/">Firewalld</a>. All of the existing playbooks are set up for iptables, which was used in earlier versions of CentOS.<br />
<br />
I thought about rolling it back to iptables, but I decided to try using firewalld first. I hit some problems:<br />
<br />
<ul>
<li>Firewalld port forwarding only supports remote traffic:</li>
<ul>
<li>If I want to run Tomcat and use the firewall to forward from port 80 to the Tomcat port, then accessing http://localhost will not trigger these port forwarding rules.</li>
<li>Fortunately, you can work around that with a "direct" rule.</li>
</ul>
<li>The ansible <a href="http://docs.ansible.com/ansible/latest/firewalld_module.html">firewalld module</a> seems immature:</li>
<ul>
<li>flagged as 'preview'</li>
<li>doesn't support <a href="https://github.com/ansible/ansible/issues/28349">port forwarding</a></li>
<li>doesn't support <a href="https://github.com/ansible/ansible/issues/21439">direct rules</a></li>
<li>You can work around that by invoking `firewall-cmd` using the command module.</li>
</ul>
<li>Not easy to use `firewall-cmd` in an idempotent way.</li>
<ul>
<li>Firewalld is configured with commands, somewhat like iptables. You can run these commands using the command module.</li>
<li>It's not that easy to invoke these commands in Ansible in a way that lets you be properly idempotent -- only run this command or mark it changed if something has changed. As a result, these will run every time, and if you have a handler to restart the firewalld service, that will also trigger every time.</li>
</ul>
</ul>
<div>
What I ended up doing is configuring firewalld with commands, then looking at the configuration files that result and instead of having the ansible playbook trigger these commands, I have the playbook copy these files into place. File copying is something that is easier to do in an idempotent way than command invocation, so I can configure Ansible to copy configuration files into place (<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">/etc/firewall/direct.xml</span>, <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">/etc/firewall/zones/public.xml</span>), and then restart firewalld if the files have changed.</div>
<div>
<br /></div>
<div>
That seems to be reasonably happy.</div>
Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-54455528227599632602017-05-26T06:21:00.001-07:002017-05-26T06:23:05.350-07:00Limiting Android Localization to Supported LanguagesWhen you're working with an Android app that will be used in a variety of markets, localization is an important feature to support the different countries, date formats, number and currency formats that are used around the world. Android has deep support for localization which you can <a href="https://developer.android.com/guide/topics/resources/localization.html">read about</a> in great detail.
<br />
<br />
In iOS, in order to add support for a language, you <a href="https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPInternational/LocalizingYourApp/LocalizingYourApp.html#//apple_ref/doc/uid/10000171i-CH5-SW2">add languages to your project</a> properties in an explicit way. In Android, adding support for a language is more implicit -- you simply start localizing resources. This is easy to use but it creates a problem where you might have partial support for a language in place but not be ready to take that support live. Android doesn't offer a means of explicitly listing which languages you wish customers to have access to. This can lead to situations where you have to keep localization resources on a branch or out of source-control or otherwise limit how those files are managed. That's viable but it isn't a great fit for every workflow.<br />
<br />
On a recent client project, I did a little work with Gradle to make supported languages more explicit. I added a list of supported languages in the <span style="font-family: "courier new" , "courier" , monospace;">build.gradle</span>, used <a href="https://developer.android.com/studio/build/shrink-code.html">resource shrinking</a> with <span family="Courier New, Courier" style="font-family: "courier new" , "courier" , monospace;">resConfigs</span> and exposed the languages to Java in <a href="https://developer.android.com/studio/build/gradle-tips.html">BuildConfig</a> using <span family="Courier New, Courier" style="font-family: "courier new" , "courier" , monospace;">buildConfigField</span>:<br />
<br />
<pre class="code">// Approved Languages
def approvedLanguages = [ "en", "ru" ]
resConfigs approvedLanguages
buildConfigField "String[]", "APPROVED_LANGUAGES", "{\"" + approvedLanguages.join("\",\"") + "\"}"
</pre>
<br />
By exposing the approved languages to Java, it was possible to use it localization code, for instance to only localize dates to languages that are in the supported language list:<br />
<pre class="code">public class LocaleUtils {
public static boolean isSupported(Locale locale) {
for (String language : BuildConfig.APPROVED_LANGUAGES ) {
if (locale.equals(locale.getLanguage()))
for (String language : BuildConfig.APPROVED_LANGUAGES) {
if (language.equals(locale.getLanguage()))
return true;
}
return false;
}
public static String localizeLocalDate(LocalDate localDate, String format) {
return localDate.toString(
DateTimeFormat.forPattern(format).withLocale(getDefaultSupportedLocale()));
Locale locale = getDefaultSupportedLocale();
DateTimeFormatter formatter = DateTimeFormat.forPattern(format).withLocale(locale);
return localDate.toString(formatter);
}
}
</pre>
<br />
With this in place, we can start working on a new locale, integrating translated strings and graphics in the main line of development and know that they won't be exposed to customers until we're ready. With a little work, we could also use a different list of supported languages on a debug and production build.Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-26455329536349706832016-10-24T11:41:00.004-07:002016-10-24T11:42:42.042-07:00Swift Mutable, Hashable and Collections<p>Swift sets have similar behaviour to Java sets when it comes to mutable objects: if you have a mutable object, wherein mutation of the object can result in a change to the hash value, that can lead to surprising behaviour. The collection may store the object in a bucket based on the hash it had at the time of insertion. If you modify the object, the hash value changes, but it doesn't move from one bucket to another. This can mean that a set may report that the object is not present even when it is.</p>
<p>I made a little playground (<a href="https://github.com/geoffreywiseman/public-playgrounds/blob/master/MutableHashable.playground/Contents.swift">raw view</a>, <a href="https://github.com/geoffreywiseman/public-playgrounds/blob/master/MutableHashable.tar.gz">compressed form for download</a>) to demonstrate this issue, but in essence:</p>
<pre>
let mi = MutableInt(1)
let mis:Set = [mi]
mis.contains(mi) // true
mi.value = 2
mis.contains(mi) // false
</pre>
<p>The same object is present in the same set for both contains calls, but because the hash value has changed, the set can no longer find it.</p>Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-28450049305689022022016-09-18T09:11:00.007-07:002016-09-19T06:37:16.309-07:00Host Key Verification and AnsibleI've been using <a href="http://www.ansible.com/">Ansible</a> to configure some instances on Amazon Web Services with a client, for two reasons:
<ul>
<li>To make it repeatable in the future, if we want to configure an instance again, or another instance.</li>
<li>Because I will be repeating it immediately now, making more than one server to live behind a load balancer that are meant to be configured identically.
</ul>
When you connect to a host for the first time over SSH, you are asked to verify the host key. When you use Ansibleagainst a host for the first time, the same thing happens. If you are connecting to multiple hosts at the same time, <a href="http://toroid.org/ansible-ssh-locking">bad things happen</a> -- you get multiple prompts to verify the host key and responding to them doesn't seem to work:<br />
<br />
<pre>
$ ansible servergroup -m ping
The authenticity of host '10.0.2.161 (<no hostip for proxy command>)' can't be established.
ECDSA key fingerprint is SHA256:hEdMy3XKWV/zWobmSuwf+b6oI9xt4cYJzM1eAa2T8Ak.
Are you sure you want to continue connecting (yes/no)?
The authenticity of host '10.0.1.79 (<no hostip for proxy command>)' can't be established.
ECDSA key fingerprint is SHA256:N5iv0/+zRHk7UTsIQOUlzn2ZiU9L2xL+Fn153nlZdjs.
Are you sure you want to continue connecting (yes/no)? yes
Please type 'yes' or 'no': yes
Please type 'yes' or 'no': yes
Please type 'yes' or 'no': yes
Please type 'yes' or 'no': yes
Please type 'yes' or 'no': yes
Please type 'yes' or 'no': ^CProcess WorkerProcess-3:
Process WorkerProcess-2:
Traceback (most recent call last):
[ERROR]: User interrupted execution
</pre>
<br />
While I was pleased to read <a href="http://toroid.org/ansible-ssh-locking">the toroid.org post</a> about bugs filed, it doesn't seem to be fixed yet.<br />
<br />
Happily, as long as you connect to a single host at at time, everything is fine, and after that you can connect to the group:
<pre>
$ ansible server-one -m ping
The authenticity of host '10.0.1.79 (<no hostip for proxy command>)' can't be established.
ECDSA key fingerprint is SHA256:N5iv0/+zRHk7UTsIQOUlzn2ZiU9L2xL+Fn153nlZdjs.
Are you sure you want to continue connecting (yes/no)? yes
server-one | SUCCESS => {
"changed": false,
"ping": "pong"
}
</pre>
That's fine for a couple of servers, but it wouldn't be fun with a large cluster. If there's a better way, I haven't discovered it yet.Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com2tag:blogger.com,1999:blog-7453872857219605475.post-83397787818661238862016-07-14T11:49:00.001-07:002016-07-14T11:50:28.859-07:00Using Ligature Fonts in IntelliJIntelliJ IDEA 2016.2 was released with improved support for programming fonts with ligatures. I tried a few of them to see how I felt about them. I do some Scala in IntelliJ, so I used Scala examples to compare how I felt about things.<br />
<br />
<b>Hasklig</b><br />
<a href="https://github.com/i-tu/Hasklig">Hasklig</a> initially felt like the right font for me. It's based on <a href="https://github.com/adobe-fonts/source-code-pro">Source Code Pro</a>, which I like, although it's never been my favourite programming font. It's very round, and a bit wider than I normally choose, but legible, and the ligatures look nice.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeHLl7uHRTzugR0r0JAjvjaRO_81ETghFTWgJUw1zfqFaXCTMENrrp0U68Jln6LnBmsxoHBMXQuR1grid_p4jablQOAgzdKG44sEbr1h6jkSqunDjpjbHGSrZnAr42BvjFF1k8fVxJcW65/s1600/Hasklig13pt.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeHLl7uHRTzugR0r0JAjvjaRO_81ETghFTWgJUw1zfqFaXCTMENrrp0U68Jln6LnBmsxoHBMXQuR1grid_p4jablQOAgzdKG44sEbr1h6jkSqunDjpjbHGSrZnAr42BvjFF1k8fVxJcW65/s1600/Hasklig13pt.png" /></a></div>
<br />
However, I do like the idea of having a ligature for "!=", and Hasklig limited ligature set doesn't include one for this.<br />
<br />
<b>Fira Code</b><br />
<a href="https://github.com/tonsky/FiraCode">Fira Code</a> looks a reasonable amount like Hasklig, really. It's a little narrower, a little less round. I found it very slightly less legible at first, but it was a close second place. Notice that it does include a not-equals ligature:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRLLjGhlkkPksWU5KEYWOP0ptvXpZIVkxWJNK__BiSbTtWehAIBNyG1C4atCCzniqhYoFdFHMhAdmQDGcInNOYrAQRUo_ZMTQNHblzrfqR2JylYzZLbohD0rQr5JXh1Z1obZrVaToxnZ1_/s1600/FiraCode13pt.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRLLjGhlkkPksWU5KEYWOP0ptvXpZIVkxWJNK__BiSbTtWehAIBNyG1C4atCCzniqhYoFdFHMhAdmQDGcInNOYrAQRUo_ZMTQNHblzrfqR2JylYzZLbohD0rQr5JXh1Z1obZrVaToxnZ1_/s1600/FiraCode13pt.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Although I started with Hasklig, I have since switched to Fira. </div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<b>Monoid</b><br />
<a href="https://github.com/larsenwork/monoid">Monoid</a> is big. It's the only one that I'm showing here in 12pt instead of 13pt, and even that feels bigger than the others. It's pretty legible, but the weight feels a little too light as a result to me. Feels like coding in Helvetica Neue Ultra-Light.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAILVzeUTYu0k4woWOtWY2og9a81BfPDHidATr0bcafw0NEkFyjFUZeAfcNQ36z-TpzDdGarKsZtaVsS243I1vs8uFf7R2tOGVEeX4KnPPecT3derVyNupB7Bav3qgGR4TjkJ1LzGQf_31/s1600/Monoid12pt.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAILVzeUTYu0k4woWOtWY2og9a81BfPDHidATr0bcafw0NEkFyjFUZeAfcNQ36z-TpzDdGarKsZtaVsS243I1vs8uFf7R2tOGVEeX4KnPPecT3derVyNupB7Bav3qgGR4TjkJ1LzGQf_31/s1600/Monoid12pt.png" /></a></div>
<br />
<b>Pragmata Pro</b><br />
<a href="http://www.fsd.it/shop/fonts/pragmatapro/">Pragmata Pro</a> is narrow and tall. The wideness of Source Code Pro is one of the reasons I stayed away from it at first, but Pragmata feels almost cramped to me. I like the idea of firing a lot of text in, but it feels less legible as a result of its narrowness, so I'm not sold.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0SGrfEG6TaQCMnDR20hPsnjjNdiOV-nB79PNS8SUIkVG5lkxN89udHLnzg9cnB2jPzKvk9peeCyiDe3rCN4uXOOGYIb6CD2Ow7e9pLnBbEupvrkUGrUFAs-NFl-oLr7jR57UdjxsIA1OC/s1600/Pragmata13pt.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0SGrfEG6TaQCMnDR20hPsnjjNdiOV-nB79PNS8SUIkVG5lkxN89udHLnzg9cnB2jPzKvk9peeCyiDe3rCN4uXOOGYIb6CD2Ow7e9pLnBbEupvrkUGrUFAs-NFl-oLr7jR57UdjxsIA1OC/s1600/Pragmata13pt.png" /></a></div>
The fact that it costs €199 for the complete family does not endear me to it either. I understand making fonts is a lot of work, but many programmers aren't going to bother with a custom font at all, and making it that expensive pretty much rules out the odds of it being an impulse purchase. Some way of trailing the font in an IDE would be nice.<br />
<br />
If I felt this were the right font for me, I suppose I would probably purchase it anyway, but at that price I'd want to be sold right away, and I'm not. If it were cheap, maybe I would have impulse-purchased and given it a longer trial.<br />
<br />
Pragmata does have a lot of nice features, so if you do feel so inclined, it's still worth looking at.<br />
<br />
<b>My Choice</b><br />
To be honest, these are all pretty decent fonts, and if you're interested in having programming ligatures, I urge you to try some of them out and see which one fits for you.<br />
<br />
For me, I think I'm going to try <i>Fira Code</i> for a while. If Hasklig or Source Code Pro adds more ligatures, I might consider going back. I'll also be watching for other ligature programming fonts (Inconsolata, for instance?).<br />
<br />
Have any suggestions I should try?<br />
<br />
<br />Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-12709797908323616422016-05-31T06:19:00.004-07:002016-05-31T06:35:59.659-07:00NSDecimalNumber and unsignedIntegerValueSo, if you have an <span style="font-family: "courier new" , "courier" , monospace;">NSDecimalNumber</span> and you want to get the unsigned integer equivalent of it, you'd do <span style="font-family: "courier new" , "courier" , monospace;">[nsdn unsignedIntegerValue]</span>, right?<br />
<br />
Wrong. Or, at least, risky.<br />
<br />
I hit a bug in <span style="font-family: "courier new" , "courier" , monospace;">NSDecimalNumber</span> this week, and I thought I'd share it as a playground, which you can <a href="https://github.com/geoffreywiseman/public-playgrounds/blob/master/NSDecimalNumber.tar.gz">download</a> or <a href="https://github.com/geoffreywiseman/public-playgrounds/blob/master/NSDecimalNumber.playground/Contents.swift">read</a>, but I'll summarize here.<br />
<br />
Essentially, if you have an <span style="font-family: "courier new" , "courier" , monospace;">NSDecimalNumber</span> containing a real number with a large mantissa and you call <span style="font-family: "courier new" , "courier" , monospace;">unsignedIntegerValue</span> on it, you may get zero instead of the nearest integer. I assume it's a bug, rather than a design decision, but it's definitely risky to rely on <span style="font-family: "courier new" , "courier" , monospace;">unsignedIntegerValue</span> here. If you convert the value yourself (e.g. call <span style="font-family: "courier new" , "courier" , monospace;">floor(nsdn.doubleValue)</span>) you'll be much better off.<br />
<br />
I've filed a <a href="http://www.openradar.me/radar?id=4972120933138432">radar</a>.Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-61471753146924740072016-01-12T22:07:00.000-08:002016-01-14T07:06:12.915-08:00Opening App Transport Security failures in SafariOne of my client projects is an iOS application which, like many iOS Applications, has an embedded web view, which it uses in this case to display help content from the web. Because this app targets iOS 7.x as a baseline, it uses <span style="font-family: Courier New, Courier, monospace;">UIWebView</span> instead of, say, <span style="font-family: Courier New, Courier, monospace;">SFSafariViewController</span>.<br />
<div>
<br /></div>
<div>
The help content is part of the company's website, and the web page footer has social media links, and some of these links, if you clicked on them, wouldn't load.</div>
<div>
<br /></div>
<div>
Initially, I thought this might be because of the <span style="font-family: Courier New, Courier, monospace;">target="_blank"</span> attribute, which has been a problem with <span style="font-family: Courier New, Courier, monospace;">UIWebView</span> in the past, so I built a <span style="font-family: Courier New, Courier, monospace;">UIWebViewDelegate</span> that would inject a little bit of JavaScript to remove the targets. It didn't work. So I changed the JavaScript to set the target to <span style="font-family: Courier New, Courier, monospace;">"_self"</span>. Still didn't work:<br />
<br /></div>
<pre>- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSString *injectedJavascript = @"function "</pre>
<pre> "AppPrefix_Injected_suppressBlankTargets() {\n"</pre>
<pre> " var links = document.getElementsByTagName('a');\n"
" var hrefs = [];\n"
" for( index = 0; index < links.length; index++ ) {\n"
" var link = links.item( index );\n"
" if( link.getAttribute( 'target' ) == '_blank' ) {\n"
" link.setAttribute( 'target', '_self' );\n"
" hrefs.push( link.outerHTML );\n"
" }\n"
" }\n"
" return hrefs.join();\n"
"}"
"AppPrefix_Injected_suppressBlankTargets();";
NSString *result = [webView </pre>
<pre> stringByEvaluatingJavaScriptFromString:injectedJavascript];
DLog( "Injected javascript to suppress blank targets on links: %@", result );
}
</pre>
<div>
<br />
Having failed on two assumptions, I realized that I should verify my assumption instead, and implemented <span style="font-family: Courier New, Courier, monospace;">webView:didFailLoadWithError</span>, which quickly showed me that the actual problem was App Transport Security.</div>
<div>
<br /></div>
<div>
The social media links? Some of them are HTTP rather than HTTPS, and those ones won't load because App Transport Security doesn't like it. That left options.</div>
<div>
<br /></div>
<div>
<b>Disable App Transport Security?</b></div>
<div>
I don't like going this route until I have to. I don't want to whitelist certain sites because the social media links might lead to other sites which would also need to be whitelisted and then eventually you just end up disabling app transport security anyway.</div>
<div>
<br /></div>
<div>
<b>Open ATS Failures in Safari</b></div>
<div>
What I ended up doing instead is listening for errors (<span style="font-family: Courier New, Courier, monospace;">webView:didFailLoadWithError</span>) and then check the error. If it was an app transport security failure, pull the URL out of <span style="font-family: Courier New, Courier, monospace;">NSError</span>'s <span style="font-family: Courier New, Courier, monospace;">userInfo</span> and then open that link in mobile safari instead.</div>
<div>
<div>
<br /></div>
</div>
<div>
It's fairly straightforward. Write a <span style="font-family: Courier New, Courier, monospace;">UIWebViewDelegate</span> like this:<br />
<br /></div>
<pre>- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSURL *failedUrl = [self parseATSError:error];
if( failedUrl != nil ) {
DLog( "ATS Failure, opening in Safari: %@", failedUrl );
[[UIApplication sharedApplication] openURL:failedUrl];
} else {
DLog( "Failed to load, with non-ATS error: %@", error );
}
}
- (NSURL *)parseATSError:(NSError*)error {
if( error == nil )
return nil;
if( ![error.domain isEqualToString:NSURLErrorDomain] )
return nil;
if( error.code != -1022 )
return nil;
NSString *url = error.userInfo[ NSURLErrorFailingURLStringErrorKey ];
return [NSURL URLWithString:url];
}
</pre>
<div>
<br />
This seems to work. So if you're using an <span style="font-family: Courier New, Courier, monospace;">UIWebView</span> and running afoul of App Transport Security, maybe this will help you.</div>
<script src="http://127.0.0.1:35729/livereload.js?ext=Safari&extver=2.1.0"></script>Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com1tag:blogger.com,1999:blog-7453872857219605475.post-40428108353197692002016-01-05T05:55:00.001-08:002016-01-05T07:31:06.147-08:00AppStore Release JoyAfter what felt like an eternal delay, one of the my client's apps has finally gone live in the App Store. It is always nice when something you write goes live, but somehow the app store process, painful though it may be, makes the final victory of "Hey, there's my App, live in the App Store" feel that much sweeter.<br />
<br />
I've already seen some press for the app (and related wearables), so it's already somewhat promising, but it'll be interesting to watch it grow.<br />
<script src="http://127.0.0.1:35729/livereload.js?ext=Safari&extver=2.1.0"></script><br />
<script src="http://127.0.0.1:35729/livereload.js?ext=Safari&extver=2.1.0"></script>Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-65082451395691154192015-11-05T06:01:00.002-08:002015-11-05T06:06:21.733-08:00Fabric / Crashlytics ADD in ImagesRemember how <a href="http://blog.codiform.com/2015/10/fabric-crashlytics-growth-add.html">I commented</a> that Crashlytics' daily email was so focused on the daily delta that it lacked a sense of the overall trends?<br />
<br />
Here's two daily emails back to back. October 30th:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjylK85PcEmRWZLSNMZM0QuVt5QUuwx20V_ObKGeyqLfPgNeD8bpKeeLAar3XRg4HCSMiulG99zKd9eMXEsnef3EAUXFy2C9F3uHhE4Iis__WhwyFr8Fa-vBLQ2iO_uH1lEtM3pHrfr57cr/s1600/crashlytics-add-oct30.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="100" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjylK85PcEmRWZLSNMZM0QuVt5QUuwx20V_ObKGeyqLfPgNeD8bpKeeLAar3XRg4HCSMiulG99zKd9eMXEsnef3EAUXFy2C9F3uHhE4Iis__WhwyFr8Fa-vBLQ2iO_uH1lEtM3pHrfr57cr/s320/crashlytics-add-oct30.png" width="320" /></a></div>
<br />
<br />
October 31st:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOHIDUQxnMU4NkWqd4olkQ5qsyqmh6X0yZufHZcj8goqsL03_7sg-3u6J-5YWofa9FqrJ_VyjuCrwUqI1hQGf3C9h4yb4YHOfKuBUfnZ_aLH4elhtOYLhSf6-QtSqiMKrNnMlgK-ANfFBA/s1600/crashlytics-add-oct31.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="100" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOHIDUQxnMU4NkWqd4olkQ5qsyqmh6X0yZufHZcj8goqsL03_7sg-3u6J-5YWofa9FqrJ_VyjuCrwUqI1hQGf3C9h4yb4YHOfKuBUfnZ_aLH4elhtOYLhSf6-QtSqiMKrNnMlgK-ANfFBA/s320/crashlytics-add-oct31.png" width="320" /></a></div>
<br />
<br />
App name pixelated because I haven't cleared sharing this information, so I'm sharing it without the identifiable information.<br />
<br />
These kinds of transitions, from "growing like crazy" to "growth slows" and back are a daily occurrence which totally obscures any real trends about how many users are actually using your app.Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-68147046068441629472015-10-27T07:35:00.002-07:002015-10-27T07:35:33.502-07:00GWT Plugin Still Not Ready for Eclipse 4.5?Eclipse Mars / v4.5 was <a href="https://projects.eclipse.org/projects/rt.equinox/releases/4.5.0-mars">released June 24, 2015</a>. The Google Plugin for Eclipse 4.5 has not yet been released. If you go to the Google Plugin for Eclipse <a href="https://developers.google.com/eclipse/docs/getting_started">quickstart page</a>, you can see that it's still showing links for Eclipse 4.4, 4.3 and 4.2 / 3.8.<br />
<br />
In essence, if you'd like to use GWT and you want to use the latest Eclipse, there's no officially supported option nearly four months later.<br />
<br />
If you do a little searching, you'll find a few threads on the subject, :<br />
<br />
<ul>
<li>Some threads recommend just using the Eclipse 4.4 plugin (<a href="https://groups.google.com/forum/#!topic/google-plugin-eclipse/1356CGHq0Gs">Google group for the Google Plugin</a>).</li>
<li>Other threads (<a href="http://stackoverflow.com/questions/31044037/would-gwt-plugin-for-eclipse-4-4-work-with-eclipse-mars-4-5">stackoverflow</a>, ) suggest that the 4.4 plugin doesn't work well with Eclipse 4.5.</li>
<li>And of course, some threads just got no answer (<a href="http://devquestion.tk/32021667/when-google-plugin-for-eclipse-4-5-mars-would-be-available.html">devquestion</a>)</li>
</ul>
<div>
A friend tried Eclipse 4.5 and the Google Plugin for Eclipse 4.4 together and hit a NoClassDefFound error for Java2HTMLEntityReader. A quick search found that it was a <a href="https://groups.google.com/forum/#!topic/google-plugin-eclipse/1356CGHq0Gs">bug</a>, but that raises further questions about a <a href="http://gwt-plugins.github.io/documentation/">plugin fork</a>, which looks like it might have been around <a href="https://groups.google.com/forum/#!search/gwt$20eclipse$20plugin$20fork/google-web-toolkit-contributors/PB3vfIsUceA/v71_Puo4HXYJ">since 2013</a>. It looks like there's <a href="https://github.com/gwt-plugins/gwt-eclipse-plugin/milestones/GWT%20Plugin%202.0">a lot of work underway</a>.</div>
<div>
<br /></div>
<div>
Ultimately, between this and some of the <a href="https://groups.google.com/forum/#!topic/google-web-toolkit-contributors/lq82Uc7JMIQ">posts about GWT 2.8</a>, this adds to my long-growing sense that GWT is stagnating. It hasn't descended into a terrible state yet by any means. It's quite usable for existing projects. Still, I wouldn't recommended it for a new project, and I'd recommend that people using it on existing projects start thinking about the long-term plan for their projects and consider a plan to move away from GWT at some point and how to manage that transition.</div>
<br />
<script src="http://127.0.0.1:35729/livereload.js?ext=Safari&extver=2.1.0"></script>Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-62732107482079816262015-10-22T07:31:00.001-07:002015-11-05T06:05:01.334-08:00Fabric / Crashlytics Growth ADDCrashlytics' / Fabric has a daily email which gives you a sense of recent crashes, and also some basic stats. It's useful, but they don't seem to consider that there can be a fair amount of day-to-day variance and attempt to smooth that out and give you a sense of real trends. Instead, everything is about yesterday's change:<br />
<br />
<ul>
<li><b>Day N</b>:</li>
<ul>
<li>New sessions up 20%!</li>
<li>Your app is on fire!</li>
<li>Seek investment immediately! </li>
</ul>
<li><b>Day N+1</b>:</li>
<ul>
<li>New sessions down 15%!</li>
<li>Your app is beginning to stagnate!</li>
<li>Oh noes!</li>
</ul>
</ul>
<div>
<b>UPDATE</b>:<br />
I <a href="http://blog.codiform.com/2015/11/fabric-crashlytics-add-in-images.html">captured images</a> from two back-to-back emails as a good example.</div>
Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-81548440598369998072015-09-24T10:58:00.000-07:002015-09-24T10:58:53.965-07:00Searching for Android Documentation in DashAndroid developers: are you using <a href="https://kapeli.com/dash">Kapeli's Dash</a> to read Android docs on a Mac? Want to be able to search for inherited methods without hitting "expand all" first? Request it!<br />
<br />
<script src="http://127.0.0.1:35729/livereload.js?ext=Safari&extver=2.1.0"></script>
<br />
<blockquote class="twitter-tweet" lang="en">
<div dir="ltr" lang="en">
<a href="https://twitter.com/GeoffreyWiseman">@geoffreywiseman</a> I might change it to auto-expand depending on how many users request it. So far you're the first one.</div>
— Kapeli (@kapeli) <a href="https://twitter.com/kapeli/status/647105982049468416">September 24, 2015</a></blockquote>
<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-52987230186052604362015-09-22T11:08:00.003-07:002015-09-22T11:09:12.148-07:00Limited Integration between MailChimp and MandrillI'm surprised by how limited the integration is between <a href="http://mailchimp.com/">MailChimp</a> and <a href="https://mandrill.com/">Mandrill</a>. Given that Mandrill is a product developed by MailChimp, I was expecting the integration between the two to be pretty strong, and it really doesn't seem to be.<script src="http://127.0.0.1:35729/livereload.js?ext=Safari&extver=2.1.0"></script><br />
<div>
<br /></div>
<div>
They're not the same use case, I admit. MailChimp is more of a broadcast / marketing email platform, with elements like campaigns to consider, whereas Mandrill is a transactional email service, for personalized delivery of email messages like notifications from an application. I'm not surprised that they have somewhat different feature sets as a result. </div>
<div>
<br /></div>
<div>
I was a little surprised to discover that Mandrill and Mailchimp have a totally different account system, and that while you can link your MailChimp account to your Mandrill account, it's not like having a single account with multiple products/services.</div>
<div>
<br /></div>
<div>
The more you look, the deeper the differences get, and the more surprised I was.</div>
<div>
<br /></div>
<div>
For instance, look at templates. MailChimp allows you to define email templates that you might send to a mailing list. Think of something like a sale email, where you might configure a template and then merge in a few specifics as you're sending to each person. MailChimp has a sophisticated WYSIWYG editor that you can use to define these email templates.</div>
<div>
<br /></div>
<div>
Mandrill also has templates. You might want to configure a template for the welcome email for your application in Mandrill rather than having the application encode all the details of what's in the email. This means that later, you could come back in and modify the template without changing the application.</div>
<div>
<br /></div>
<div>
However, Mandrill doesn't have a WYSIWYG editor. If you wanted to have someone on your growth hacking team change the email, they'll have to know HTML to do it. That may not be the end of the world, depending on your company structure but it was a surprising inconsistency for me, and a bit of a missed opportunity.</div>
<div>
<br /></div>
<div>
There are integrations between the two. For instance, you can actually create an HTML template in MailChimp using their editor and then send the template over to Mandrill. But when you do so, you don't seem have the option of choosing which template to replace, or to make use of Mandrill's template versioning. And if you make use of Merge Codes (think personalized fields) in MailChimp's designer, some, but not all of those codes are supported in Mandrill, but neither MailChimp nor Mandrill seems to mind if you make use of them and then send the template from MailChimp to Mandrill. Those codes won't do their job anymore, but neither system tells you that, from what I can see.</div>
<div>
<br /></div>
<div>
I just think MailChimp has missed an opportunity, both to integrate these two systems more tightly, encouraging MailChimp customers to consider Mandrill and vice versa, and also, frankly, to improve the experience of both systems by leveraging the work done by the other. Why doesn't MailChimp offer their WYSIWYG designer in the Mandrill interface? Can MailChimp templates by sent to Mandrill to become alternate versions of existing templates rather than replacing them? If not, why not?</div>
Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-38017439820624227692015-08-12T10:50:00.003-07:002015-08-12T20:39:26.615-07:00Mocking HTTP ServersI am doing some HTTP-based integration work for a client on a GWT-based project (which is why I wanted to know <a href="http://blog.codiform.com/2015/08/gwt-httpclient-version-compatibility.html">which HttpClient version to use</a>). I'm still waiting on access to the third-party system, but I've already written and tested the code to generate the XML request and parse the XML response, so I wanted to take a step farther and write the code that would connect that with HTTP:<br />
<br />
<ul>
<li>Generate XML using the XML writer/generator</li>
<li>Send the XML over HTTP</li>
<li>Wait for the HTTP Response, check it (status code, etc)</li>
<li>Parse the HTTP response's body using the XML parser/reader</li>
</ul>
<br />
But if I were going to do that, I want to test it, and testing it requires something to listen for HTTP requests (or to mock out the HTTP client, but this is already a pretty thin layer). I don't have access to the real service yet, so I can't write an integration test and, to be honest, I tend to try not to rely on integration tests. So basically I want something that can pretend to be an HTTP Server of some kind but that has an API that makes writing test code convenient. I went looking for candidates to easily test an HTTP-based client in Java.<br />
<br />
When I'm picking tools in other platforms, I often consider the popularity of the dependency as well as other signs of its maturity. In Java, it's hard to get good numbers for such things. First of all, while there are some dependency management systems for Java, they're not universal or nearly-universal like they are on other platforms. Maven's central repository is probably the closest thing to that, but even then, there are lots of projects that download their dependencies directly. And, sadly, Maven doesn't publish stats about their dependencies, stats you could use to gauge how many other people are using and trusting a particular library vs. another.<br />
<br />
However, after looking at some of the alternatives, I decided to start with <a href="http://mocky.io/">mocky.io</a>. That worked pretty well, but I don't really love using an external server for a simple test. That test wouldn't run offline, wouldn't run if the internet was down, or if mocky.io was having problems. It's basically too much of an integration test, and not even an integration test against the real service. Still, it worked well, and it's a reasonable place to start if you just want to fire up a simple test without adding new dependencies to your project.<br />
<br />
I looked at alternatives to replace it. <a href="http://wiremock.org/">WireMock</a> has a reputation for being very capable and having a good API, but perhaps not kept as up-to-date as one might like and a little heavy-weight. I also looked at <a href="http://www.mock-server.com/">MockServer</a>, which looks very capable but also looks pretty heavy. My requirements for now are super-simple, so I ended up using <a href="https://github.com/square/okhttp/tree/master/mockwebserver">okhttp's MockWebServer</a>, one of Square's open-source projects. It's very simple, reasonably light, very fast, and has enough capability for my simple needs. I like it.<br />
<br />
There are lots of alternatives, from something custom (Netty, RxNetty, Spray) to other competitive libraries (<a href="https://github.com/jadler-mocking/jadler/wiki">jailer</a>,<a href="https://github.com/kristofa/mock-http-server">mock-http-server</a>) and services (<a href="https://github.com/square/okhttp/tree/master/mockwebserver">Sandbox</a>), but for the moment, MockWebServer does the job for me.<br />
<br />
<script src="http://127.0.0.1:35729/livereload.js?ext=Safari&extver=2.1.0"></script>Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com1tag:blogger.com,1999:blog-7453872857219605475.post-38210588282073483682015-08-10T12:10:00.002-07:002015-08-10T12:10:38.707-07:00GWT / HttpClient Version CompatibilityIf you're using GWT and Apache HttpClient in a single project, it can be useful to know which version of HttpClient ships with which version of GWT, because GWT includes HttpClient directly in its jar, and conflicting versions can be a bit of a pain.<br />
<br />
I can't find that information published anywhere else, so I'm publishing it here. Basically, look in the <a href="https://github.com/gwtproject/gwt/blob/master/dev/build.xml">dev/build.xml</a> file for the tagged version of GWT that you care about. Since I've already done that, here's the result for several of the most recent releases:<br />
<br />
<table>
<thead>
<tr><th>GWT Version</th><th>HttpClient Version</th></tr>
</thead>
<tbody>
<tr><td>2.7</td><td>4.3.1</td></tr>
<tr><td>2.6.x</td><td>4.3.1</td></tr>
<tr><td>2.5.x</td><td>4.1.2</td></tr>
<tr><td>2.4</td><td>4.0.1</td></tr>
</tbody></table>
<br />
I hope this will be useful for someone else.Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-50498684246030010902015-06-19T07:30:00.003-07:002015-06-19T08:41:17.623-07:00Web Assembly is Backed by the Right PeopleThere have been a few attempts over the years to take a more revolutionary approach to pushing the web forward. Google's Dart, Microsoft's TypeScript are some examples of these. While these have been interesting to skim, I have never followed them too closely because they lacked the kind of support they would need to succeed. JavaScript alternatives can compile to JavaScript, but without being built into the browser, they were never going to replace JavaScript.<br />
<br />
Yesterday, news of the Web Assembly initiative broke:<br />
<ul>
<li>TechCrunch: <a href="http://techcrunch.com/2015/06/17/google-microsoft-mozilla-and-others-team-up-to-launch-webassembly-a-new-binary-format-for-the-web/">Google, Microsoft, Mozilla And Others Team Up To Launch WebAssembly, A New Binary Format For The Web</a></li>
<li>Ars Technica: <a href="http://arstechnica.com/information-technology/2015/06/the-web-is-getting-its-bytecode-webassembly/">The Web is getting its bytecode: WebAssembly</a></li>
</ul>
It's far too early to decide whether or not this will work out, there's very little detail to go on at this point. However, I was pleased to see that the group of companies putting some support behind it seems to be the right group. If Apple, Mozilla, Microsoft and Google all put their weight behind an initiative to improve the web, there's a much better chance of it succeeding than if one, or none of them does.<br />
<br />
Now, "weight" is a subjective term, and it wouldn't be hard to imagine one or all of these companies not putting enough force behind this to make it a success. But if they really do all commit significant resources to this kind of effort, there's a real chance that this could be a moment in the history of the web that we look back on as a turning point.<br />
<br />
It's still too early to have much of an opinion, but I'm interested to see where this goes.<br />
<script src="http://127.0.0.1:35729/livereload.js?ext=Safari&extver=2.1.0"></script><br />
<script src="http://127.0.0.1:35729/livereload.js?ext=Safari&extver=2.1.0"></script>Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-62242785818777691012015-05-25T07:23:00.000-07:002015-05-25T07:24:42.540-07:00Javascript Testing FrameworksOn a recent <a href="http://clarity.fm/geoffreywiseman">Clarity</a> call, one of the topics we discussed was JavaScript Testing Frameworks.<br />
<br />
It’s a complex topic, more complex than it is in many other platforms, because JavaScript testing frameworks are pretty granular – there are lots of pieces and they can be used together or independently.<br />
<br />
There are:<br />
<ul>
<li>Test runners</li>
<li>Unit testing frameworks</li>
<li>Integration testing frameworks</li>
<li>Assertion frameworks</li>
<li>Mocking frameworks</li>
</ul>
Some tools are very specifically just one of these. Other tools cover more than one category or blur the lines between several of these.<br />
<br />
If you’ve used some of these, you might have a preference based on what feels right for you. If you haven't, you might use a different criteria: popularity.<br />
<br />
If you don’t have a preference, picking the most popular test framework is useful for a few reasons:<br />
<ul>
<li>You are crowdsourcing the research. If the most people are using a particular framework, there’s often a reason for that. It might not be the best framework, or the up-and-coming framework, but it’s probably mature and reasonably solid.</li>
<li>If you have an issue, you probably won’t be the first person to have that issue, and finding answers to your problems is often easier.</li>
<li>When hiring or outsourcing, it’s a lot easier to find someone with experience with the popular frameworks than the niche frameworks.</li>
</ul>
<div>
Of course, using the popular frameworks isn't likely to give you a competitive advantage, but it's up to you to decide when it's worth seeking that kind of advantage. I'm not sure that Javascript testing frameworks is the first place I'd seek it.</div>
<h2>
Gauging Popularity</h2>
Of course, if you’re trying to assess the popularity of a tool, there’s a bunch of different ways you could do that, including:<br />
<ul>
<li><a href="https://www.npmjs.com/">npm</a> statistics</li>
<li><a href="http://stackoverflow.com/">stack overflow</a> questions</li>
<li>job trends (e.g. <a href="http://www.indeed.com/jobtrends">indeed</a>)</li>
<li>google <a href="https://www.google.ca/trends/">search trends</a></li>
</ul>
<h2>
Candidates</h2>
What are some of the primary candidates for JavaScript and testing?<br />
<h3>
QUnit</h3>
<a href="https://qunitjs.com/">QUnit</a> is a unit testing framework that was originally part of JQuery.<br />
<br />
Popularity:
<br />
<ul>
<li><em>NPM</em>: ~20k downloads in the last month. Often over 500 downloads per day.</li>
<li><em>Job Trends</em>: QUnit was initially a strong contender against Jasmine until early 2013, after which it has stayed steady while Jasmine has quadrupled. In early 2014, Mocha also surpassed it and has now doubled.</li>
<li><em>Stack Overflow</em>: a distant third in total questions, and rarely top of the trending tags for Javascript test frameworks.<br />
Basically, QUnit has been around for a while, it’s mature, but it’s clearly not at the same popularity level as Jasmine and Mocha. It’s probably not a terrible choice, but those people looking for the popular framework should focus their energy on either Jasmine or Mocha instead.</li>
</ul>
<h3>
Jasmine</h3>
<a href="http://jasmine.github.io/">Jasmine</a> is a behaviour-driven test framework, intended to be a fairly complete system on its own – it comes with assertions, spies, mocking.<br />
<br />
Popularity:
<br />
<ul>
<li><em>NPM</em>: Almost ~300k downloads in the last month. Was hitting ~1k/day in 2014, regularly over 5k/day in Q1 2015 and now often over 10k/day.</li>
<li><em>Job Trends</em>: Jasmine is the leading JavaScript testing framework, although Mocha has been making steady progress, and is probably the strongest alternative.</li>
<li><em>Stack Overflow</em>: Jasmine is the leader in tag trends and total questions, but the data is close and varies wildly.</li>
<li><em>Google</em>: Jasmine got the early lead and has held it. Mocha is making some headway.</li>
</ul>
<h3>
Mocha</h3>
Test framework designed with an extension model in mind.<br />
<br />
Popularity:<br />
<ul>
<li><em>NPM</em>: Mocha seems to dominate NPM downloads, with 2M downloads in the last month, and many days over 50k downloads/day this year.</li>
<li><em>Job Trends</em>: Mocha is a relative newcomer, but has already surpassed QUnit and is making headway on Jasmine. Still, Jasmine is featured in twice as many job postings as Mocha, so it’s still the underdog.</li>
<li><em>Stack Overflow</em>: Half of Jasmine’s total questions, but pretty competitive on tag trends.</li>
<li><em>Google</em>: Mocha got the latest start on the search trends, but has already surpassed QUnit. It’s not clear if it’s making headway on Jasmine on search trends.</li>
<li><em>Stackshare</em>: Mocha handily beats Jasmine <a href="http://stackshare.io/javascript-testing-framework">on Stackshare</a>. I’m not sure if people are more happy to share that they’re using Mocha (e.g. aspirational) or if the kinds of people on Stackshare are more likely to use Mocha (e.g. selection bias).</li>
</ul>
Mocha is clearly a credible alternative to Jasmine, and seems better suited to people who want to mix and match pieces of the framework rather than relying on a single complete solution. I feel like Mocha is ‘in fashion’ or ‘trending’ right now now. Still, I don’t think you’ll go that far wrong with either Mocha or Jasmine.<br />
<h3>
Karma</h3>
<a href="http://karma-runner.github.io/">Karma</a> is a test runner, popularized by AngularJS. It is test framework agnostic, and works with QUnit, Jasmine and Mocha.<br />
<br />
Popularity:<br />
<ul>
<li><em>NPM</em>: Almost 1M downloads in the last month and regularly over 30k dowloads/day this year.</li>
<li><em>Job Trends</em>: Karma is the only JavaScript test runner with meaningful penetration on job postings that I could find, although it’s certainly less common than any of the test frameworks.</li>
<li><em>Stack Overflow</em>: Vastly ahead of all the other JavaScript test runners on total questions.</li>
<li><em>Google</em>: Karma’s pretty recent on Google search trends, but it’s already competitive with Mocha in terms of search.</li>
</ul>
<h3>
Chai</h3>
<a href="http://chaijs.com/">Chai</a> is an assertion framework, but one that has an extension model of its own, allowing yet another layer of library to layered on top of it.<br />
<br />
Popularity:<br />
<ul>
<li><em>NPM</em>: ~850k downloads in the last month and regularly over 20k downloads per day. Chai is about twice as popular as Should, but they’re in the same league.</li>
<li><em>Job Trends</em>: Chai is the only assertion framework to have meaningful results on job trends. Of course, doesn’t help that its two strongest competitors, expect.js and should.js have more generic names that make searching a little harder.</li>
<li><em>Stack Overflow</em>: Doesn’t seem to place on tag trends, but well ahead of should.js on total questions.</li>
<li><em>Google</em>: Up alongside QUnit, but below Mocha and Jasmine.</li>
</ul>
<h3>
Should.js</h3>
<a href="https://github.com/shouldjs/should.js">Should</a> is a BDD-style assertion framework for node and browsers.<br />
<br />
Popularity:<br />
<ul>
<li><em>NPM</em>: Almost 400k downloads int he last month, and regularly over 10k/day downloads this year.</li>
<li><em>Job Trends</em>: Although it’s harder to search for “javascript” and “should” and get useful results, neither “shoud.js” nor “shouldjs” seem to show up on significant numbers of job posts.</li>
<li><em>Stack Overflow</em>: Well behind Chai on total questions.</li>
<li><em>Google</em>: Hard to get clean results, but “should.js” is clearly below Chai.</li>
</ul>
<h3>
Others</h3>
There are, of course, countless others. Some of them are specific to a particular platform, but many are simply alternatives to those listed above, or another tool related to testing but not fitting within the broad categorization above. <br />
<br />
I’ve tried to cover the most popular set, but there are lots of others on the long tail of popularity, such as:<br />
<ul>
<li><a href="https://github.com/Automattic/expect.js">Expect</a> is another BDD-style assertion framework, built by Automattic, for node.js and browser testing. At about 200 downloads/day and <10k downloads in the last month, it’s clearly far less popular than Should and Chai.</li>
<li><a href="http://wallabyjs.com/">Wallaby</a> is another test runner.</li>
<li><a href="https://github.com/caolan/nodeunit">NodeUnit</a> is a test framework built on node.js’s assertions.</li>
<li><a href="http://phantomjs.org/">PhantomJS</a> is a headless WebKit with scriptable JavaScript API which can be used for testing.</li>
<li><a href="https://code.google.com/p/js-test-driver/">JSTestDriver</a> is another test runner.</li>
<li><a href="https://github.com/cucumber/cucumber-js">Cucumber.js</a> is a behaviour-driven development (BDD) framework for JavaScript.</li>
<li><a href="http://dalekjs.com/">Dalek</a> is more of an end-to-end framework, with a runner, assertions, browser support and more.</li>
<li><a href="https://github.com/krasimir/atomus">Atomus</a> is a test framework for client-side tests in a node environment by simulating a browser.</li>
<li><a href="https://github.com/airportyh/testem">Testem</a> is another JavaScript test runner. At 200k NPM installs in the last month, it’s probably the most credible alternative to Karma.</li>
<li><a href="http://sinonjs.org/">Sinon</a> adds spies, stubs and mocks, and is often used with Chai.</li>
<li><a href="http://angular.github.io/protractor/">Protractor</a> is an end-to-end testing tool for AngularJS.</li>
<li>Although code coverage tools aren’t specific to testing, they are often used in conjunction with testing. Some of the candidates here are:</li>
<ul>
<li><a href="https://github.com/gotwarlost/istanbul">Istanbul</a></li>
<li><a href="http://blanketjs.org/">Blanket</a></li>
<li><a href="http://tntim96.github.io/JSCover/">JSCover</a></li>
<li><a href="http://timurstrekalov.github.io/saga/">Saga</a></li>
</ul>
<li><a href="http://nightwatchjs.org/">Nightwatch</a> is an end-to-end testing framework that works with Selenium and node.js.</li>
<li><a href="http://facebook.github.io/jest/">Jest</a> is layered on top of Jasmine with the goals of making Jasmine that much easier to use (“painless”).</li>
<li><a href="http://mmanela.github.io/chutzpah/">Chutzpah</a> is another JavaScript test runner that seems to be built with the Microsoft stack at least partially in mind.</li>
</ul>
<h2>
What Should I Use?</h2>
That’s ultimately your decision, but if popularity is your primary criterion, then <em>Karma</em> with either <em>Jasmine</em> or <em>Mocha</em> & <em>Chai</em> are clearly the leaders. <br />
<br />
If you want to talk about this or something else, <a href="https://clarity.fm/geoffreywiseman">call me on Clarity</a>, or <a href="http://www.codiform.com/contact.html">get in touch</a>.Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-47239995862701931212015-04-21T14:45:00.000-07:002015-04-21T20:26:21.837-07:00Wrapping Up a CITS eApp Notification FeedI'm in the late stages of wrapping up a project to add a <a href="https://www.cliedis.ca/cits.php">CITS</a> eApp Notification feed as a integration between an insurance client of mine and a vendor's system (<a href="http://www.bluesun.ca/">bluesun's WealthServ</a>), and I thought it might be helpful to look back over the effort and share things that I learned anew or, at least, things I received fresh reminders of along the way.<br>
<br>
If you haven't encountered some of these terms elsewhere, let me explain:<br>
<br>
<ul>
<li><i><a href="https://www.cliedis.ca/cits.php">CITS</a></i> is the Canadian Insurance Transaction Standardization effort by <a href="http://www.cliedis.ca/">CLIEDIS</a>.</li>
<li><i><a href="http://www.cliedis.ca/">CLIEDIS</a></i> is the Canadian Life Insurance EDI Standards organization.</li>
<li><i>eApp Notification feed</i> is a feed to inform vendors of an eApplication submission. Basically, it carries core information about a new application from one organization to another.</li>
</ul>
<br>
For instance, an insurance company might want to send information about new policies in the application process to, say, <a href="http://www.bluesun.ca/">WealthServ</a> or <a href="http://www.virtgate.com/">VirtGate</a> so that brokers could login and see their policy, and get updates and commission information.<br>
<div>
<br></div>
I've been taking notes in an outline to write up, and the outline is now big enough that I'm sure I'm going to need more than one post. So I'm starting here with a brief description of the project and some of the terms, and then I'll follow that with a series of posts covering many of these topics:<br>
<br>
<ul>
<li>Integration Projects and Models</li>
<li>Generic Data Models</li>
<li>Monitoring and Logging</li>
<li>Security</li>
</ul>
<br>
That's not an exhaustive list, but it's a start. More to come.<br>
<br>Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-63014154668082561502014-08-12T09:09:00.001-07:002015-02-08T08:04:04.755-08:00Git Tower 2 Recommendation: Cautious Buy (Update: Buy)I've been using <a href="http://www.git-tower.com/">Git Tower</a> for a couple of years now, and I'm happy to recommend v1.x as a solid GUI Git client for OS X. There are a few things I'd like to see improved, but the basics are quite good, and it's been relatively bug-free for me (although after doing a Twitter search, I can see some people had some problems with v1.x that I didn't).<br>
<br>
Since Git Tower v2.0 was released, I've installed a trial and I've been using it on and off in parallel with Git Tower v1.x. In general, I like it. I think it's a fine evolution of Git Tower, I think the UI changes are mostly improvements, and I'm looking forward to using it full-time.<br>
<br>
Having said that, I don't think it's ready for prime-time use, at least not for me. I've reported a few bugs already while using the trial, and I'm <a href="https://twitter.com/search?q=%40gittower%20bug&src=typd">not the only one</a>. And if you read over the <a href="https://twitter.com/search?q=%40gittower&src=typd">mentions that @gittower has on Twitter</a>, I think that's the general sentiment: looks good, but needs a bit more attention.<br>
<br>
There has been a few point releases already (v2.0.3 at the time of this writing) and I expect there will be more. Basically, I'd say it looks like a promising upgrade, but if you're in no rush, it wouldn't hurt to wait a little longer. If you're curious, then download the trial in the meantime. If you really want to use Tower 2, you probably can, but accept there might be a few issues along the way.<div><br></div><div><b>Update</b>: I have been using Git Tower 2 for months now and almost all the issues have been worked out. It still, isn't perfect, but I don't think the remaining issues warrant much caution. I would say "buy".</div>Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-18518519991441425852014-06-11T10:55:00.001-07:002014-06-11T12:25:58.410-07:00TweetDeck XSS Vulnerability in Desktop ApplicationTweetDeck's recent <a href="http://thenextweb.com/twitter/2014/06/11/tweetdeck-users-xss-vulnerability-means-revoke-access-now/">XSS vulnerability</a> highlights one of the downsides to using web technologies to build a desktop app.<br />
<br />
Of course, no app is immune to vulnerabilities, and using desktop application platforms certainly doesn't prevent all vulnerabilities. But TweetDeck's vulnerability exists in part because the content it displays is delivered in the same form as its user interface.<br />
<br />
A desktop application that failed to properly control web content that it was displaying would still expose the web container in which the content was running, but that wouldn't make it vulnerable to manipulating the application controls. A badly secured web view to display a tweet within in a native application with native controls for displaying tweets would still make it hard to, say, trigger the application to <a href="https://twitter.com/derGeruhn/status/476764918763749376">retweet the content automatically</a>.<br />
<br />Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-10892320392513096512014-05-23T07:24:00.000-07:002014-05-23T07:24:01.049-07:00Moo v2.0 ReleasedI put the finishing touches on the second release of one of my open-source projects, Moo, last night. I'd been updating the documentation for a few weeks now to get that in sync with all the code changes that have been taking place. Now that it's finally done, I've released Moo v2.0 up to the Maven central repository.<br />
<br />
Moo v2.0 is the first release that was driven more from requests made by users of Moo than for my own needs. If you're using Moo and you'd like to make a request, get in touch over email, twitter, or GitHub, and I'll see what I can do.<br />
<br />
For those of you already using Moo, I'd love to hear some thoughts about how Moo v2.0 works for you and what enhancements you still need.<br />
<br />
If you're not using Moo, it's a relatively unintrusive way of mapping one object graph to another in Java. Check out the <a href="http://geoffreywiseman.github.io/Moo/">website</a>, the <a href="https://github.com/geoffreywiseman/Moo/wiki/Release-2.0">release notes</a> or the <a href="https://github.com/geoffreywiseman/Moo/wiki/">wiki</a> to learn more.<br />
<br />Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0tag:blogger.com,1999:blog-7453872857219605475.post-9563117028726042272014-04-22T10:49:00.001-07:002014-04-22T10:49:19.363-07:00Dash Cheatsheet for git-svnOne of my clients uses Subversion for source-control, and since I spend a lot of time in git anyway, I have been using <span style="font-family: Courier New, Courier, monospace;"><a href="https://www.kernel.org/pub/software/scm/git/docs/git-svn.html">git-svn</a></span> to interact with their subversion repository. I like being able to commit locally even when I don't have a connection to their server, and then push the commits up to subversion when I do have a connection.<br />
<br />
However, while <span style="font-family: Courier New, Courier, monospace;">git-svn</span> lets me use some of the same tools that I use elsewhere, like Git Tower, the syntax for <span style="font-family: Courier New, Courier, monospace;">git-svn</span> isn't exactly the same as either the <span style="font-family: Courier New, Courier, monospace;">git</span> or <span style="font-family: Courier New, Courier, monospace;">svn</span> clients, and it's not a perfect abstraction, which means that I occasionally have trouble remembering the exact syntax or the way to do something like find out which git commit corresponds to a subversion revision number.<br />
<br />
Since I've been using <a href="http://kapeli.com/dash">Dash</a> as a documentation browser as well, and since <a href="http://blog.kapeli.com/dash-2-now-available">Dash 2.0</a> introduced cheat sheets, it seemed like I could save myself some time by writing a <a href="https://github.com/Kapeli/cheatsheets/blob/master/cheatsheets/Git_Subversion.rb">git-svn cheatsheet</a> for Dash, which I did, and since it might save someone else some time, I contributed it to the Dash <a href="https://github.com/Kapeli/cheatsheets">cheatsheet repo</a> (using a <a href="https://github.com/Kapeli/cheatsheets/pull/46">pull request</a>).<br />
<br />
It's available now, so if you ever use <span style="font-family: Courier New, Courier, monospace;">git-svn</span> and you use Dash, you should be able to download it within the app. And since the repository is open, you can even submit a pull request for changes to my cheatsheet if there are things you'd like to add to it (or, if you prefer, you can let me know, and perhaps I'll make the change).Geoffrey Wisemanhttp://www.blogger.com/profile/05205826495553720705noreply@blogger.com0