weewar.com corner

Command history meme

Posted on April 18th, 2008 by Duff OMelia

Why not? Everyone else seems to be.

history | awk '{a[$2]++}END{for(i in a){print a[i] " " i}}' | sort -rn | head
125 git
66 gs
64 rake
56 cd
45 cap
33 ls
18 g
16 ./script/server
9 mv
8 svn

g is an alias for git. gs is an alias for git status. I like Chu’s alias of ss for script/server. I think I’ll do that.

cap deploy:pending

Posted on April 16th, 2008 by Duff OMelia

Running `cap deploy:pending` displays the commits since your last deploy. I wanted it’s output to be a bit different primarily so I could send a message to the others on the team about what’s just been deployed.

Specifically, I wanted the output to look something like this:

Deployment revision 49c45b7
I deployed the latest. It includes:

* Added avatar support  (Duff)
* Cleaned up the project switcher  (Alex)

To do that, I added the following capistrano recipe:

namespace :deploy do
  namespace :pending do
    desc <<-DESC
      Show the commits since the last deploy
    DESC
    task :default, :except => { :no_release => true } do
      deployed_already = current_revision
      to_be_deployed = `git rev-parse --short "HEAD"`

      puts "\n\nDeployment revision #{to_be_deployed}"
      puts "I deployed the latest. It includes:"
      puts
      system(%Q{git log --no-merges --pretty=format:"* %s %b (%cn)" #{deployed_already}.. | replace '<unknown>' ''})
      puts "\n\n\n"
    end

  end
end

Then I added a hook in my deploy.rb to run this automatically whenever someone deploys:

before "deploy:update_code", "deploy:pending:default" 

The next piece of automation I’d like is to have it automatically post a message to basecamp.

Rails plugin - svn_messages (updated)

Posted on January 8th, 2008 by Duff OMelia

I created a rails plugin to help me keep clients up to date on the status of projects. This post is an update to the original svn_messages plugin.

The plugin now uses capistrano 2 (Multistage). I use it in 2 instances – whenever I deploy a site, and whenever I send a weekly status report. For a deployment, this is what I type at the command line:

cap staging svn:deployment_report

This gives me the following output:

Deployment to staging (Revision 34)
I deployed the latest.  It includes:

* Started adding user registration system. (domelia)
* Now translating validation errors and their attribute names. (bjones)
* Reorganized views a bit. (domelia)
* Now sending activation email when user signs up. (fsmith)
* Added support for "remember me" when logging in. (domelia)
I could have also typed:
cap production svn:deployment_report

For a weekly status report, I type the following:

cap svn:messages r=70 u=domelia

70 is the oldest subversion revision I care about. domelia is me. I specify this because I only care about the svn commits that I personally made. This gives me the following output:

* Started adding user registration system.
* Reorganized views a bit. 
* Added support for "remember me" when logging in.

I like the plugin because I can keep clients up to date and spend very little time doing it.

To install:

svn export https://terralien.devguard.com/svn/projects/plugins/svn_messages vendor/plugins/svn_messages
If you’d like this deployment report to happen every time you deploy, you can add the folllowing to your deploy.rb:
before  "deploy:update_code", "svn:deployment_report" 

Rails Plugin - svn_messages

Posted on August 22nd, 2007 by Duff OMelia

UPDATE: This plugin has been updated.

I created a rails plugin to help me keep clients up to date on the status of projects. I use it in 2 instances – whenever I deploy a site, and whenever I send a weekly status report. For a deployment this is what I type at the command line:

rake svn:messages r=70

70 is the oldest subversion revision I care about. This gives me the following output:

* Started adding user registration system. (domelia)
* Now translating validation errors and their attribute names. (domelia)
* Reorganized views a bit. (domelia)
* Now sending activation email when user signs up. (domelia)
* Added support for "remember me" when logging in. (domelia)

For my deployment report, I typically remove the svn messages that aren’t immediately visible in the site or that don’t matter too much to the client. So, I’d remove “reorganized views a bit”.

For a weekly status report, I only care about the svn commits that I personally made. In that case, I’d say something like:

rake svn:messages r=32 u=domelia

This would give me similar output, except it will only show me the commits made by the domelia user.

I like the plugin because I can keep clients up to date and spend very little time doing it.

To install:

svn export https://terralien.devguard.com/svn/projects/plugins/svn_messages vendor/plugins/svn_messages

Custom Mephisto

Posted on June 15th, 2007 by Duff OMelia

I’ve really been enjoying Mephisto, the blogging software that’s running this site.

I wanted to create another blog for some reviews I’ve written. So I started with the standard Mephisto and chose a theme from a Mephisto theme gallery and things were looking pretty nice. Then I wanted to start making some customizations. I considered using the plugin system that ships with Mephisto but I thought I’d just opt for a completely custom system and see if it would be difficult to have complete flexibility.

It turns out that it was super cinchy to do. Mephisto is open source, and it’s a well-written rails app. The technology world sure is changing in some fascinating ways. When you put some things together like ruby, rails, open source, the mac, quicksilver, and textmate, things start to get pretty exciting for developers.

I’ve made a few customizations so far and I’m not sure if I’ll ever want to customize it further. For now though, it’s much easier to mark a review as thumbs up or thumbs down and it’s easy to specify an amazon url for a review and know that it’ll show up with the correct amazon associates url.

One of the many amazing things about Rails is that you can so easily sit down and completely modify a Rails app you’ve never seen before, primarily because the framework makes it painful to step outside convention. I couldn’t even imagine doing something like this in some of the other web frameworks out there.

Here’s what Revnut looks like so far.

RJS for local javascript

Posted on May 8th, 2007 by Duff OMelia

There are times when I need to write a local javascript function (without needing a round trip to the server) and I’d like to use RJS rather than writing the function in pure javascript. Why would I want to use RJS?

  • The syntax is clean and it’s often less code than pure javscript.
  • When writing this function, I can mix in powerful Ruby constructs.
  • It’s consistent with the other RJS in the project.
  • I automatically get the function wrapped up in a CDATA tag.
  • I automatically get exceptions handled by giving me an alert of the problem.
  • It’s easy to conditionally exclude some of the javascript.
  • My rails views look cleaner.

Here’s an example from a rails view (new.erb):

<%= define_js_function(:my_cool_local_function) do | page | 
    page[:choice_entry_area].hide unless @question.multiple_choice?
    page[:add_answer_link].show
    page[:add_answer_button].hide
  end  
%> 

So where’d this define_js_function method come from? I added it to ApplicationHelper. Here it is:

  def define_js_function(function_name, &block)
    parens = function_name.kind_of?(Symbol) ? "()" : ""
    update_page_tag do | page |
      page << "function #{function_name}#{parens} {"
      yield page
      page << "}"
    end  
  end
When the view is rendered in the browser, the following javascript gets generated:
<script type="text/javascript">
//<![CDATA[
try {
function my_cool_local_function() {
$("choice_entry_area").hide();
$("add_answer_link").show();
$("add_answer_button").hide();
}
} catch (e) { alert('RJS error:\n\n' + e.toString()); alert('function my_cool_local_function() {\n$(\"choice_entry_area\").hide();\n$(\"add_answer_link\").show();\n$(\"add_answer_button\").hide();\n}'); throw e }
//]]>
</script>

If the javascript function you’re creating takes no arguments, then you simply pass a symbol to define_js_function to specify the function name. If your function takes arguments, then you can pass in a string which contains those arguments like so:

<%= define_js_function("add_tag(tag)") do | page | 
    page << "$('tag_list_str').value += tag"
  end  
%>

TUAW category feeds

Posted on May 4th, 2007 by Duff OMelia

I’ve been subscribed to the Unofficial Apple Weblog for quite awhile. It’s in my NetNewsWire folder entitled “Can’t Keep Up” (the place containing feeds that have so much content that I’ll never be able to read them all.)

I’m probably the last person in the Mac universe to realize that there are category specific feeds available so that you can subscribe to a subset of TUAW. This is outstanding because there are certainly parts of TUAW that are quite interesting to me.

I started out by subscribing to the following categories: Mac 101, Hacks, Tips and Tricks, Cool Tools, Terminal Tips, How To’s, and Productivity.

Rails Plugin - cadillac_edge_deploy

Posted on May 3rd, 2007 by Duff OMelia

Is your deployed rails application frozen to a particular revision? If so, when a new version of rails is released how easy is it for you to upgrade?

Are you living on the edge of rails? If so, how do you move to a more recent revision when you notice an enhancement you want to take advantage of?

Switching to a different version of rails should be easy.

Mike Clark described a number of ways to manage your rails versions in this blog post.

Mike wrote of a “Cadillac Edge Deploy Approach” in referring to Rick Olson’s code that makes managing rails versions quite cinchy.

This plugin is basically a rip-off of that blog post and Rick’s code.

I refactored things a bit primarily because I needed the approach to work even if Rails wasn’t installed on the server I was deploying to. I’ve also changed it since to work with git rather than svn.

Example Usage

To use the plugin, all you need to do is add the following to your deploy.rb:

set :rails_revision, '745359a49452da34978724144eaa318b8a363e08'

When you’d like the server to start using a different revision of rails, just adjust the rails_revision variable.

To install the plugin:

./script/plugin install git://github.com/duff/cadillac_edge_deploy.git

TextMate ternary operator command

Posted on March 15th, 2007 by Duff OMelia

Ever have a simple conditional that you’d like to quickly switch to ternary operator syntax? For example, you have a simple piece of code like this:

if x > y
  result = "Fred"
else
  result = "Joe"
end

and you’d rather it look like this:

result = (x > y) ? "Fred" : "Joe"

Or, you’ve got this:

if x > y
  "Fred"
else
  "Joe"
end

And you’d like this:

(x > y) ? "Fred" : "Joe"

I wrote a little TextMate command to help. It allows you to select the 5 lines of the conditional and convert it to ternary operator syntax by pressing a keyboard shortcut. Here’s what it looks like:

#!/usr/bin/env ruby

original = STDIN.read
result = original
begin
  lines = original.split("\n")
  if lines.size == 5
    space, conditional = /(\s*)if\s(.*)$/.match(lines.first)[1..2]
    if_parts = lines[1].split("=")
    if if_parts.size == 2
      variable = if_parts.first.strip
      if_result = if_parts[1].strip
      else_result = /.*?=(.*)/.match(lines[3])[1].strip
      result = "#{space}#{variable} = (#{conditional}) ? #{if_result} : #{else_result}"
    elsif if_parts.size == 1
      result = "#{space}(#{conditional}) ? #{lines[1].strip} : #{lines[3].strip}"
    end
  end

rescue Exception => e
ensure 
  print result
end  
I set the properties of the command to the following:
  • Save : Nothing
  • Input : Selected Text or Nothing
  • Output : Replace Selected Text
  • Activation : Key Equivalent (Specify whatever Key Equivalent you’d like)
  • Scope Selector : source.ruby

Prettied up test output using assert_arrays_equal

Posted on March 1st, 2007 by Duff OMelia

I’ve got a pretty simple test:

def test_relationships
  wellspring = churches(:wellspring)
  cleaning_crew = Group.create!(:name => "Cleaning Crew", :church => wellspring)
  assert_equal([cleaning_crew], wellspring.groups)
end

The problem is that when this test fails, I get a ridiculous amount of output and it’s difficult to quickly determine why the test is failing. Here’s the output I get:

1) Failure:
test_relationships:10
<[#<Group:0x3492918
  @attributes=
   {"name"=>"Cleaning Crew",
    "church_id"=>1,
    "id"=>32,
at top level in "created_at"=>Thu Mar 01 15 at line 56
  @church=
at top level in #<Church at line 0
    @attributes=
     {"name"=>"Wellspring Community Church",
      "account_owner_id"=>"1",
      "timezone"=>"Alaska",
      "subdomain"=>"wellspring",
      "id"=>"1",
      "tz_name"=>nil},
    @groups=
at top level in #<Group at line 0
  @errors=
at top level in #<ActiveRecord::Errors at line 0
  @new_record=false>]> expected but was
<[#<Group:0x348c0a4 @attributes={"name"=>"Elders", "church_id"=>"1", "id"=>"1", "created_at"=>"2007-03-01 10:01:58"}>, #<Group:0x348c07c @attributes={"name"=>"Children's Ministry", "church_id"=>"1", "id"=>"4", "created_at"=>"2007-03-01 10:01:58"}>, #<Group:0x348c054 @attributes={"name"=>"Cleaning Crew", "church_id"=>"1", "id"=>"32", "created_at"=>"2007-03-01 15:56:38"}>]>.

1 tests, 1 assertions, 1 failures, 0 errors

This is what I’d like the output to look like:

 1) Failure:
test_relationships(GroupTest)
method assert_arrays_equal in test_unit_extras.rb at line 8
method test_relationships in group_test.rb at line 11
<"Cleaning Crew"> expected but was
<"Elders, Children's Ministry, Cleaning Crew">.

1 tests, 1 assertions, 1 failures, 0 errors

To me, that’s a bit easier to read. Here’s how I did it. The test has a small change. Instead of calling assert_equal, it calls assert_arrays_equal. So the test now looks like this:

def test_relationships
  wellspring = churches(:wellspring)
  cleaning_crew = Group.create!(:name => "Cleaning Crew", :church => wellspring)
  assert_arrays_equal([cleaning_crew], wellspring.groups, :name)
end

Where did assert_arrays_equal come from? You can get it by installing the plugin I just created:

./script/plugin install git://github.com/duff/assert_arrays_equal.git

assert_arrays_equal allows you to specify which method you’d like to be called on the objects in your arrays for the output message. It still asserts that the arrays are actually equal. The failure error message now uses the method you specify. In the test above, I specified the :name method because I want Group.#name to be called rather than Group#inspect. If you don’t specify a method, it defaults to :to_s.

Viewing a page in a browser

Posted on February 27th, 2007 by Duff OMelia

I’ve been doing a bunch of integration testing of a rails site lately. I have found it quite useful to be able to quickly view the current page in a browser.

I added the following method to my integration testing DSL:

module IntegrationDsl

  def view
    filename = File.dirname(__FILE__) + "/integration/.integration_test_output_for_browser.html"
    File.open(filename, "w+") { | file | file.write(response.body) }
    `open #{filename}`
  end

end

It simply writes out the response body to a file and then opens it (using the `open` on a mac).

So now in my integration test, I can view the current page in a browser like this:

def test_feature
  s = new_session_as :duff
  s.view
end

Clicking a link

Posted on February 27th, 2007 by Duff OMelia

There have been times when integration testing a rails site that I’ve wanted to click a link on a page without caring about it’s route or url.

So let’s say the following exists on a page in my app:

<html>
  <a href="http://meresheep.com/users/4;resend_activation_email">
      resend your activation email
  </a>
</html>

In my integration test, I can now do things like this:

def test_non_activated_user
  s = new_session_as :duff
  s.click_link("resend your activation email")
  s.assert_template("invitation_sent")
end
To get this functionality, I added the following method to my integration testing DSL:
module IntegrationDsl

  def click_link(link_text)
    assert_select("a", link_text, "Trying to click a link named '#{link_text}' that did not exist") do | links |
      get_via_redirect links.first.attributes['href']   
    end
  end

end

The test fails if the link doesn’t exist on the current page.

TDD - Rails integration style

Posted on February 27th, 2007 by Duff OMelia

I’ve been writing unit tests for many years. I can’t live without them. Writing code from a test-driven perspective became second nature for me a long time ago.

I’ve played with Rails integration tests on a few different projects now and each time I did, I found that there was a slight learning curve for using them. I also found that the tests were often larger than the code I was actually testing. The tests seemed brittle and they seemed to take such a long time to write.

Recently I decided that I just needed to keep writing them for awhile to see if I could get good enough at them such that they were no longer a burden to write. A conversation with Robert certainly played a part in this decision. Since I was working on a personal project this past week, it was a perfect opportunity.

What I’ve found is this. Rails integration tests are amazing. They were taking such a long time to write because I was new at it. They were brittle because I didn’t know what I was doing. The tests were long because I was testing things more than once and I wasn’t refactoring to a Domain Specific Language (DSL) like Jamis wrote about.

The result is that my integration tests are now slim. They’re cinchy to write and they’re getting easier as the DSL for the project improves. I’ve gotten good enough at it that I can now write the integration test before implementing the feature. They’re not taking very long to write at all. It feels amazing knowing that the regression tests are accumulating and it’s becoming harder and harder to break the site I’m working on.

I realize now that the unit testing I did when starting out wasn’t test-driven at all. I wrote unit tests after I wrote the code I was testing. After developing some competence, I was able to switch over to test-driven unit tests. A similar process has now happened with integration tests.

The 2 or 3 days it took to get up to speed was quite worth it.