charlock_holmes
is a useful gem if you have to deal with
user supplied data which may come in a variety of text-encodings. Not only does
it enable you to detect the encoding of a string, but it also allows you to
transcode the string to a different encoding.
charklock_holmes
uses libicu
to deal with string encoding.
Unfortunately, the default Heroku buildpack for Ruby doesn’t include
libicu
which prevents bundler from being able to compile charklock_holmes
C-extension.
There have been a few attempts at solving this problem, most of which are
discussed over on stack overflow. The accepted
answer is a common solution, which relies on using
a version of the gem which includes a bundled version of libicu
. While this
works, it does result in very slow build times both on heroku, and locally when
doing a bundle install.
Another solution uses a custom version of the ruby
buildpack which includes libicu
— while this is a simple solution it
relies on the maintainer of that solution keeping it up to date with heroku’s
ruby buildpack.
My favourite solution seems to move in the right direction,
it uses heroku-buildpack-multi
and
heroku-buildpack-apt
to install libicu
using apt.
Unfortunately it uses a forked version of the heroku-buildpack-apt
which
adds specific behaviour for charlock_holmes
and where bundler
can find the
version of libicu
installed by apt
.
My solution builds upon the previous solution, but rather than use a custom
version of the heroku-buildpack-apt
I have added one more buildpack into the
mix — heroku-bundle-config
.
This buildpack allows you to configure your heroku bundler config in your
repository in the .heroku-bundle
directory. During the build it will move this
directory to .bundle
, and most importantly, make sure that all /app
paths
point correctly to the temporary build directory.
I’ve created a sample project, that can be deployed to heroku – the only thing
you need to do is ensure that you have set the BUILDPACK_URL
to
https://github.com/ddollar/heroku-buildpack-multi.git
:
$ heroku config:set BUILDPACK_URL=https://github.com/ddollar/heroku-buildpack-multi.git
When you push to heroku, this buildpack will check for a .buildpacks
file,
which specify the different buildpacks you want to use:
https://github.com/ddollar/heroku-buildpack-apt
https://github.com/timolehto/heroku-bundle-config
https://github.com/heroku/heroku-buildpack-ruby
heroku-buildpack-apt
will then check for an Aptfile
and install the
specified packages:
libicu-dev
Finally, you need to configure you .heroku-bundle/config
to make sure that
bundler
can use your newly installed version of libicu
:
---
BUNDLE_FROZEN: '1'
BUNDLE_PATH: vendor/bundle
BUNDLE_BIN: vendor/bundle/bin
BUNDLE_JOBS: 4
BUNDLE_WITHOUT: development:test
BUNDLE_DISABLE_SHARED_GEMS: '1'
BUNDLE_BUILD__CHARLOCK_HOLMES: --with-icu-lib=/app/.apt/usr/lib --with-icu-include=/app/.apt/usr/include
That should be all you need.