gemedit version 0.0.1 has been released! 1
Check out my first published gem: gemedit
A utility to view a gem’s source in your favorite editor
Changes:
- 0.0.1 2008-02-27
- 1 major enhancement:
- Initial release
Upgraded Syntax Highlighting for Scribbish Theme 1
We were bored of the plain green on black syntax highlighting for our code blocks, not to mention sick of the weird borders around code comments, so I hunted around for a little to find something a little better. The theme we’re using, Scribbish, didn’t have any CSS defined for Typo’s code blocks, so I stole them from James Wilford’s post. Thanks, James.
Maybe we’ll tweak the colors some more later, but these will do for now.
class Scribbish < TypoTheme
include JamesWilfordCSS
endBloated RailsConf Presentation Downloader 2
I’ve updated my downloader from earlier to include all sorts of fancy options. It no longer requires wget, it just uses open-uri. It can give the files a fancy name. It can be told where to download the files to. It will skip files that won’t download for some reason. It will even butter your toast if you can find the correct command line switch.
It’s about 3 times bigger than the previous one. But maybe you can learn a little more about optparse, hpricot, file handling, and error handling along the way.
Here it is:
#!/usr/bin/env ruby
require 'optparse'
OPTIONS = { :Verbose => false,
:Force => false,
:DownloadDir => '.',
:DescriptiveFilenames => true
}
OptionParser.new do |opts|
opts.banner = "Usage: #{$0} [options]"
opts.on("-v", "--[no-]verbose", "Run verbosely, default #{OPTIONS[:Verbose]}") do |verbose|
OPTIONS[:Verbose] = verbose
end
opts.on("-f", "--[no-]force", "Force downloads, default #{OPTIONS[:Force]}") do |force|
OPTIONS[:Force] = force
end
opts.on("-d", "--[no-]descriptive", "Use long descriptive filenames, default #{OPTIONS[:DescriptiveFilenames]}") do |long|
OPTIONS[:DescriptiveFilenames] = long
end
opts.on("-p", "--path PATH", "Path to download to, default #{OPTIONS[:DownloadDir]}") do |path|
OPTIONS[:DownloadDir] = path
end
opts.on_tail("-h", "--help", "Print help message") do |help|
puts opts
exit
end
end.parse!
require 'rubygems'
require 'hpricot'
require 'open-uri'
require 'fileutils'
BASE_URL = 'http://www.web2expo.com'
def log(str)
puts str if OPTIONS[:Verbose]
end
def download(href, filename)
url = "#{BASE_URL}#{URI.escape(href)}"
download_file = File.join(OPTIONS[:DownloadDir], filename)
if OPTIONS[:Force] || !File.exists?(download_file)
log "downloading #{File.basename(href)}..."
begin
File.open(download_file, 'w') { |f| f.write(open(url).read)}
log "\tsaved as #{download_file}"
rescue Object => e
FileUtils.rm(download_file)
$stderr.puts "ERROR downloading #{url}: #{e.message}"
end
else
log "skipping #{File.basename(href)}... already downloaded as #{download_file}"
end
end
FileUtils.mkdir_p(OPTIONS[:DownloadDir])
h = Hpricot(open("#{BASE_URL}/pub/w/51/presentations.html"))
h.search('div.presentation').each do |presentation_node|
href = presentation_node.at('a[@href^="/presentations/rails2007/"]')[:href]
if OPTIONS[:DescriptiveFilenames]
name = presentation_node.at('b a').inner_text.strip
text = presentation_node.inner_text
speaker = text[/Speaker\(s\):\s+(.*)\s*$/, 1]
date = Date.parse(text[/Presentation Date:\s+(.*)\s*$/, 1])
filename = [speaker, date, name, File.basename(href)].compact.map { |s| s.to_s.strip.gsub(/[^\w\.]/, '_').squeeze('_') }.join('-')
else
File.basename(href)
end
download(href, filename)
endDownload RailsConf 2007 Presentations 5
Updated: Now more bloated!
Run this to get the RailsConf 2007 presentations:#!/usr/bin/env ruby
require 'rubygems'
require 'hpricot'
require 'open-uri'
base = 'http://www.web2expo.com'
h = Hpricot(open("#{base}/pub/w/51/presentations.html"))
h.search('div .presentation > a[@href^="/presentations/rails2007/"]').each do |a|
url = "#{base}#{a[:href]}"
if File.exists?(File.basename(url))
puts "skipping #{url}... already downloaded"
else
puts "downloading #{url}..."
`wget --quiet #{url}`
end
end Will Marcel Molina Steal Matz's Ruby Super Powers 3
I didn’t start watching Heroes until after I heard Rich Kilmer and Marcel Molina talking about it while putting the badges together for RubyConf 2006 in Denver. Had I watched it before maybe I would have been a little scared of Marcel (Sylar), but he was very nice and didn’t seem like a threat to the Ruby world at all.
So, why be scared of Mr. Molina? You be the judge.
class MarcelMolina
include Heroes
def <=>(other_hero)
other_hero.name == 'Sylar' ? 0 : 1
end
end
==
# => true
RubyKagi2007 (日本 Ruby 会議 2007) 1

I would love to go once to Japan to attend the (now) RubyKaigi conference. This year won’t work for me…hey Lee, what about next year?
Find more info here (in Japanese) and here (in Google translated English)
Updated Rake Command Completion
Here’s another update to a previous post.
- A bug was fixed that caused the first task to be omitted from the task list.
- A new feature was added to allow rake not to be the first command on the command line. Now you get completion for both rakes in the following command:
rake db:migrate VERSION=0 && rake db:migrate - Another feature for quicker rake command completion development, <sarcasm>a feature everyone has been waiting for</sarcasm>. This feature just adds the rake completion script itself to the cache dependency list, so the cache will be refreshed while you are playing with the command completion script. This should make tweaking the script much easier in the future.
Rake Command Completion Using Rake 2
This is an update of a previous post.
I just watched Jim Weirich's talk on Rake at the Rails Edge Studio and decided to update the command completion script to use Rake itself for caching the tasks. Err the Blog did an update on my script last month to include caching for a nice speedup, so I took that and made sure the cache is regenerated when the Rakefile or any rake tasks found in your rails app have been updated. I added this with just some straight ruby code, but Jim's talk made me realize that this type of thing is exactly is what rake is for. So, here is the updated version:
#!/usr/bin/env ruby
# Complete rake tasks script for bash
# Save it somewhere and then add
# complete -C path/to/script -o default rake
# to your ~/.bashrc
# Nicholas Seckar <nseckar@gmail.com>
# Saimon Moore <saimon@webtypes.com>
# http://www.webtypes.com/2006/03/31/rake-completion-script-that-handles-namespaces
# http://errtheblog.com/post/33
exit 0 unless File.file?(File.join(Dir.pwd, 'Rakefile'))
require 'rubygems'
require 'rake'
DOTCACHE = File.join(File.expand_path('~'), ".rake_task_cache" , Dir.pwd.hash.to_s)
RAKE_FILES = FileList[ __FILE__, 'Rakefile', 'lib/tasks/**/*.rake', 'vendor/plugins/*/tasks/**/*.rake']
file DOTCACHE => RAKE_FILES do
tasks = `rake --silent --tasks`.split("\n").map { |line| line.split[1] }
require 'fileutils'
dirname = File.dirname(DOTCACHE)
FileUtils.mkdir_p(dirname) unless File.exists?(dirname)
File.open(DOTCACHE, 'w') { |f| f.puts tasks }
end
Rake::Task[DOTCACHE].invoke
tasks = File.read(DOTCACHE)
exit 0 unless /\brake\b/ =~ ENV["COMP_LINE"]
after_match = $'
task_match = (after_match.empty? || after_match =~ /\s$/) ? nil : after_match.split.last
tasks = tasks.select { |t| /^#{Regexp.escape task_match}/ =~ t } if task_match
# handle namespaces
if task_match =~ /^([-\w:]+:)/
upto_last_colon = $1
after_match = $'
tasks = tasks.map { |t| (t =~ /^#{Regexp.escape upto_last_colon}([-\w:]+)$/) ? "#{$1}" : t }
end
puts tasks
exit 0 Update: I fixed the bug that janfri pointed out. The bug caused the first task to be missed. I also changed it so it won't abort if rake isn't the first command on the command line. This will allow stringing multiple commands together. For instance:
rake db:migrate VERSION=0 && rake db:migrate Namespaces and Rake Command Completion 2
I got some basic rake command line completion working today using Jon Baer’s comment. Very simple, very easy:
complete -W '`rake—silent—tasks | cut -d ” ” -f 2`' -o default rake However this didn’t work for the namespaced tasks in a rails app like rake test:units. Searching a little further I found a reference to some code Nicholas Seckar wrote on project.ioni.st. This used ruby to find the possible tasks for command completion. This looked promising, but it still didn’t work for namespaced tasks. A little more googling led me to what looked like the perfect link: Rake-completion script that handles namespaces. Alas, it only handled one level of namespacing. It worked nicely for rake test:units, but rake tmp:ses<TAB> would complete to rake tmp:clear instead of rake tmp:sessions:clear. Also, rake test:units <TAB> would complete to rake test:units units instead of giving me all the tasks again, just in case you want to run multiple tasks form the command line.
So, now what? Stand on the shoulders of others, naturally. Here is what I’m using now that handles multiple namespaces and multiple tasks per command line:
#!/usr/bin/env ruby
# Complete rake tasks script for bash
# Save it somewhere and then add
# complete -C path/to/script -o default rake
# to your ~/.bashrc
# Nicholas Seckar <nseckar@gmail.com>
# Saimon Moore <saimon@webtypes.com>
# http://www.webtypes.com/2006/03/31/rake-completion-script-that-handles-namespaces
exit 0 unless File.file?(File.join(Dir.pwd, 'Rakefile'))
exit 0 unless /^rake\b/ =~ ENV["COMP_LINE"]
after_match = $'
task_match = (after_match.empty? || after_match =~ /\s$/) ? nil : after_match.split.last
tasks = `rake --silent --tasks`.split("\n")[1..-1].collect {|line| line.split[1]}
tasks = tasks.select {|t| /^#{Regexp.escape task_match}/ =~ t} if task_match
# handle namespaces
if task_match =~ /^([-\w:]+:)/
upto_last_colon = $1
after_match = $'
tasks = tasks.collect { |t| (t =~ /^#{Regexp.escape upto_last_colon}([-\w:]+)$/) ? "#{$1}" : t }
end
puts tasks
exit 0 Fowler on RubyGems
Since Daniel came back from Europe, I figured I’d finally pick up his slack and write an entry. :)
Last night I went to the Boulder-Denver Ruby Group where Chad Fowler spoke about all things RubyGems. It was a laid back talk with a recap of gems past, present and future. Pre-RubyConf 2003 the ruby-talk list would get peppered with questions like “Where is Ruby’s CPAN?”
At RubyConf 2003, when put on the spot by David A. Black about the topic, Matz basically said “if you build it, it will be included in ruby core.
That led Jim Weirich, Rich Kilmer, Black, and Fowler to spend the next night coding what would become the RubyGems. They even demo-ed it at the conference. It has grown quite a bit since then and now could be called the de facto standard for library distribution in Ruby.
The latest version of RubyGems is 0.9.0, which has some not so minor scalability improvements. To get the latest version simple run gem update --system. Since RubyGems isn’t in core yet, you still need to require 'rubygems' or set your RUBYOPT environment variable to rubygems. There is another way when starting ruby you can pass it the -rubygems argument. “But you said it wasn’t in core... if it’s not in core, why does it have it’s own argument for the ruby interpreter?”“
It’s true, it’s not in core. This is actually just a little trick by Chad et al to make it look it is part of ruby. The -r argument tell ruby to require whatever parameter follows, so the gem crowd made an ubygems.rb file that only has one line of code in it …
require 'rubygems'ruby -rubygems.
Bundled with gems is its own gem_server. Firing this up starts up a WebBrick server on port 8808 by default. Going to http://localhost:8808 will then display all of the gems you have installed and even link to the rdoc installed locally if it exists and to the docs on the web. It also can serve gems if you wish. This means other machines could point to your gem_server and install any of the gems that you have installed. You do this with the --source argument. Others could search what gems you have by running gem search -r --source http://your_machine:8808 search_string. There is even a tool index_gem_repository.rb that will create a directory that can be served by apache as a gem server, in case you want something a little beefier than webrick.
One interesting idea that Chad mentioned was making a meta-gem for your projects. All the gem would declare is its dependencies on the other gems that your project needs. That way when setting up a new machine you can just install your meta-gem from your gem_server and then it will automatically install all the necessary gems. Pretty cool.
Chad did a good job of describing how gems came to be and guiding us through some of the code with some nice anecdotes along the way.
P.S. The coolest part of the night was I’m going to be a volunteer at RubyConf `06! Chad was quick to warn me that there is no glamour involved. Oh well, I’m excited anyway.