update: follow the rspec upgrade guide and try the transpec gem


Upgrading a Rails app to RSpec 3, I appreciated Jake Boxer’s guide. However, his regular expressions didn’t work in the editors I use, so I whipped up some command-line options that use the flexible unix utility sed.

If you want to actually understand the following scripts, I recommend Bruce Barnett’s sed tutorial and remember xargs is your friend.

Also, there are a few differences with sed on OSX, so these may not work on other platforms.

I didn’t attempt to handle multi-lined expressions, so I searched my spec directory for “lambda” in advance and changed those manually, and, of course, found a few other exceptions by running my tests afterwards.

Change “should ==” to “should eq”

You’ll need to change other operators also, but this was a pattern that happened to be very common in my code.

find . -type f -print0 | xargs -0 sed -i "" -E "s/^([[:space:]]+)(.*)\.should ==[:space]*(.*)[:space]*$/\1\2.should eq(\3)/g"

Change “whatever.should” to “expect(whatever).to”

This also works for should_not, since it is okay to say .to_not, even though I usually see people wrote .not_to

find . -type f -print0 | xargs -0 sed -i "" -E "s/^([[:space:]]+)(.*)\.should/\1expect(\2).to/g"

Also, check out: Cyrus Stoller’s upgrade tips

I just created a very lightweight time tracking system where we can use a Google Calendar to track who does what when and then get a spreadsheet that shows all of the hours worked by individuals.

It goes with a spreadsheet where the first row has a header like this:
Date Meeting Sarah Paul Glen

Screen Shot 2014-07-27 at 6.47.14 PM

The we just make events on Google calendar and invite whoever is working together at that time — could be a team meeting or one of us working on our own. Time will be tracked by first name of email address
(paul@whatever.com and paul@yahoo.com would all get tracked in the “Paul” column). Currently the script doesn’t support two different people who share the same email name before ‘@’.

You just have to fill in your calendar id, which you can find if you choose Calendar Settings and scroll to the bottom of the first page (in Calendar Address section).

// add menu
function onOpen() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var menuEntries = [{name:"Calculate Hours", functionName: "calculateHours"}];
  ss.addMenu("Hours", menuEntries);
  // calcular al iniciar
  calculateHours();
}
 

function meetingSummary(cal_id){
  var hours = 0;
  var cal = CalendarApp.getCalendarById(cal_id);
  Logger.log(cal);
  Logger.log('The calendar is named "%s".', cal.getName());

  var this_year = new Date(2013,0,1);
  var now = new Date()
  var events = cal.getEvents(this_year, now);
  var results = []
  
  for ( i = 0 ; i < events.length ; i++){
    var event = events[i];
    title = event.getTitle();
    Logger.log(title);
    var start = event.getStartTime() ;
    var end =  event.getEndTime();
    start = new Date(start);
    end = new Date(end);
    hours = ( end - start ) / ( 1000 * 60 * 60 );
    guests = event.getGuestList();
    
    data = {meeting:title};
    for (g in guests) {
      var email = guests[g].getEmail();
      var name = email.split('@')[0];
      Logger.log(email, name);
      data[name] = hours;
    }
    data['date'] = start.toDateString();
    results.push(data)
  }
  
  Logger.log(results);
  return results;
}

function calculateHours(){
  Logger.clear();
  Logger.log("calculateHours");
  var cal_id = "put your cal id here";
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var s = ss.getSheets()[0];
  var headerValues = s.getRange("A1:F1").getValues();
  Logger.log(headerValues[0].length);
  var columns = {}
  var num_columns = headerValues[0].length;
  for (i=0; i < num_columns; i++) {
    columns[headerValues[0][i].toLowerCase()] = i+1;
  }
  Logger.log(columns);
  //{'glen':5, 'paul':4, 'description':6, 'meeting':2, 'sarah':3, 'date':1}
  var title_column = 2;
  
  // from second row
  var results = meetingSummary(cal_id);
  Logger.log("results.length="+results.length);

  for ( var row_number = 1; row_number < results.length+1 ; row_number ++){
    var result = results[row_number-1];
    Logger.log("row_number="+row_number);
    Logger.log(result);
    for (name in columns) {
      Logger.log("..."+name+"   "+result[name]);
      if (typeof result[name] == 'undefined') result[name] = "";
      s.getRange(row_number+1, columns[name]).setValue(result[name]);
    }
  }
}

I had an idea this morning that I could make a simple desktop app by combining the lightning-fast website generation capabilities of jekyll with the awesome Node-Webkit that lets us native wrappers for HTML5 apps. I checked out this nice intro to Node-Webkit, and unsurprisingly ran into a few gotchas, documented below for other adventurer and my future self:

Simple Website with Jekyll

gem install jekyll
jekyll new experiment
cd experiment
jekyll serve

go to http://localhost:4000
you should see the default sample jekyll site

Make a Native OSX App

Download and install Node-Webkit pre-built binary

At the root of your jekyll site, create a file named “package.json”

{
  "name" : "nwk-experiment",
  "window" : {
    "width" : 800,
    "height" : 600,
    "toolbar" : true
  },
  "main" : "app://whatever/index.html"
}

The app root url is a nice feature of node-webkit which makes it pretty easy to transport any website into this system of building a native app.

jekyll build  # creates the site
cd _site
zip -r ../app.nw * 
cd ..

Most tutorials tell you to zip the directory. The first time I tried, I got an Invalid package error “There is no ‘package.json’ in the package, please make sure the ‘package.json’ is in the root of the package.” On OSX, we need to zip the files from the root of the directory that had the ‘package.json file in it. (via crashtheuniverse)

Run the App

open -n -a node-webkit "./app.nw"

When I double-click on the app.nw file, I see the directory, not my index file. I haven’t figured out that part yet. Still a work in progress!