I recently needed to iterate over all the ActiveRecord models in the rake factory:generate task for factories-and-workers.

I originally tried to iterate over Object.constants (like John Philip Green’s find_invalid rake task) but that seemed a bit clunky to me, and didn’t really work for my purposes.

Here’s how I did it:

  all_models = Dir.glob( File.join( RAILS_ROOT, 'app', 'models', '*.rb') ).map{|path| path[/.+\/(.+).rb/,1] }
  AR_models = all_model_names.select{|m| m.classify.constantize < ActiveRecord::Base}

ruby Date extensions

I recently cooked these up, thought someone else might find them handy. Now you can say date.first_of_month, date.first_of_year, etc.


module DateExtensions
  def first_of_week
    self - self.wday
  end

  def first_of_month
    self - self.day+1
  end

  def first_of_quarter
    (self << (self.month%3 - 1 )%3) - self.day+1
  end

  def first_of_year
    (self << self.month-1) - self.day+1  
  end
end
Date.send :include, DateExtensions

Addendum: oops, looks like there are already similar methods in Rails’ ActiveSupport:

beginning_of_* rather than first_of_*

However, begining_of_week assumes Monday is the first day of the week, while my version assumes Sunday is the first day.

Here are my tests:

require File.dirname(__FILE__) + '/../test_helper'

class DateExtensionsTest < Test::Rails::TestCase

  def test_first_of_week
    day = Date.new( 2008, 1, 1 )
    w   = day.first_of_week
    # sunday is first day of week
    assert_equal 0, w.wday
    assert_equal 30, w.day
  end

  def test_first_of_month
    day = Date.new( 2008, 1, 15 )
    assert_equal 1, day.first_of_month.day
  end

  def test_first_of_year
    day = Date.new( 2008, 5, 27 )
    y   = day.first_of_year
    assert_equal 1, y.month
    assert_equal 1, y.day
  end

  def test_first_of_quarter_q1    
    [1,2,3].each do |month|
      day = Date.new( 2008, month, 13 )
      q   = day.first_of_quarter
      assert_equal 1, q.day
      assert_equal 1, q.month
    end
  end

  def test_first_of_quarter_q2
    [4,5,6].each do |month|
      day = Date.new( 2008, month, 13 )
      q   = day.first_of_quarter
      assert_equal 1, q.day
      assert_equal 4, q.month
    end
  end

  def test_first_of_quarter_q3
    [7,8,9].each do |month|
      day = Date.new( 2008, month, 13 )
      q   = day.first_of_quarter
      assert_equal 1, q.day
      assert_equal 7, q.month
    end
  end

  def test_first_of_quarter_q4
    [10,11,12].each do |month|
      day = Date.new( 2008, month, 13 )
      q   = day.first_of_quarter
      assert_equal 1, q.day
      assert_equal 10, q.month
    end    
  end

end

did you know gsub can be passed a block to operate on matches? I just learned it from this blog post.

I was looking for a way to backquote parentheses and spaces… here’s how I did it:

>> puts "this (is a) test".gsub( /[\s\(\)]/ ){ |c| "\\#{c}" }
this\ \(is\ a\)\ test
=> nil

Handy Mac apps and tips/tricks

Here is a list of a few handy things that I use on my Mac. Maybe you already know about them, or maybe you will pick up a new thing or two.

apple-shift-G

The G is for “go”.

Use this key command in finder, it pops up a textbox where you can type in the path directly. Very handy, and has tab-autocompletion! It also works in native file browser dialogs.

contextual menu plugin to make symbolic links

This baby really comes in handy on occasion. You can make symbolic links and rename them just like any other file. It’s much better than finder aliases.

download make symbolic link CMplugin

There are some other handy CMplugins here, but they don’t work on intel macs yet :(

option-click the airport statusbar icon to show networks sorted by signal strength

This one isn’t mind-blowing, but can be handy. Even better is the Airport Radar dashboard widget, which tells you which networks are open/closed as well. There is also coconut wifi, and for the true h4×0r, KisMAC.

Unsanity FruitMenu

This is a commercial app, but it is totally worth it. It gives you a customizable Apple menu, where I can put links to Applications folder and home directory, as well as easy access to system preferences, and anything else your heart desires. It was a little flakey with Intel macs, but seems to be working okay now.

all about FruitMenu

Custom Keybindings

Did you know that every cocoa app has configurable keybindings? You may have noticed already that some common emacs keybindings are available by default, I use kill and yank all the time (ctrl-K and ctrl-Y, it’s sorta like a mini clipboard).

http://www.hcs.harvard.edu/~jrus/Site/Cocoa%20Text%20System.html http://www.lsmason.com/articles/macosxkeybindings.html

Another handy app in this vein is Unsanity Menu Master, which let’s you customize the keyboard shortcuts for GUI menu commands, on a per-app basis. Very cool!

It was also flakey with intel macs, but supposedly this has been resolved. Though I’m a little cautious still, after it caused me hours of angst from photoshop crashes.

apple-option-I in finder

This gives you a global file info window, it stays open and when you select different files, it updates correspondingly. Again, not mind-blowing, but handy once and awhile when you want to inspect multiple files.

preview multiple images

On the topic of inspecting multiple files, if you select several images and drag them into Preview, you’ll have a nice sidebar to select between them.

Just for Fun: echo meow | cat -a

Shane at gnufoo.org cleverly patched the cat utility to bind it with the Mac OS X speech synthesizer. :)

I just compiled a universal binary. It’s probably not a good idea to replace the one in /bin/, so put it in ~/bin and change your path accordingly, or name it something different like cat_talk.

Here is an example of cool stuff you can do with it to amuse your friends:
alias say_uptime “uptime | cut -d’ ’ -f3-7 | cat -a”
(bash users need an equals sign after say_uptime)

(I forget where I got this from originally, but I seem to remember the speech synthesizer behaving differently a few years back… like it said hours and minutes. I also used to be able to type gibberish and it made cool sounds instead of naming the letters)

Download the zip file containing the univeral binary and man page here.

A handy, detachable widget for time-tracking

There is a timer widget that I find handy for time-tracking, called ChronoTrack. (UPDATE: here is the latest 2.0 version: http://www.nimblepoint.com/widgets/chronoTrack_v2.0.zip) I like to keep it tucked away in the bottom corner of my desktop. Here’s how I do it…

If you activate dashboard development mode, then you will be able to drag widgets on and off of the dashboard, to and from the desktop. (The widget will always float on top.)

Type this in your terminal to activate dashboard development mode:
defaults write com.apple.dashboard devmode YES

It’s a little tricky to get the hang of at first, but just practice and you’ll get the hang of it:

Click and drag a widget on your dashboard, then de-activate the dashboard by pressing F12. (I use SideTrack to use a mouse-pad corner tap “hotkey”) Keep dragging the mouse and place the widget wherever you would like.

To reattach the widget back to the dashboard, just do the reverse: click and drag the widget on your desktop, then activate the dashboard and place the widget where you want it on the dashboard layer.

BTW People claim you can do this without activating devmode, simply by dragging widgets directly from the “widget shelf” to create a new one, and then hitting F12. I found the behaviour was slightly different, as the widget then came back to the dashboard when you press F12 again, while with the devmode method, the detached widgets are visible but darkened and unmodifiable via the dashboard layer, because they are actually behind the transparent background layer of the dashboard.

Enjoy, and feel free to let me know about other useful widgets for development.

Set SVN to ignore certain files or patterns

SVN is a great tool, and having it work the way you want is even better. I don't want SVN to pay attention to things like my Rails log files and other artifacts.

There is a simple way to have SVN behave, and have it applied globally to all of your SVN working copies. Just edit the SVN config file located at ~/.subversion/config. Find the "global-ignores" part, and edit it as shown below:

global-ignores = *.o *.lo *.la #*# .*.rej *.rej .*~ *~ .#* .DS_Store .project *.log database.yml ruby_sess* schema.rb

The default file patterns file patterns are common file artifacts that you would want to avoid like compilation products, backup files, and the like. The patterns .project *.log database.yml ruby_sess* schema.rb ignore any RadRails/Eclipse project files, log files (Rails log files), and Rails database configuration files, ruby session state files, and Rails database schema dump files, respectively.

Since you can use pattern matching, you can get pretty creative with how you ignore files. Or, you can always just individual files without using patterns at all. Once you save this file, your changes instantly effective.

actionmailer ActionView ActiveRecord activesupport agile ajax alphadecimal audio autotest BDD blocks capistrano ssh ruby console controller css dashboard widget delegate dog puppy naming name DRM email obfuscation exceptions factories factories-and-workers factory pattern filemerge find and replace finder fink fixtures fun functional testing gem git google maps haml helper helpers imagemagick Intertrust irb javascript jquery jrails logo macro math meetup model openssl OS X patch Pioneer Electronics plugin polymorphism project management prototype.js rails rails gotcha rails,patch,validations railsconf rake rmagick RSA encryption ruby ruby on rails script scrum scrum lessons rails scrumninja shoulda subversion SyncTV TDD testing textmate tips tricks unique hashes unix shell validation view yaml zebra stripes