Malaysia.rb March 2008 Meetup
Spammed on several places:
We have two presentations lined up for the night:
1. Gavin will provide a survey of different methods of handling missing references in various programming languages with emphasis on Scala. This stuff has been popping up a lot on Reddit. For a good backgrounder (in Ruby), see http://blog.thoughtfolder.com/2008-03-16-navigating-nil-method-chaining-in-ruby.html
2. Aizat will live build a Jabber bot that integrates with Twitter, provides system monitoring and makes coffee in the morning. Expect lots of BDD love with Autotest and RSpec.
Also, I have one last FREE PeepCode Episode Coupons to give away!
When: Thursday, March 27th 2008, 7:30PM
Agenda:
7:30 - 8:00pm Socializing
8:00 - 8:15pm Opening by the Organizer
8:15 - 8:45pm "Survey of different methods of handling missing Preferences" by Gavin Bong
8:45 - 9:15pm "Developing a Jabber bot ... LIVE!" by Aizat Faiz
9:15 - 9:30pm PeepCode Episode Coupon Lucky Draw and CloseCost:
Free. No registration required, just come right in, have a seat, and join the crowd. Invite your friends.Contact:
kamal.fariz@gmail.com
+60123099143
aizat.faiz@gmail.com
+60176908783Where:
Open University Malaysia Angkasa Raya
Floor Tingkat 3, Bangunan Angkasa Raya,
Jalan Ampang
Kuala LumpurLocated near Kuala Lumpur City Centre (KLCC)
15 minute walk from the KLCC LRT station.
Praying facilities are available on the campus.
Automatically Restart script/server For Easier Plugin Development with FSEvents
When developing a Rails plugin, you are required to restart the server so that your plugin will be reloaded. You'll notice that after a while this becomes a rather tedious process, especially if you are working on a hot-off-the-press new plugin.
Following suit on my autotest with FSEvents, I opted to listen for any changes to the vendor/plugins and lib directories to restart the server as required. Thus freeing me of the horrible grunt work of restarting the server.
Note: This is Mac OS X 10.5 Leopard specific.
The Incantation
Create the required file script/autorestart_server, and put this beauty in it.
For an easy to copy dump, click "view plain".
Note: Don't forget to make it executable! chmod u+x script/autorestart_server
#!/usr/bin/env ruby
PATHS_TO_OBSERVE = /^(lib|vendor\/plugins)/
require 'osx/foundation'
OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
include OSX
def start_server
IO.popen("script/server #{ARGV.join(' ')}")
end
def stop_server(io)
return if io.nil?
Process.kill("INT", io.pid)
end
io = start_server
callback = proc do |stream, ctx, numEvents, paths, marks, eventIDs|
paths.regard_as('*')
rpaths = []
length = Dir.pwd.length + 1
numEvents.times { |i| rpaths << paths[i][length..-1] }
next if rpaths.select { |path| path =~ PATHS_TO_OBSERVE }.empty?
stop_server(io)
puts "Restarting server"
io = start_server
end
stream = FSEventStreamCreate(KCFAllocatorDefault, callback, nil, [Dir.pwd], KFSEventStreamEventIdSinceNow, 1.0, 0)
unless stream
puts "Failed to create stream"
exit
end
FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), KCFRunLoopDefaultMode)
unless FSEventStreamStart(stream)
puts "Failed to start stream"
exit
end
begin
CFRunLoopRun()
rescue Interrupt
stop_server(io)
FSEventStreamStop(stream)
FSEventStreamInvalidate(stream)
FSEventStreamRelease(stream)
end
Automatically Restarting script/server
Now feel free to execute script/autorestart_server
Wondering where else I can inject FSEvents into, its quite a handy little tool...
Taming the autotest Beast with FSEvents
autotest is a great tool and all, but it is simply resource intensive. Due to autotest's implementation, it eats up CPU resources, not because the tests are always running (only after you modify your file), but because autotest continually polls each file in your directory, and sub directories, and checks to see if it has been modified.
This continual polling isn't good for CPU resources. Now I may have some spare cycles to let autotest do its thang, but it also doesn't sound too healthy for my hard drive.
Note: This is Mac OS X 10.5 Leopard specific.
Along Comes a Leopard
Whilst reading the Arstechnica review on Leopard, I came upon the section on the File System Events (FSEvents) that was introduced in 10.4 actually (for Spotlight), but used once again for Time Machine. In Mac OS X 10.5 Leopard, the API was opened up for the public to consume.
File System Events (FSEvents)
In its simplest level, your application will notify FSEvents, that it wants to listen to a particular directory, and when that directory (or its sub directories) are modified, FSEvents basically triggers a callback in your application. This allows you to hook to, listen to file system changes, and react to accordingly.
This is exactly what I needed to calm the fury of the autotest beast.
Install RubyCocoa
Note: It looks like the code works on the stock Ruby and RubyCocoa out of the box actually. You don't need to install the latest version.
First of all, you need to install RubyCocoa, as this provides us with the bindings required to communicate with FSEvents. As I installed Ruby via MacPorts, I opted to do a source install (the MacPorts at present, is one version behind). I did run into trouble, encountering this error "file is not of required architecture".
If you ever needed a reason to actually use RubyCocoa, let this be your reason!
Taming the beast
Once installed, dump this little gem into your ~/.autotest file:
class Autotest
def run
hook :run
reset
run_tests
require 'osx/foundation'
OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
callback = proc do |stream, ctx, numEvents, paths, marks, eventIDs|
paths.regard_as('*')
rpaths = []
numEvents.times { |i| rpaths << paths[i] }
run_tests_in_paths(*rpaths)
end
allocator = OSX::KCFAllocatorDefault
context = nil
path = [Dir.pwd]
sinceWhen = OSX::KFSEventStreamEventIdSinceNow
latency = 1.0
flags = 0
stream = OSX::FSEventStreamCreate(allocator, callback, context, path, sinceWhen, latency, flags)
unless stream
puts "Failed to create stream"
exit
end
OSX::FSEventStreamScheduleWithRunLoop(stream, OSX::CFRunLoopGetCurrent(), OSX::KCFRunLoopDefaultMode)
unless OSX::FSEventStreamStart(stream)
puts "Failed to start stream"
exit
end
OSX::CFRunLoopRun()
rescue Interrupt
OSX::FSEventStreamStop(stream)
OSX::FSEventStreamInvalidate(stream)
OSX::FSEventStreamRelease(stream)
end
def find_files_in_paths(*paths)
current_dir = Dir.pwd.length + 1
result = {}
paths.each do |path|
# Largely copied from autotest
Find.find path do |f|
Find.prune if @exceptions and f =~ @exceptions and test ?d, f
next if test ?d, f
next if f =~ /(swp|~|rej|orig)$/
next if f =~ /\/\.?#/
filename = f[current_dir..-1]
result[filename] = File.stat(filename).mtime rescue next
end
end
return result
end
def run_tests_in_paths(*paths)
find_files_to_test(find_files_in_paths(*paths))
return if @files_to_test.empty?
# Copied from autotest
cmd = make_test_cmd @files_to_test
hook :run_command
puts cmd
old_sync = $stdout.sync
$stdout.sync = true
@results = []
line = []
begin
open("| #{cmd}", "r") do |f|
until f.eof? do
c = f.getc
putc c
line << c
if c == ?\n then
@results << line.pack("c*")
line.clear
end
end
end
ensure
$stdout.sync = old_sync
end
hook :ran_command
@results = @results.join
handle_results(@results)
end
end
Now when you run autotest, you'll run into something like this:
/Users/aizat/.autotest:4: warning: method redefined; discarding old run
Don't worry about it, and feel free to ignore it.
With this code, you can also tame the beast. There you have it, a much saner autotest, only for Mac OS X 10.5 Leopard.
