Skip to main content

curses! conditional ruby gem installation within a gemspec

Update 2014-03-26: curses 1.0.1, containing ruby/curses#4, removes the need for this workaround. Hurrah!


From Ruby 2.1.0 onwards, curses has been removed from the Ruby standard library and extracted to the curses Ruby gem. For sidekiq-spy, the Ruby gem providing Sidekiq monitoring in the console, that necessitated some enthusiastic dancing using conditional Ruby gem installation within a gemspec, to maintain support for Ruby 1.9.3 and Ruby 2.0.0.

To use curses in Ruby 1.9.3 and Ruby 2.0.0, all that is needed is require ‘curses’. In Ruby 2.1.0, specifying the dependency gem ‘curses’ lets you carry on unharmed. But the curses gem does not appear to work properly in Ruby 1.9.3. Thus, the Ruby gem can be installed conditionally, perhaps for Ruby 2.1.0 onwards. This same approach could be used to install different gems or versions based on which way the wind is blowing, or the Ruby version used. In a non-gem project, this could be accomplished using

# Gemfile

gem 'curses', :platform => :ruby_21

or, because the :ruby_21 platform does not appear to exist in Ruby 1.9.3, maybe something like

# Gemfile

gem 'curses' if RUBY_VERSION >= '2.1'

For a gem, however, this would only be evaluated at build-time, not install-time. The Wikibook Ruby Programming has an excellent chapter entitled RubyGems. This describes an approach where the gem is installed within the umbrella of native extensions building. I found that not all of this appeared to be necessary for my use-case, but the approach seems to work well.

First, register the extension in the gemspec:

# sidekiq-spy.gemspec

spec.extensions << 'ext/mkrf_conf.rb'

Then, create the extension file itself. In my case, I wanted to install curses only for Ruby 2.1.0 or later, but this can easily be adapted.

# ext/mkrf_conf.rb

require 'rubygems/dependency_installer'

di = Gem::DependencyInstaller.new

begin
  if RUBY_VERSION >= '2.1'
    puts "Installing curses because Ruby #{RUBY_VERSION}"

    di.install "curses", "~> 1.0"
  else
    puts "Not installing curses because Ruby #{RUBY_VERSION}"
  end
rescue => e
  warn "#{$0}: #{e}"

  exit!
end

puts "Writing fake Rakefile"

# Write fake Rakefile for rake since Makefile isn't used
File.open(File.join(File.dirname(__FILE__), 'Rakefile'), 'w') do |f|
  f.write("task :default" + $/)
end

The puts messages won’t usually show up during installation, but are useful for gem install using the -V flag. This should be all that’s necessary for handling the dependency at install-time, in this case installing the curses gem for Ruby 2.1.0 or later and not otherwise.

Next, ensure that the gem is loaded before being required. I found a variety of information online saying to use a variant of gem ‘curses’ whilst rescuing Gem::LoadError, but this did not appear to be necessary in my case. Merely requiring as before seems to be working fine (spaghetti crossed).

However, this still doesn’t handle the dependency for development-time, without which, curses isn’t available with Ruby 2.1.0. For this, a conditional development-dependency declaration ensures that the curses gem is available, whilst having no effect on install-time (at least, with the usual bundle install --without development test). Alternatively, the Gemfile could be used similarly. As mentioned, I did not have success with using the :platform => ruby_21 approach for Ruby 1.9.3.

# sidekiq-spy.gemspec

if RUBY_VERSION >= '2.1'
  spec.add_development_dependency "curses", "~> 1.0"
end

Of course, it’s very possible that there are better ways of doing all this. If you know of one, I would be very interested to hear it. :)

Peace,
tiredpixel ☮

emmental

—so you’re saying, the dog was basically wearing glow sticks? however did you work that out?!
—emmental, my dear watson.
—pardon?
—you see, whenever i eat it, i get odd dreams.
—so you didn’t deduce it?
—how? there was also this guy called ramanujan; he kept talking a lot about blood.
—can i just write it as ‘elementary’?

replacing the keyboard of a macbook air

I dislike dirty keyboards. It’s quite a thing, for me—so much so, I regularly clean my keyboards with tissue and a healthy dose of the liquid created by the magical creatures who live at the bottom of gardens. This has been my wont since time unmemorable. The other day, I instead used a sponge. It turns out that laptops do not like sponges. Allow me to tell you why.

Without the wish to spoil any surprises, sponges absorb water. Wipe-wipe, wipeity-wipe, accidental-squeeze, hmm-maybe-that’s-too-much-soapy-water, it’ll-probably-be-fine. Except it wasn’t fine. No-no. (shake) It’ll dry out overnight, thought I. It didn’t. The next morning, most keys didn’t work, and I started to consider that I might have a problem.

I tried all the usual fixes: vacuuming the keys; putting it in the oven (Gas Mark 1, door open, timed carefully so as not to spoil the taste, of course); leaving it on top of a radiator; compiling Ruby on loop; letting it watch its choice of foreign-language films on its own all day (a two-pronged attack of getting the graphics card toasty and letting it have some laptop-time and thus hopefully feeling better about life). It typed a few messages to itself, but no Shakespeare. Imagine my surprise when none of these cure-alls helped.

Perhaps I should point out that my laptop was out of warranty. Perhaps I should also warn the tempted reader by pointing out that my laptop has subsequently become so far out of warranty as to probably not be allowed to use the name MacBook Air anymore.

I tripped into the large store of forbidden fruits, feeling a little woebegone. I feared the bill would be expensive; clearly I couldn’t pretend it had always been like that, and a couple of online stories detailed such a repair as costing hundreds of dollars. What I wasn’t expecting, however, was that there were no available appointments. It seems Geniuses are much in demand. Fear not; a hair-dryer to the rescue. With added rice.

I opened my laptop and disconnected the internal keyboard, after which an external keyboard worked without having its polite requests shouted down by the aqueous party within. I removed every single key except for the Caps Lock (which didn’t approve of what the other keys were doing, and kept itself to itself throughout). This took some hours. There was ample time for reflection. I thought I’d managed to remove the keys without damage except to one tiny bit which hopefully didn’t matter too much anyway. I left the keys in a box of rice overnight to dry out.

This didn’t seem to help, however, so I tried a hair-dryer, carefully styling both keys and frame. This didn’t help, either. In fact, all these approaches did were to lead to an unpleasant game of one-player Scrabble, at the conclusion of which I realised I’d damaged the keyboard frame probably beyond reasonable repair and really had to get it replaced.

I ordered a keyboard from the electronic seaside inlet. This arrived on metaphorical wings of eagles. I compared the keyboards quizically. Most keys matched. Most—because I’d mistakenly ordered a US, rather than a UK, layout. For most keyboards, this would simply be a matter of keys having identity issues. But in this case, the keyboard wouldn’t even fit; the US layout has a horizontal Return key, and the UK layout has a vertical. I tried again, with greater success.

You can skip the rest if you don’t want a screw-by-screw account of my activities. I would say just look at the pretty pictures, but I’m concerned they might give bad dreams. Like most electrical products, the MacBook Air has a mixture of screw types: magical, and impossible. The magical fitting is a small, star-shaped screw, a bit like CR-VT5 but not quite. I purchased an expensive ‘Mac’ screwdriver which fitted. I removed the rear case screws—8 short, 2 long. I removed the battery pack—3 long, 2 short. Somebody (for once, not me) had already damaged the corners of the battery pack by over-tightening the screws. No matter. I disconnected what are probably the speakers, but they didn’t say anything so I can’t be sure. This was an anxious time, as the long, black mounts (I’m guessing also the antennas?) were stuck on. I carefully levered them up. I’d already disconnected the keyboard data ribbon. I disconnected the backlight ribbon. I disconnected the screen data connector—patiently, thankfully, as I didn’t intially realise it’s locked by a bar and then is removed vertically. I disconnected the broad flat cable connecting the two main boards—another vertical connector. I disconnected the screen power ribbon. I disconnected what is probably the microphone but might be an alien listening device for all I know.

Then to remove the motherboard. I disconnected the SSD—so impressively small! It slides out, in case you’re wondering and following along at home (which I seriously hope you’re not). Underneath the SSD is a screw—I found this after a game of let’s-apply-gentle-pressure-oh-no-this-doesn’t-feel-right. There’s a screw near the USB ports. There’s a screw on the power connector board underneath the broad flat cable. The fan was tricksy; there are 5 screws, some of them hidden under cables. The screws are also of inconsistent lengths, which later made me wish I’d written this down at the time instead of reconstructing it afterwards when reassembling.

With a few rubber washers, the keyboard was finally exposed. I proceeded to remove the 39 screws surrounding the keyboard. Yes, 39. It turns out these were of type impossible, which required a diversion to some shops. At one of these I found what I needed: coffee. Oh, and I also found a CR-VPH00 fitting (but not in the same shop). It first seemed that CR-VPH000 fitted—until I destroyed the thread of one of the screws, that is. If you’re already squirming, don’t read this next bit. I removed the keyboard. It doesn’t lift out; it’s riveted in or something. Also by this point, the case is nowhere near as tough as it looks when it’s stuffed with innards. Also, the screen was very exposed, so care was needed with anything sharp. One crack at a time, I broke the rivets of the keyboard and removed it from the shell, bending the keyboard in the process. I decided this was acceptable, so long as I didn’t bend the shell or scratch the screen. For the broken screw, I broke the edge of the keyboard, and then used pliers. An ugly job. It can probably be done a lot better. But I do not know how; by this point, I was gaining the distinct impression that it is not intended that customers replace their keyboards themselves.

But finally the keyboard was removed, and work to install the new keyboard could begin.

I could not anchor the keyboard anywhere near as well as it had been done previously, because of the missing rivets. But I hoped the 39 screws would be sufficient once the rest of the innards were in, and this turned out to be the case. The keyboard I installed was used, so the seal around the edges wasn’t quite daisy-fresh. Electrical tape to the rescue (black tape is best, so it matches; I wouldn’t like to have a laptop which had poor colour co-ordination inside). With the rubber washers replaced, it actually looked quite fetching.

After this, everything was much easier. All that remained was to attach the various cables, try to remember which screws went where, add the SSD, and attach the speakery things back onto the case. For this, I used a crafty glue gun. It probably wasn’t necessary, but I didn’t want to risk anything breaking. Of course.

Although I suspect very few people will find themselves treading this road of tool-juggling and patience-or-else-you’ll-snap-it, I would encourage those who do to remember that no matter how it feels at times, it is possible to put it back together again. Neatly, even—though I say so myself.

I’d hate to bore the two readers who have made it thus far, so I’ll let you use your imagination for the battery pack and rear case. When I powered on the laptop, I discovered… it works! In fact, you’ll be delighted to learn that I’m using the fully-functional keyboard to write this endearing tale. And clearly, everyone will be delighted that the keyboard was fixed and this delightful story could be written.

The only real problem is that the keyboard, as I have mentioned, was used. This means I will shortly feel compelled to clean it.