Tuesday, December 19, 2017

Bulk rename files in Emacs

Here's a problem I recently encountered at work which Emacs solved quite effectively with wdired-mode.

I had a large directory tree of Javascript files that I bulk-converted from coffeescript to ES6. The files originally had the extension ".js.coffee," but after conversion they all became ".js.js". Well, a ".js.js" file extension is absurd, right? How can I rename them all to a more sensible ".js"?

Easy -- use Dired mode to display the files, and then switch to 'wdired" (writeable-dired). String-replace the extension, save -- done and done.

But how to list all files from all subdirs in a single Dired buffer for string replacing? In Dired, subdirs can be "included" in the listing by typing 'i' when your cursor is on the subdir, but this is a pain for more than a couple of includes. After searching a bit, I cam across this bit in the Emacs docs for Dired:

The simplest way to include multiple directories in one Dired buffer is to specify the options ‘-lR’ for running ls. (If you give a numeric argument when you run Dired, then you can specify these options in the minibuffer.) That produces a recursive directory listing showing all subdirectories at all levels.

That is, "C-u 1 C-x d" to launch Dired, choose the parent dir, and then type "lR" in the minibuffer prompt for ls options. Voila! All the subdirs listed in your buffer. Now switch to wdired mode ("C-x q"), then query-replace the string ('M-%'). "C-c C-c" to save the buffer. Done.

I don't think there's an easier way to do a bulk rename.

Monday, March 20, 2017

The Trouble With Mail Attachments in Rails

I ran into a problem with a Rails ActionMailer at work recently, and I thought I'd share my findings. Attachments require a multipart mail message -- but adding an attachment does not cause the message to be multipart.

So I had an HTML-formatted email that was working just fine. The Mailer basically looked like this in the action, with only an .html template was in the views directory.

 class DdosReportMailer < ActionMailer::Base  
  layout 'layouts/mailer'  
  def daily_report(rcpts:)  
   mail(  
    to: EnvVar.get('NOREPLY_EMAIL'),  
    from: EnvVar.get('NOREPLY_EMAIL'),  
    bcc: rcpts,  
    subject: I18n.t("ddos_report_mailer.daily_report.heading")  
   )
  end  
 end  

My story required that I add attachments, but the Rails guide is not exactly clear about when and where to call the 'attachments' method. First, I tried calling it on the mail instance after it was created:

 class DdosReportMailer < ActionMailer::Base  
  layout 'layouts/mailer'  
  def daily_report(rcpts:, reports:)  
   mail(  
    to: EnvVar.get('NOREPLY_EMAIL'),  
    from: EnvVar.get('NOREPLY_EMAIL'),  
    bcc: rcpts,  
    subject: I18n.t("ddos_report_mailer.daily_report.heading")  
   ).tap do |m|  
    reports.each { |k,v| m.attachments[k] = v }  
   end  
  end  
 end  

In the Rails previewer, this looked good. HTML formatting, check; attachment, check. Tests which verified the mail body and attachments passed, so I figured the work was done.  

But when I sent the actual mail, it looked like this:


I immediately lay this problem at the feet of the MailSafe gem, which we use to prevent outbound mail in staging. The MailSafe modification seems so close to the attachment boundary, it must be causing a parsing error! Not to worry -- in production, we won't have this problem. Just to be sure, I whitelisted the original recipient address so there was no MailSafe intervention. Surprisingly, that did not help. 

I went back to the Rails guide and decided the 'attachments' call should be sent to the mailer, not the mail.

 class DdosReportMailer < ActionMailer::Base  
  layout 'layouts/mailer'  
  def daily_report(rcpts:, reports:)  
   reports.each { |k,v| attachments[k] = v }  
   mail(  
    to: EnvVar.get('NOREPLY_EMAIL'),  
    from: EnvVar.get('NOREPLY_EMAIL'),  
    bcc: rcpts,  
    subject: I18n.t("ddos_report_mailer.daily_report.heading")  
   )
  end  
 end  

Now the delivered message looked even worse! Something is wrong with the message parts.


After a bit of Googling I found a discussion about a similar multi-part problem that was solved by passing a block to the 'mail' method and using the format object which is yielded. This did the trick. Although attachments require a multipart message, adding an attachment does not cause the message to be multipart. Using a format block will. Here is the final code.

 class DdosReportMailer < ActionMailer::Base
  layout 'layouts/mailer'
  def daily_report(rcpts:, reports:)
    reports.each { |k, v| attachments[k] = v }
    mail(
      to: EnvVar.get('NOREPLY_EMAIL'),
      from: EnvVar.get('NOREPLY_EMAIL'),
      bcc: rcpts,
      subject: I18n.t("ddos_report_mailer.daily_report.heading")
    ) do |format|
      format.html { render 'daily_report' }
    end
   end
  end

Wednesday, January 25, 2017

Emacs and The Lisp Keybard

I've been using Emacs for my day-to-day developer work for over a year now. I love it, but I do sometimes get a sore left pinky (aka, "Emacs pinky") from hitting the Control key so much. It's not nearly as bad as the mouse-hand tendonitis I used to get from all the clicking in a windowed development environment, but still.

Well it turns out that Emacs' heavy reliance on the control key is explained by the "Lisp keyboards" of the 80s, which influenced its early development. Control is next to the spacebar, where you can hit it with your thumbs.


If you use Emacs, Control is supposed to be big, right next to the space bar. On Windows keyboards, that's typically where the Alt key goes now. Macs put the Command key there. It's time for some key mapping.

I spend about half my day on Macbook Pro docked with a Windows keyboard, the other half using the laptop keyboard on the go. The modifier keys are crazily different, but luckily System Preferences > Keyboard > Modifier Keys lets you pick different mappings for your different keyboards. So I've been trying to get used to this setup:



Whichever key is next to the space bar is now Control, next comes Alt (Meta), and furthest is Super/Command/Windows. CapsLock is an extra Control key, for good measure.

Outside of Emacs, it's going to take some getting used to -- esp. Cmd-C/V, Cmd-Space, Cmd-Tab, the combos I use all the time on the Mac. On the plus side, copy and paste is more compatible with Windows/Linux key combos for copy/paste, where I was already used to "Ctrl-C" with a pinky.

Inside Emacs, using the thumb for Control feels a whole lot better. Control-X O, Control-X G, etc -- I use those constantly, and the thumb just works a lot better. I recommend it!