How YOU can work with vim

October 22nd, 2008

I recently saw a great podcast on reddit titled  “How I work:  Rails with Vim” (http://blip.tv/file/1372096).  It showed how the author had made shortcuts to use vim more effectively.  Specifically, how to do a find from the current directory, get a list of files, and jump to any of those files.  TextMate fans will recognize this as a project file list with the ability to jump to that file.  Also the demo had a vim plugin that would do some replacements.

While it’s neat to see what he had done, the screen cast didn’t say _how_ he had done it.  It was almost like he had this cool funcitonality, which I was excitited to start using, but there were no instructions on how _I_ could use it.

So, I spent a little time figuring it out.  Here’s what I came up with:

VIM-

This is an alias to get a list of files in the current directory and below, and load that into vim.  Here’s my version

alias vim-=”find . -name .svn -prune -o -type f -print |vim -R -”
This finds all files (not directories “-type f”) under the current directory and prunes the .svn directories.  It then reads this list into vim from standard out.  It reads the file list as read-only “-R” so that you don’t have to worry about saving it or doing q! to get out of the editor

NOTE:  The find command may take some time with large code bases.

Next, change your vimrc file so that control T opens a new window with the file under the cursor
map <Ctrl-T> :new <cfile><Enter>
Apparently, <cfile> is the file name under the cursor.  You can use vnew instead of new to get a vertical window to the right.
Also, you can put this line in the vimrc to make the window open below the current window instead of above.
set splitbelow

Grill

The author also had a script that does a grep that is recursive, case insensitive, and lists the matching file names and puts that into a vim editor.  Here is my version.

I don’t know enough about aliases in bash to take arguments, so I just wrote a script.  My ~/bin directory is in my path, so I created a script called grill with this line
grep -ril $1 . |grep -v “.svn”  |vim -R -

Now “grill user” will open a vim window with a list of files that have the word user in them, and control-T will jump into that file.

Replacements

The last feature of this demo was the ability to input a shortcut and get several more keystrokes.  For this I use AutoHotKey in windows.  It is a great little utility that allows you to type “/ult/” and a tab, and it erases /utl/ and inserts /user/local/tools/tomcat*/apache*/, or whatever else you want.  It’s great for naviaging long paths or abbreviating frequent logins.  Now J@H maps to joe@hotmail.com<tab> password <enter> and it pops up on the screen as if you had done it yourself.  It’s very impressive in demos, and it works in any window, not just vim.

My Month-A-Page Calendar

October 5th, 2008

Recently I needed to present data that relates to a month’s worth of appointments in Ruby.  In one scenario, the user might have 6 or 7 appointments on the same day for a dozen days in the month.  In another scenario they may only have 2 in a month.  Either way, I wanted to show the entire month and the title of each appointment on a calendar that took up the whole screen.

I looked for plugins, but nothing seemed to fit the bill.  Most of the plugins are for choosing a specific date (and time).  I downloaded and installed the calendar_helper plugin, but it didn’t seem to fit the bill, either.  Not until I dig in and played with it did I see that this was just what I was looking for.

Here’s how I got it to work:

First, install calendar helper

script/plugin install http://topfunky.net/svn/plugins/calendar_helper/

Then create the appointment model

script/generate model appointment name:string starts_at:datetime duration:integer
 exists  app/models/
 exists  test/unit/
 ... and so on

Update the database

rake db:migrate

Next, create a new controller, an index page, and a detail page

script/generate controller appointments index detail
 exists  app/controllers/
 exists  app/helpers/
 create  app/views/appointments
 exists  test/functional/
 create  app/controllers/appointments_controller.rb
 create  test/functional/appointments_controller_test.rb
 create  app/helpers/appointments_helper.rb
 create  app/views/appointments/index.html.erb
 create  app/views/appointments/detail.html.erb

And add it to the routes and bounce the app

map.resources :appointments

In the controller, we’ll want to set the default year and month to the current one.  We’ll also want to get the list of appointments for the needed month.  For simplicity’s sake, I’m just going to hard code some appointments here.  They will all be for the current day.

class AppointmentsController < ApplicationController
def index
@cal_time = Time.now
@appointments = []
@appointments << Appointment.new(:name=>"Lunch with the Boss", :starts_at=>Time.now.change(:hour=>12, :minute=>0) )
@appointments << Appointment.new(:name=>"Interview - Big Corporation", :starts_at=>Time.now.change(:hour=>8, :minute=>0) )
@appointments << Appointment.new(:name=>"Interview - Hot Startup", :starts_at=>Time.now.change(:hour=>3, :minute=>30) )
end
...

In the view we will need to do two things:

  1. We don’t want to loop through all the appointments for every day, so in order to handle the data intelligently, we’ll make an array with an entry for each day of the month.  All the appointments that fall on a particular day will be added to an array for that day.  This should make sense shortly.  If not, you can skip this part for now.
  2. We want to create a calendar with calendar_helper, and on each day we want a link to each appointment on that day.

To achieve number one here is the code

#Collect all the days into an array of arrays, so we can pull them out by day
appt_by_day = [];             # Holds arrays of appointments by day of month
@appointments.each{ |apt|
day = apt.starts_at.mday
if appt_by_day[day].nil?    # If this day does not have an array
appt_by_day[day] = []     # Then make one
end
appt_by_day[day].push(apt)  # Add this appointment to this day
}

To achieve number two, we use the calendar helper with the block that does processing for each day

calendar(:year => @cal_time.year, :month => @cal_time.month,  # 1
:previous_month_text=>"<a href='/make_this_smarter'><<</a>", #2
:next_month_text=>"<a href='/make_this_smarter'>>></a>"
) do |d|
display_string = ""                                #3
if !appt_by_day[d.mday].nil?                       #4
apt_for_day = appt_by_day[d.mday]
end
apt_for_day.each{ |a|                              #5
display_string += "<a href='/appointments/detail/#{a.id}'>#{a.name}</a><br>"
}
end
["<div class='dateBox'>#{d.mday}</div><br><div class='appt'>#{display_string}</div>" ] #6
end

This code does several things

  1. On this first line it sets the year and month for the calendar.  These should be integers.
  2. At number 2 and the next line it sets the URL for the next and previous month links.  These are left as an exercise to the user.
  3. This block runs for each day of the month, with the day as “d”.  The display_string is the string we want to display for this day.  It should contain a link to the detailed view for all the appointments for this day.
  4. The appt_by_day array for each day will either be nil (no appointments) or an array of appointments, since you can have several in the same day.
  5. The array of today’s appointments is in apt_for_day.  For each of these we want to add a link to the display_string for display.  Once we have the whole string we’ll send it to the helper.
  6. This line tells the helper what we want to display in the box.  I have put the day and the display_string in different divs, just to make formatting easier.

This works, but it’s pretty ugly.  The boxes for each day are too small, and there aren’t enough colors.

To make it nicer we need to install the css.

script/generate calendar_styles

This gives us some pretty css that the calendar can use.  Edit the views/layout/appointments.html.erb to look more like this

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>

<html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”en” lang=”en”><head>
<meta http-equiv=”content-type” content=”text/html;charset=UTF-8″ />
<title>Events: <%= controller.action_name %></title>
<%= stylesheet_link_tag ’scaffold’ %>
<%= stylesheet_link_tag ‘calendar/blue/style’ %>
</head>
<body>

<p style=”color: green”><%= flash[:notice] %></p>

<%= yield  %>

</body>
</html>

I added some css details for the divs we created and got this (picture was taken the next day):

Next up will be making it as wide as the screen, adjusting the colors, and making the users happy.

Taking full advantage of the flexibility of calendar_helper allowed me to deliver a full-screen view of a month with all the calendar information within easy reach of the user.

Blogging is like working on the bathroom.

June 15th, 2008

Recently I noticed that the floor in my bathroom was uneven.  Near the toilet the linoleum had an odd line it where the floor underneath was curled up a little.  We had only lived in this house six months and I knew that this was a new problem, but we had family in for a week and I had just broken my leg, so I let it go for a while.

About a month later I was a little more mobile, and I started to investigate.  Let me preface this by saying I’m kind of a handy guy, but I had never worked on bathrooms or flooring.  I started by looking at the sink and toilet in that bathroom.  The sink looked fine, but the toilet’s bolts that held it to the floor were loose.  That’s not good.  I emptied the toilet and took it off (first time for me) and saw that the floor around it had started to weaken.  So, I started pulling up linoleum (first time again).  The floor underneath this was “underlayment” which is particle board for the floor to sit on, and it looked like the toilet had been leaking into the underlayment, which became swollen, and that’s the problem we were seeing.  Around this time my wife starts talking about replacing the linoleum with hard wood floors.

At this point, I’m a little out of my depth.  I call my dad, who is very handy, and talk to some people at Lowe’s.  Everybody says floors are easy, and I figure I’ll need a plumber and carpenter to put things back together, but I certainly don’t need them to take things apart.  I mean, I can see that the underlayment needs to be replaced, and surely I can pull that out.  So I do (another first).  But that means removing the vanity, washer, dryer, pictures, and baseboards (another first).  Then I cut into the underlayment and start prying it up.

Under the underlayment, theres a “subfloor”.  This is what sits on the joists that holds us up.  The subfloor is in really good shape, except for a couple of square feet around the toilet that needs to be replaced because it was wet and now it is weakened.  Again, I ask around, look on the internet, buy a new saw (yipee!) and figure out how to replace it.  Then we decide on new flooring, figure out how to install it, and get it in.  Then we still have to get the vanity (with new counter top and faucets, thanks honey) back in, washer, dryer, baseboards (which now need varnish) and, finally, the toilet.

All that was because, while trying to wash my hands, I thougth “Huh.  That line on the floor shouldn’t be there.”

That’s how blogging is for me.  I start with one topic, which leads to another, which leads to another.  I started a post on training this morning, which lead to some thoughts on how prepared you REALLY need to bee, which lead to a rant on continuous integration (which, like linoleum, I never spell right) which led to a catchy title for the post, which made me realize I now had at least 3 topics in the same post and I forgot where I was going in the first place.  Sometimes trying to do the simplest thing turns out to be a lot of work.

New Blog! Now what?

April 14th, 2008

After some thought and much procrastination, I’ve decided to update the web site, start a blog, fill in my wiki, and update the PayPal book. I’ve also made a resolution to blog for 20 minutes a day every day this week.

Judging from past experience with resolutions, I suspect this will be my only post in 2008. Hopefully, I’ll get around to starting a list of resolutions to make, so I’ll remember to post again in January, 2009.

There are lots of things to write about. I’m learning some advanced Hibernate debugging techniques, getting interested in Groovy, looking for time to update the PayPal book, thinking about starting another book (or two) or web site (or two), and holding down a job. So you’ll forgive me if I don’t actually post again this year. But hopefully I’ll be adding content somewhere more useful.

So, welcome to the new site. If things aren’t working let me know.