You visit Stackoverflow.
You double check Wikipedia.
Nine times out of ten, that’s you sorted.
However, this was that tenth time.
“What is a shim?” - I could now write my own wiki entry, but remained clueless as to how to apply my new-found knowledge.
So, here goes - go eleven - the blog post.
What is a shim?
“We can solve any problem by introducing an extra level of indirection .”
A shim is a small library that intercepts and changes calls to another library, mainly to aid compatibility.
Examples of using a shim
Summarising examples I have found:
- Compatibility between versions
- Compatibility between runtime environments
- Compatibility between Operating Systems
- Multiple Rubies
Maintaining multiple versions of a library is necessary to support your clients. Shim libraries translate old to new library calls before forwarding on to the new library. Fewer libraries now need supporting.
In our example we are making version 7 of our famous quotes library Recite. We want to use an update of the Random Quote Library, V 1.1, but do not wish to upgrade to the breaking change of the Quote Library V2.0.
|Recite V 6.0||Recite V 7.0|
|Random Quote Library||V 1.0||V 1.1|
Unknown to us, the random quote library developers have stopped working on version 1 of the library and are only releasing version 2. Version 2 breaks with Version 1 of the API; to get a random quote, we must pass an empty string to the “author” attribute. The developers are maintaining compatibility by supplying a shim that sets the author to an empty string.
Developers will only have to support Version 2 of the quote library and a small shim library for compatibility.
Shim allow 64-bit applications to call 32-bit libraries. 64-bit applications cannot load 32-bit libraries. Shims solve incompatibility issues such as size of the data, pointers and dependencies. The shim library creates a child-process for completing the application’s requests. The shim acting as a go between for forwarded requests to the child-process and returning results back to the application.
Microsoft uses shims to fake an application’s Windows calls. When an application makes a system call it goes through the ‘Import Address Table’.
You can change the table and replace the Windows call with a call to a shim. The shim in the example is a ’version-lie’ shim. The application thinks it’s on a Windows 7 machine.
Linux installations normally come with a single Ruby version. Ruby developers need to have different Ruby versions on their many active projects. Rbenv uses shims to solve this problem.
Running a Ruby command in Linux, means checking for the executable in the path - which it searches from left to right. So, running the Ruby command
rails server, means Linux finds and runs Rails, a Ruby executable, in the first directory.
Rbenv adds a
shims directory and loads it with shim scripts before prepending the Path. There is a shim script for every Ruby application and running a Ruby application now means running the matching shim script. So, running rails means executing the Rails script in the
shims directory and not the Rails application in
usr/local/bin. The script works out the required Ruby version and then runs its matching application, in this case Rails, under that expected Ruby version.
Shim’s versatility makes it confusing to understand all the associated terms. Here I list similar terms and their relationship to shims.
|Adapter||a design patterns by the Gang of four that is a shim.|
|Hooking||covers a range of techniques to alter the running of software including shims.|
|Polyfill||is a shim associated with providing fallback functionality to older browsers|
Use shims for bodging by supporting older APIs, lying to applications or calling across runtime environment. Or use shims as a design choice with supporting multiple Rubies.
We can solve any problem by introducing an extra level of indirection - adding an extra hop remains a versatile tool.