Posted by admin Fri, 08 Jan 2010 04:29:00 GMT

So Rails Templates have been around now since 2.3.0.  I have finally found the time to dig in and enjoy them, after watching Ryan Bates’ screencast episode 148.

Feel free to use my base_template or any of my rails generators which can be found at github.com/cblackburn.  My template is pretty basic, but I’ve wanted a way to do this kind of thing for years.  I used to write shell scripts to setup my default Rails applications. This is so much nicer!

To use it just do this:

rails appname -m http://github.com/cblackburn/rails-templates/raw/master/base_template.rb

  # Plugins
  plugin 'exception_notifier', :git => 'git://github.com/rails/exception_notification.git'

  # Gems
  if yes?("Will you need to paginate?")
    gem "mislav-will_paginate", :lib => 'will_paginate'
  end
  rake("gems:install", :sudo => true)

  # Generators
  generate :app_layout
  if yes?("Will you be using MySQL?")
    db_password = ask("What is the MySQL password for root?")
    puts "Type 'y' and Enter to overwrite the current database.yml file."
    generate( "database_yml_mysql #{db_password}") # This uses my own generator
  end
  if yes?("Do you need Authlogic?")
    generate :authlogic # This uses my authlogic-generator
  end
  generate :controller, "home index"
  route "map.root :controller => :home"

  # General cleanup
  rake "db:create:all"
  rake "db:migrate"
  run "echo TODO > README"
  run "rm -f public/index.html"

  # Files
  file ".gitignore", <<-END
  .DS_Store
  log/*.log
  log/*.pid
  tmp/**/*
  config/database.yml
  db/*.sqlite3
  END

  # Git
  git :init
  git :add => "."
  git :commit => "-m 'initial commit'"

If you decide to use this you’ll also want to take a look at my generators: http://github.com/cblackburn/personal-rails-generators

Posted by admin Sun, 06 Dec 2009 07:05:00 GMT

While working on a project I found it necessary to generate some custom Google map markers and I wanted them numbered 1..999. I searched for a while and found a few php scripts and some snippets here and there, none of which did quite what I wanted. I decided to write my own. Here it is for your own mapping pleasure:

I saved mine as

gen_google_markers.rb
. This is quick and dirty but it does what I wanted. If you use it and make it better please send me a patch or just a note to let me know what you did so I can improve my version as well.

#!/usr/bin/env ruby

require 'rubygems'
require 'rmagick'
include Magick

WIDTH = 33
HEIGHT = 37
FONT_COLOR = "#000000"
MARKER_COLOR = "#CC0000"

def generate_marker(num, font_color, marker_color)
  throw "Number too big [#{num}], can only be 3 digits maximum." if num > 999
  canvas = Image.new(WIDTH, HEIGHT) {
    self.background_color = 'transparent'
    # self.background_color = 'white'
  }
  gc = Draw.new
  gc.fill_opacity(100)
  gc.fill(marker_color)
  gc.stroke('none')
  gc.stroke_width(1)

  # Draw triangle
  gc.path("M#{WIDTH*0.091},#{HEIGHT*0.433} L#{WIDTH/2},#{HEIGHT*0.966} L#{WIDTH*0.909},#{HEIGHT*0.433} z")

  # Draw ellipse on top of triangle
  gc.stroke(marker_color) # set stroke to fix the offset where elipse meets triangle
  gc.ellipse(WIDTH/2, HEIGHT*0.313, WIDTH*0.455, HEIGHT*0.267, 0, 360)

  # Annotate
  pointsize = (WIDTH*1.0/2) - 2
  gc.fill(font_color)
  gc.stroke('none')
  gc.pointsize = pointsize
  gc.gravity = NorthGravity
  gc.text_align(CenterAlign)
  gc.text_antialias(true)
  gc.text(WIDTH/2, (HEIGHT/2)-(HEIGHT/20), "#{num}")

  # Write file
  gc.draw(canvas)
  canvas.write("#{num}.png")
end

marker_color = ARGV.shift
font_color = ARGV.shift

for num in 1..999
  generate_marker(num, font_color, marker_color)
end

Happy mapping…

Posted by admin Thu, 18 Jun 2009 21:03:00 GMT

Running RCOV, and having it complain about a p articular plugin that it should not have been looking at in the first place, made me want to find a way to exclude it. In my usual practical fashion, I hit google up for a quick way to exclude the unwanted plugin.

Here it is re-posted from Dan Mange’s Blog

config.plugins = Rails::Initializer.new(config).send(:find_plugins, config.plugin_paths).map {|path| File.basename(path)}
config.plugins -= %W(plugin_one plugin_two)

UPDATE: JJ Barrett has an update for Rails 2.x… http://www.jjbarrett.net/archives/plugin-ordering-and-exclusion-in-rails-20

  config.plugins = config.plugin_locators.map do |locator|
                     locator.new(Rails::Initializer.new(config)).plugins
                   end.flatten.map{|p| p.name.to_sym}
  config.plugins -= [:do_not_load_plugin_1, :do_not_load_plugin_2]

Posted by admin Fri, 29 May 2009 22:23:00 GMT

‘jaap’ wrote a rake task to convert your Rails application from using Gettext translation into the new I18n support, which is really great!

WARNING: Before you run the new rake task rake gettext_to_i18n:transform, read “So How Do You Fix It?” below

OK, so you’ve followed along in the article and now you want to test out your new localization system. Problem is when you try to start the server up script/server you get this error message:

/Users/cblackburn/Source/ruby/bols/lib/active_support/memoizable.rb:71:in `path': can't modify frozen object (TypeError)

…or something similar.

What happened? Since you have frozen Rails and the rake task does not exclude the frozen vendor/rails directory, it modified memoizable.rb in ActiveSupport causing this error.

So How Do You Fix It?

Do one of the following:

  • Backup lib/active_support/memoizable.rb before you run the rake task, then restore it after.
  • Restore lib/active_support/memoizable.rb using your SCM.

For this project in my case it was

svn revert lib/active_support/memoizable.rb

Posted by admin Wed, 13 May 2009 18:55:00 GMT

Recently I upgraded my blog here and lost permalinks to several articles, as well as the articles themselves. For that I apologize, if you are looking for something unfound. One such article that I referred to often was ‘Scripting Mac Terminal Using Ruby’. Though this is not the original article, here is a script I recently needed. As will all source code published herein, this is hereby released into the public domain with no warranties of any kind.

This humble little script uses rb-appscript, to change the background color, in Terminal, of the current tab you are running. I like to change colors of tabs to mean different things. For instance, where I am tailing log files – dark green… running irb – dark blue, etc.

Maybe, like me, you stay logged into several machines around the world and want to color-code your tabs based on location.

I called mine colorme.rb. The command line expects a color in this format:

  • array: [0,32767,65535] of color values [Red, Green, Blue], 0 to 65535
  • string: ‘red’, ‘green’, ‘black’, etc., only the basic colors work as strings

Have fun and let me know if you are doing something interesting with Terminal using Appscript on Ruby.

#!/usr/bin/env ruby

require 'rubygems'
require 'appscript'
include Appscript

_color = (ARGV.length > 0) ? ARGV[0] : 'black'

begin
  term = app('Terminal')
  current_window = term.windows.first
  tab = current_window.tabs.first
  current_color = tab.background_color.get
  puts "Current Color is: #{current_color.inspect}"
  tab.background_color.set(_color)
rescue Exception => e
  puts "#{e}"
  puts "Usage: colorme.rb array"
  puts "  ...where array is an array of 3 color values like this [0,32767,65535]"
  puts "  ...values can be anywhere between 0 and 65535"
  puts "  ...values, in order, represent 'Red, Green Blue'"
end

Posted by admin Tue, 28 Apr 2009 05:00:00 GMT

Thought I had posted this last year but can’t seem to find it, so here it is again:

#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../../config/boot'

# Restart Applicable Passenger Instance
filename = "#{RAILS_ROOT}/tmp/restart.txt"
File.open(filename, 'w') {|f| f.write('restart passenger') }

Save this file as: ./scripts/process/reaper, then deploy and it will restart your passenger instance.

Posted by admin Mon, 24 Nov 2008 21:38:00 GMT

UPDATE: As cremes pointed out below, these timings obviously only reflect the version of Ruby I was running at the time. (ruby 1.8.6 (2008-08-11 patchlevel 287) [universal-darwin9.0]). When using any other version of the interpreter, all bets are off.

Another interesting set of timings are the differences between the branching control statements case-when and if-else. Here is a quick example:

require 'profilings'
include PeepcodeProfiler

###
# Case-when vs. If-Elsif-Else
###

time_this("case-when using strings") {
  x = "10"
  case x
  when "1"
    puts "it is #{x}"
  when "2"
    puts "it is #{x}"
  when "3"
    puts "it is #{x}"
  when "4"
    puts "it is #{x}"
  when "5"
    puts "it is #{x}"
  when "6"
    puts "it is #{x}"
  when "7"
    puts "it is #{x}"
  when "8"
    puts "it is #{x}"
  when "9"
    puts "it is #{x}"
  when "10"
    puts "it is #{x}"
  end
}
Timings for case when using strings
Thread ID: 218880
Total: 0.000113

 %self     total     self     wait    child    calls  name
 36.28      0.00     0.00     0.00     0.00       10  Kernel#=== (ruby_runtime:0}
 31.86      0.00     0.00     0.00     0.00        2  IO#write (ruby_runtime:0}
 22.12      0.00     0.00     0.00     0.00       10  String#== (ruby_runtime:0}
  9.73      0.00     0.00     0.00     0.00        1  Kernel#puts (ruby_runtime:0}
  0.00      0.00     0.00     0.00     0.00        0  PeepcodeProfiler#time_this (./profilings.rb:8}

…and again the faster if-elsif-else:

time_this("if-elsif-else using strings") {
  x = "10"
  if x == "1"
    puts "it is #{x}"
  elsif x ==  "2"
    puts "it is #{x}"
  elsif x ==  "3"
    puts "it is #{x}"
  elsif x ==  "4"
    puts "it is #{x}"
  elsif x ==  "5"
    puts "it is #{x}"
  elsif x ==  "6"
    puts "it is #{x}"
  elsif x ==  "7"
    puts "it is #{x}"
  elsif x ==  "8"
    puts "it is #{x}"
  elsif x ==  "9"
    puts "it is #{x}"
  elsif x ==  "10"
    puts "it is #{x}"
  else
    puts "it is #{x}"
  end
}
Timings for if elsif else using strings
Thread ID: 218880
Total: 0.000049

 %self     total     self     wait    child    calls  name
 46.94      0.00     0.00     0.00     0.00       10  String#== (ruby_runtime:0}
 30.61      0.00     0.00     0.00     0.00        2  IO#write (ruby_runtime:0}
 22.45      0.00     0.00     0.00     0.00        1  Kernel#puts (ruby_runtime:0}
  0.00      0.00     0.00     0.00     0.00        0  PeepcodeProfiler#time_this (./profilings.rb:8}

OK, obviously the case-when version is slower because it does a compare for each value using the === Kernel operator, which in turn calls the correct == operator for the value type. Whereas the if-elseif-else version doesn’t incur the overhead of the Kernel triple-equal operator for each comparison.

So I wonder if the same thing happens with a different object type:

time_this("case when using integers") {
  x = 10
  case x
  when 1
    puts "it is #{x}"
  when 2
    puts "it is #{x}"
  when 3
    puts "it is #{x}"
  when 4
    puts "it is #{x}"
  when 5
    puts "it is #{x}"
  when 6
    puts "it is #{x}"
  when 7
    puts "it is #{x}"
  when 8
    puts "it is #{x}"
  when 9
    puts "it is #{x}"
  when 10
    puts "it is #{x}"
  end
}
Timings for case when using integers
Thread ID: 218880
Total: 0.000104

 %self     total     self     wait    child    calls  name
 46.15      0.00     0.00     0.00     0.00       10  Kernel#=== (ruby_runtime:0}
 19.23      0.00     0.00     0.00     0.00        9  Fixnum#== (ruby_runtime:0}
 15.38      0.00     0.00     0.00     0.00        2  IO#write (ruby_runtime:0}
 15.38      0.00     0.00     0.00     0.00        1  Kernel#puts (ruby_runtime:0}
  3.85      0.00     0.00     0.00     0.00        1  Fixnum#to_s (ruby_runtime:0}
  0.00      0.00     0.00     0.00     0.00        0  PeepcodeProfiler#time_this (./profilings.rb:8}

Hmmm, bad news. Here we see the Kernel === operator in the call stack again.

time_this("if elsif else using integers") {
  x = 10
  if x == 1
    puts "it is #{x}"
  elsif x ==  2
    puts "it is #{x}"
  elsif x ==  3
    puts "it is #{x}"
  elsif x ==  4
    puts "it is #{x}"
  elsif x ==  5
    puts "it is #{x}"
  elsif x ==  6
    puts "it is #{x}"
  elsif x ==  7
    puts "it is #{x}"
  elsif x ==  8
    puts "it is #{x}"
  elsif x ==  9
    puts "it is #{x}"
  elsif x ==  10
    puts "it is #{x}"
  else
    puts "it is #{x}"
  end
}
Timings for if elsif else using integers
Thread ID: 218880
Total: 0.000057

 %self     total     self     wait    child    calls  name
 50.88      0.00     0.00     0.00     0.00       10  Fixnum#== (ruby_runtime:0}
 26.32      0.00     0.00     0.00     0.00        2  IO#write (ruby_runtime:0}
 15.79      0.00     0.00     0.00     0.00        1  Kernel#puts (ruby_runtime:0}
  7.02      0.00     0.00     0.00     0.00        1  Fixnum#to_s (ruby_runtime:0}
  0.00      0.00     0.00     0.00     0.00        0  PeepcodeProfiler#time_this (./profilings.rb:8}

Interesting. Even for a different object type, the case statement incurs the overhead of the Kernel === operator.

Now I’m not saying to always avoid the case statement. It is really a judgement call. I typically prefer cleaner code over slight optimization benefits. However, in this case (no pun intended) if-elsif-else is consistently about twice as fast as the case-when statement. You be the judge for your own particular situation.

Take a look here for the Peepcode profiler that we use when doing our metrics. If you come up with interesting metrics please let us know. We are happy to publish them here, giving you the credit, or link to them from here.

Happy Hacking!!!

UPDATE: Peter Wagenet brought up a good point, noting that our metrics did not completely determine if the slower case statement is indeed slower merely because it uses === operator, or if there is more overhead involved with case. So here is our test…

time_this("if elsif else using integers with ===") {
  x = 10
  if x === 1
    puts "it is #{x}"
  elsif x ===  2
    puts "it is #{x}"
  elsif x ===  3
    puts "it is #{x}"
  elsif x ===  4
    puts "it is #{x}"
  elsif x ===  5
    puts "it is #{x}"
  elsif x ===  6
    puts "it is #{x}"
  elsif x ===  7
    puts "it is #{x}"
  elsif x ===  8
    puts "it is #{x}"
  elsif x ===  9
    puts "it is #{x}"
  elsif x ===  10
    puts "it is #{x}"
  else
    puts "it is #{x}"
  end
}
Timings for if elsif else using integers with ===
Thread ID: 218880
Total: 0.000081

 %self     total     self     wait    child    calls  name
 50.62      0.00     0.00     0.00     0.00       10  Kernel#=== (ruby_runtime:0}
 23.46      0.00     0.00     0.00     0.00        9  Fixnum#== (ruby_runtime:0}
 11.11      0.00     0.00     0.00     0.00        1  Kernel#puts (ruby_runtime:0}
 11.11      0.00     0.00     0.00     0.00        2  IO#write (ruby_runtime:0}
  3.70      0.00     0.00     0.00     0.00        1  Fixnum#to_s (ruby_runtime:0}
  0.00      0.00     0.00     0.00     0.00        0  PeepcodeProfiler#time_this (./profilings.rb:8}

After running the test several times I have determined that using the === operator is obviously, and consistently slower than == alone. However, the case statement is consistently slower still, than if-elsif using the === operator. Therefore case does incur overhead beyond the simple use of === by about 20%.

Posted by admin Mon, 24 Nov 2008 06:00:00 GMT

My friend and colleague Peter Wagenet has written an “excellent article”:http://in.finitu.de/2008/11/22/pragmatic-programming about the true meaning of “pragmatic programming”, and how to achieve it. Check it out!

Posted by admin Tue, 18 Nov 2008 06:00:00 GMT

A few people have asked me for the module I’ve been using to profile my code in my “Double Quotes vs. Single Quotes”:/2008/06/10/ruby-performance-use-double-quotes-vs-single-quotes article. It is merely a wrapper around “RubyProf”:http://ruby-prof.rubyforge.org/, dead simple and nothing worth commenting on, but since some keep asking – here it is:

require 'rubygems'
require 'ruby-prof'

module PeepcodeProfiler
  def time_this(comment, &block)
    RubyProf.measure_mode = RubyProf::PROCESS_TIME
    RubyProf.start
    yield
    result = RubyProf.stop
    puts "\nTimings for #{comment}"
    printer = RubyProf::FlatPrinter.new(result)
    printer.print(STDOUT, 0)
  end
end

I wrote this for some work on a “Scaling Rails” minibook for “Peepcode”:http://peepcode.com/, which I seem to be unable to finish. I hope Geoffrey forgives me.

Posted by admin Thu, 06 Nov 2008 06:00:00 GMT

Between the time your migration files get generated and deploying to production with your new application you will likely need to add indexes to one or more of your tables. How do you know which tables need indexes and which fields in those tables to index?

Here are some tips to help figure out where the likely bottlenecks will occur with regard to your database tables:

  1. Always index @*_id@ foreign_key fields. Let’s say you have 2 tables, @users@ and @addresses@. You may have migration file code in @./db/migrate@ that looks like this:
  ...
  def self.up
    create_table "users", :force => true do |t|
      t.column :login, :email             :string
    end
  end
  ...
  ...
  def self.up
    create_table "addresses", :force => true do |t|
      t.column :user_id       :integer
      t.column :address       :string
    end
  end
  ...

You will always want to index the foreign_key @user_id@, with few exceptions. So simply add the @add_index@ line like this:

  ...
  def self.up
    create_table "addresses", :force => true do |t|
      t.column :user_id       :integer
      t.column :address       :string
    end
    add_index "addresses", :user_id
  end
  ...

This will facilitate much faster associations when you lookup related models like this view code:

  <%=h user.address.address %>
  
  # OR as the case may be...
  
  <%=h address.user.email %>
  1. As a rule of thumb index any column for which you will be using to lookup a row or set of rows. For example, if you will be doing something like this:
  u = User.find_by_email('test@example.com')

… then you will want to index the email field.

  1. Run explain plans on your associations to determine where indexes would benefit the queries. Now this one is more tricky unless you know about the query_analyzer plugin. This nifty little tool will automatically dump out explain plans into your logfiles in development mode that look like this example from the README file:
# development.log

P Load (0.008669)
 => SELECT p.* FROM p INNER JOIN d ON p.id = d.p_id WHERE (d.p_id = 2 AND ((d.type = 'P')))

Analyzing P Load

select_type | key_len | type   | Extra       | id | possible_keys | rows | table  | ref    | key
 ----------------------------------------------------------------------------------------------------
SIMPLE      |         | ALL    | Using where | 1  |               | 74   |    d   |        |
SIMPLE      | 4       | eq_ref | Using where | 1  | PRIMARY       | 1    |    p   | d.p_id | PRIMARY

Now what this tells you is that the table @d@ is missing an index because MySQL is having to look at “ALL” the records. Whenever you see a @type@ of @ALL@ and/or @possible_keys@ is empty it should be a red flag telling you to index something.

If we had followed tips 1 and 2 this explain plan would be much different, showing reference types of @ref@ and @eq_ref@ which is typically a good thing.

For more information see the README file here: http://agilewebdevelopment.com/plugins/query_analyzer.