Posted by kev
Sun, 06 Apr 2008 00:13:00 GMT
Just drop this in your Rakefile. This is slightly modified from something I’m using in production.
Disect! Enjoy! Explanation (read: spoilers) after the jump.
begin
require 'rake_remote_task'
APP_NAME = "someapp"
DEPLOY_ROOT = "/usr/local/share/applications/#{APP_NAME}"
ON_DEPLOY_RESTART = ["someappd"]
role :app_server, "myserver.com"
def archive
commit = `git-rev-list --max-count=1 --abbrev=10 --abbrev-commit HEAD`.chomp
file = "#{APP_NAME}-#{commit}.tar.gz"
end
def restart_daemons
ON_DEPLOY_RESTART.each do |app|
run "sudo god restart #{app}"
end
end
namespace :deploy do
task :build do
sh "git archive --format=tar HEAD | gzip > #{archive}"
end
remote_task :push => :build do
rsync archive, "/tmp"
end
desc "Install a release from the latest commit"
remote_task :install => :push do
date_stamp = Time.now.strftime("%Y%m%d")
last_release = run("ls #{DEPLOY_ROOT}/rels | sort -r | head -n 1").chomp
if last_release =~ /#{date_stamp}\-(\d+)/
serial = $1.to_i + 1
else
serial = 0
end
rel = ("%d-%02d" % [date_stamp, serial])
rel_dir = "#{DEPLOY_ROOT}/rels/#{rel}"
run "sudo mkdir -p #{rel_dir}"
run "sudo tar -xzvf /tmp/#{archive} -C #{rel_dir} && rm -rf /tmp/#{archive}"
run "sudo ln -s -f -T #{rel_dir} #{DEPLOY_ROOT}/current"
restart_daemons
end
desc "Rollback to the previous release"
remote_task :rollback do
current_link = run("ls -alF #{DEPLOY_ROOT} | awk '/current -> .*/ { print $NF }'").chomp
current = File.basename(current_link)
releases = run("ls #{DEPLOY_ROOT}/rels | sort -r").split("\n")
previous = releases.find {|rel| current > rel}
raise "No previous release" if previous.nil?
run "sudo ln -s -f -T #{DEPLOY_ROOT}/rels/#{previous} #{DEPLOY_ROOT}/current"
restart_daemons
puts "Moved to #{previous}"
end
desc "Rollforward to the next release"
remote_task :rollforward do
current_link = run("ls -alF #{DEPLOY_ROOT} | awk '/current -> .*/ { print $NF }'").chomp
current = File.basename(current_link)
releases = run("ls #{DEPLOY_ROOT}/rels | sort -r").split("\n")
next_rel = releases.find {|rel| current < rel}
raise "No next release" if next_rel.nil?
run "sudo ln -s -f -T #{DEPLOY_ROOT}/rels/#{next_rel} #{DEPLOY_ROOT}/current"
restart_daemons
puts "Moved to #{next_rel}"
end
end
rescue LoadError => e
puts "NOTE: Install vlad to get Kevin's awesome deployment tasks"
end
Read more...
Posted in Ruby, Hacks | 4 comments
Posted by kev
Tue, 27 Nov 2007 19:24:00 GMT
Because I hadn’t implemented DFS in Ruby before, and it’s just so damn easy.
Update: Phillip rightly pointed out in the comments that with the yield at the end, it’s actually post-order traversal, not depth first search per se.
class TreeNode
attr_reader :name
def initialize(name)
@name = name
@children = []
end
def add_node(node)
@children << node
end
def each_depth_first
@children.each do |child|
child.each_depth_first do |c|
yield c
end
end
yield self
end
end
root = TreeNode.new("root")
root.add_node( a = TreeNode.new("a"))
a.add_node( b = TreeNode.new("b"))
a.add_node( c = TreeNode.new("c"))
c.add_node( d = TreeNode.new("d"))
root.add_node(e = TreeNode.new("e"))
e.add_node(f = TreeNode.new("f"))
e.add_node(g = TreeNode.new("g"))
root.each_depth_first do |child|
puts child.name
end
Posted in Ruby, Hacks | 3 comments
Posted by kev
Tue, 30 Oct 2007 01:16:00 GMT
I haven’t yet decided if this is a good idea or not.
I’ll be at RubyConf this weekend. Say hello, if you get the urge.
require "test/unit"
require 'rubygems'
require 'mocha'
require 'stubba'
module ForwardsToEnumerable
def self.included(klass)
klass.extend(ClassMethods)
end
module ClassMethods
def forward_to_enum(instance_var, *meths)
meths.each do |meth|
class_eval <<-METH
def #{meth}(*args, &block)
#{instance_var.to_s}.each do |i|
i.send(:#{meth}, *args, &block)
end
end
METH
end
end
end
end
class ForwardsToArray
include ForwardsToEnumerable
forward_to_enum :@array, :foo, :bar, :baz
def initialize(array)
@array = array
end
end
class TestForwardsToArray < Test::Unit::TestCase
def test_forward_to_enum
items = [mock(), mock(), mock()]
items.each {|i| i.expects(:foo); i.expects(:bar); i.expects(:baz) }
f = ForwardsToArray.new(items)
f.foo
f.bar
f.baz
end
end
Posted in Ruby, Hacks | no comments
Posted by kev
Fri, 21 Sep 2007 20:21:00 GMT
def <=>(other)
return 0 if self.version == other.version and self.rel == other.rel
versions = self.version.split(/[^[:alnum:]]/).push self.rel
other_versions = other.version.split(/[^[:alnum:]]/).push other.rel
return 1 if versions.size > other_versions.size
return -1 if versions.size < other_versions.size
versions.size.times do |n|
if versions[n] =~ /[^\d]/ && other_versions[n] =~ /[^\d]/
comparison = (versions[n] <=> other_versions[n])
elsif versions[n] !~ /[^\d]/ && other_versions[n] !~ /[^\d]/
comparison = (versions[n].to_i <=> other_versions[n].to_i)
else
comparison = -1
end
return comparison unless comparison.zero?
end
return 0
end
Original version sort was here.
Posted in Ruby, Hacks | no comments
Posted by kev
Wed, 19 Sep 2007 21:19:00 GMT
/* take care of the case where the two version segments are */
/* different types: one numeric and one alpha */
if (one == str1) return -1; /* arbitrary */
if (two == str2) return -1;
– rpm/lib/misc.c
Posted in Musings, Hacks | no comments
Posted by kev
Wed, 19 Sep 2007 07:35:00 GMT
/usr/bin/git-quickserver
#!/bin/sh
git daemon --verbose --reuseaddr --export-all --base-path='.'
sisyphus:~/code/god kev$ git quickserver
sisyphus:~ kev$ git clone git://localhost/ somethin
Initialized empty Git repository in /Users/kev/somethin/.git/
remote: Generating pack...
remote: Done counting 1469 objects.
remote: Deltifying 1469 objects...
100% (1469/1remote: 469) done
Indexing 1469 objects...
remote: Total 1469 (delta 905), reused 1461 (delta 902)
100% (1469/1469) done
Resolving 905 deltas...
100% (905/905) done
(via KirinDave)
Posted in Hacks | no comments
Posted by kev
Sun, 06 May 2007 10:58:00 GMT
One of the many awesome things about working at Powerset is the guys I get to hack with. Tonight, my buddies Tom Preston-Werner, Chris Van Pelt, and I were feeling whimsical. Full source, with quicksilver hook and startup scripts can be found here, but this is the meat:
growl_handler.rb
require 'rubygems'
require 'ruby-growl'
module Jakl
class GrowlHandler
def initialize
if `which growlnotify` =~ /^no .+ in/
@strategy = :ruby
@growl = Growl.new("localhost", "jakl", ["jakl_message"])
else
@strategy = :command
end
end
def notify(group, name, message)
case @strategy
when :command
img_path = File.join(File.dirname(__FILE__), '../../assets/jakl.png')
`growlnotify -n jakl --image
when :ruby
@growl.notify("jakl_message", "#{name} (#{group})", message)
else
raise StandardError.new('Invalid strategy')
end
end
end
end
client.rb
require 'rubygems'
require 'net/dns/mdns-sd'
require 'base64'
module Jakl
class Client
DNSSD = Net::DNS::MDNSSD
@@debug = false
def self.debug=(value)
@@debug = value
end
def self.debug
@@debug
end
def initialize(options={})
default_options = {
:default_recv => "jakl",
:timeout => 2,
:login => ENV['USER']
}
@options = default_options.merge(options)
end
def send(message, recv=nil)
recv ||= @options[:default_recv]
recv = recv.split(',').collect {|g| g.strip }
puts "Sending: '#{message}' to '#{recv.join(',')}'" if @@debug
find_recipients = DNSSD.resolve('jakl', '_jakl._tcp') do |r|
puts "Found jakl service at #{r.target}" if @@debug
recvs = r.text_record['recvs'].split(',').collect {|g| g.strip }
puts " responds to: #{recvs.join(', ')}" if @@debug
if (succ_recvs = recvs & recv).any?
puts "Sending to: #{r.target}:#{r.port}" if @@debug
data = [@options[:login], succ_recvs.first, message].map do |s|
Base64.encode64(s)
end.join(';')
TCPSocket.new(r.target, r.port).send(data, 0)
end
end
sleep @options[:timeout]
find_recipients.stop
end
end
end
server.rb
require 'rubygems'
require 'eventmachine'
require 'net/dns/mdns-sd'
require 'base64'
$:.unshift File.dirname(__FILE__)
require 'growl_handler'
module JaklEventServer
def receive_data(data)
name, recv, message = data.split(';').map {|s| Base64.decode64(s) }
Jakl::GrowlHandler.new.notify(recv, name, message)
$stderr.puts "Name: #{name}, Recipient: #{recv}, Message: #{message}" if Jakl::Server.debug
end
end
module Jakl
class Server
DNSSD = Net::DNS::MDNSSD
@@debug = false
def self.debug=(value)
@@debug = value
end
def self.debug
@@debug
end
def initialize(options={})
default_options = {
:recvs => "jakl",
:timeout => 5,
:login => ENV['USER']
}
@options = default_options.merge(options)
validate_login!
end
def start
DNSSD.register('jakl', '_jakl._tcp', 'local', 4180,
{'recvs' => @options[:recvs], 'login' => @options[:login]})
EventMachine::run {
EventMachine::start_server "0.0.0.0", 4180, JaklEventServer
puts "Listening for howls on 4180"
}
end
def validate_login!
name_validation = DNSSD.resolve('jakl', '_jakl._tcp') do |r|
if [r.text_record['login'],
r.text_record['recvs'].split(',')].flatten.include? @options[:login]
puts "The name #{@options[:login]} is already taken. Sorry :\\"
exit 1
end
end
@options[:recvs] = [@options[:recvs].split(',') << @options[:login]].join(',')
sleep 3
name_validation.stop
end
end
end
Posted in Ruby, Hacks | no comments
Posted by kev
Tue, 20 Mar 2007 07:47:00 GMT
class Array
def shuffle
sort { rand(3) - 1 }
end
end
arr = (1..10).to_a
arr.shuffle
arr.shuffle
Posted in Coding, Ruby, Hacks | 7 comments
Posted by kev
Tue, 19 Dec 2006 09:24:00 GMT
Update: Ruby2Ruby is having gem propogation issues. Feel free to download the gem here directly and install via gem install ruby2ruby-1.1.2.gem.
Update 2: We’ve found a bug in the loading that causes problems when you supply a method to Heckle. A bug fix has been checked into the repo and we’re preparing a release. Look for 1.1.1 soonish.
Update 3: Ok, 1.1.1 is out the door. The gem server is syncing, so look for a new version this afternoon (12/20) with several bugs including the loading error fixed.
Yes, I know what you’re thinking. “Holy crap, Kevin posted for the first time in months! I thought he died, or got eaten by a corporate zombie, or set out on a epic adventure to find himself.” But hey, good things come to those who wait, right?
So, you’ve been waiting, and I’ve been writing Heckle. It’s a good thing.
Heckle is a mutation tester. It modifies your code and runs your tests to make sure they fail. The idea is that if code can be changed and your tests don’t notice, either that code isn’t being covered or it doesn’t do anything.
It’s a little weird, I know, but I like to think about it as pen-testing. It’s like hiring a white-hat hacker to try to break into your server and making sure you detect it. You learn the most by trying to break things and watching the outcome.
Anyway, Heckle was inspired by Jester, and Ryan Davis wrote a proof of concept at RubyConf. As he notes, I went a little nuts and much of the current implementation I rewrote that night or on the plane home.
You can install Heckle from Ruby Gems:
gem install heckle --include-dependencies
Let’s take the new toy out for a test drive.
Read more...
Posted in Hacks, testing | 10 comments
Posted by kev
Tue, 22 Aug 2006 17:49:00 GMT
Are your test runs dull and listless? Do you miss the red/green bar of JUnit? Well, now you don’t have to! Between Pat Eyler’s RedGreen and Chris Wanstrath’s modifications you can have red, green and yellow tests. That doesn’t sound nearly as cool written, but take a look:

It makes me happy. Oh, and a 3 line addition to your ~/.autotest will make colorized autotest runs (pictured above).
Posted in Ruby, Hacks, testing