Posted by rick
Tue, 27 Nov 2007 15:14:00 GMT
I’m posting at a new site (b.logi.cx) ... more news on that later. The first article is about a new piece of software I’m developing called Object Daddy, meant to do away with the need for Ruby on Rails fixtures and to greatly ease ActiveRecord model testing. The article about it is LONG, but don’t let that scare you, it’s all in good fun.
Tags daddy, flawed, logic, logicx, object, ogconsulting, output, rails, ruby, testing | no comments
Posted by rick
Wed, 03 Jan 2007 00:07:00 GMT
UPDATE: I edited the autotest hook to (1) fix a bug, and (2) to make it more readable (not sure if I succeeded, but worth a shot).
I figured out a couple of things that were useful enough to me that I thought I’d share.
First off, if you’re a big user of autotest (and if you’re not, you might oughta should be), and you’re working on a really large application, maybe you’ll find this useful:
Drop this in your ~/.autotest file:
Autotest.send(:alias_method, :real_find_files, :find_files)
Autotest.send(:define_method, :find_files) do |*args|
real_find_files.select do |k,v|
(ENV['AUTOTEST'] and ! ENV['AUTOTEST'].empty?) ?
Regexp.new(ENV['AUTOTEST']).match(k) : true
end
end
and then you can do stuff like:
% AUTOTEST='users' autotest # run all tests with 'users' in the name
% AUTOTEST='digital.*cont' autotest # run all the digital signaturing functional tests
% AUTOTEST='party|digital' autotest # run all tests for parties or digital signaturing
This allows you to focus in on a small area of concern and still get the rapid feedback autotest gives you. Be sure to periodically run your entire test suite to make sure you’re not missing action-at-a-distance errors.
The other tip is a purely Rails tip. If you’re ever used ActiveRecord’s polymorphic associations then you may have run into a situation where you want many many many things to be able to be on the other (has_many) end of the polypmorphic association. I ran into this while doing work with digital signatures: I want to be able to associate a signature with almost any model object in my system. I should be able to retrieve the signatures (if any) for an object by just saying “object.signatures”.
This would normally require a “has_many :signatures, :as => signable” in every signable class in the system. Maybe reasonable, but if I were to forget to add the has_many to a class signaturing would stop working. It’s actually worse in my case since the signing algorithm can sign a web of related objects, so forgetting the has_many in one class would result in failures in related but not identical classes.
I decided to add functionality to have this happen automatically. At first I tried just opening ActiveRecord::Base and adding the call, which doesn’t put the call in the right scope for the subclasses (as don’t a variety of other AR::Base hacks I tried). I realized at some point that I needed to use Ruby’s “inherited” method to get the subclass at the right time, but AR::Base already uses inherited for other nefarious purposes. It turns out, this is the incantation that works:
class ActiveRecord::Base
def self.inherited_with_signaturing(subclass)
self.inherited_without_signaturing(subclass)
subclass.send(:has_many, :signatures, :class_name => 'DigitalSigDigest', :as => :signable) unless subclass.name =~ /^DigitalSig/
end
class << self
alias_method_chain :inherited, :signaturing
end
end
As you’ll notice, my signaturing model is actually DigitalSigDigest. Also, I have other DigitalSig-something classes lying around that are part of the signaturing library, and using inherited this way allows me to actually conditionally include/exclude which classes get the signaturing has_many relationship. That’s cooler than I expected. Chalk up another one for Ruby.
By the way, you’re probably also going to be interested in this ticket if you’ve got STI classes with polymorphic stuff linked to them.
Tags autotest, polymorphic, rails, ruby | no comments
Posted by rick
Thu, 13 Jul 2006 01:07:00 GMT
Kent Sibilev posted a cool article, Tutorial on ruby-debug ,which shows off his new Ruby+C hybrid debugger suitable for use with large applications, such as Rails apps, where the debug.rb debugger was just too slow to be useful.
This brings most of the slickness of the breakpointer (just drop a line in your code and wait for the prompt to appear) to the power of the debugger (pop up and down stack frames with ease, step, breakpoint, watchpoint, whatever). Just gem install the thing and hack++.
Looks like the only thing is missing is the DRuby hooks to let the new debugger work with FastCGI (etc.) when there’s no console waiting, and I’m guessing someone will get to that before my lazy butt gets around to scratching that particular itch.
Very cool.
Tags input, rails, ruby | no comments
Posted by rick
Mon, 27 Feb 2006 00:17:00 GMT
In order to make automated “7 day Art weather forecast” emails like these on the new AirSet art-talk calendar I had to update my ruby script. Here’s the new version (and you’ll note that the bulk of the code deals with cleanup of bastardized input):
#!/usr/bin/ruby
require 'open-uri'
require 'rexml/document'
require 'rexml/xpath'
require 'time'
URL = "http://www.airset.com/syndicate/public/1391/week.xml"
def cleanup(text, keep_stars = false)
result = text.
gsub(/&/, '&').
gsub(/&[lr]?quot;/, '"').
gsub(/'/, "'").
gsub(/'/, "'").
gsub(/>/, '>').
gsub(/</, '<').
gsub(/ /, ' ').
gsub(%r{</?[^>]+>}, '').
gsub(/\*\s*\*/, '**').
gsub(/\342\200\235/, '"').
gsub(/\342\200\234/, '"').
gsub(/\342\200\231/, "'").
gsub(/\342\200\223/, " -- ").
gsub(/\342\200\224/, " -- ").
gsub(/\303\242/, "a").
gsub(/\303\251/, "e").
gsub(%r{/+\s*$}, '')
result.gsub!(/\s*\*\s*/, '') unless keep_stars
result
end
def wordwrap(text, line_width = 70)
text.gsub( /\n/, "\n\n" ).gsub( /(.{1,#{line_width}})(\s+|$)/, "\\1\n")
end
def output(title, time, location, link, description)
title = cleanup(title)
time = cleanup(time)
location = cleanup(location)
link = cleanup(link)
description = wordwrap(cleanup(description, true))
puts "#{time.chomp} - #{title}"
puts " online: <#{link.chomp}>"
puts " location: #{location}\n\n"
description.split("\n").each {|l| puts " #{l}"}
puts ""
end
def fetch_document(link)
open(link) { |f| return f.read.split("\n").join(' * ') }
end
def extract_location(doc)
doc =~ %r{<span\s+class="evDescAndLoc">([^<]+)</span>}
place = $1 || ''
place = place.sub(/^.* at /, '').gsub(/\s+\*\s+/, '')
doc =~ %r{<span\s+class="evAddress">(.*?)</span>}
address = $1 || ''
address = address.gsub(/Get map/, '').gsub(/\s+\*\s+/, '')
place += " / #{address}" unless address =~ /^\s*$/
place
end
def extract_description(doc)
doc =~ %r{<span\s+class="evNote">(.*?)</span>}
return ($1 || '')
end
def retrieve_data(item)
# Extract title and time
if item.elements['title'].text =~ /\(([^)]+)\)\s*$/
time = $1
else
t = Time.parse item.elements['pubDate'].text
hour = t.hour % 12
hour = 12 if 0 == hour
time = "%02d/%02d/%4d (%d:%02d%sM)" %
[t.month, t.day, t.year, hour, t.min, t.hour > 11 ? 'P':'A']
end
title = cleanup(item.elements['title'].text.sub(/\([^)]+\)\s*$/, ''))
link = item.elements['link'].text
doc = fetch_document(link)
location = extract_location(doc)
description = extract_description(doc)
[title, time, location, link, description]
end
puts "7 Day Art Weather Forecast"
puts
puts " ... see the Art-Talk Calendar for more events:"
puts
puts " online at: <http://www.airset.com/Public/Calendars.jsp?id=1391>"
puts
puts " -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"
puts
open(URL) do |f|
xml = REXML::Document.new(f.read)
REXML::XPath.each(xml, '//item') do |item|
title, time, location, link, description = retrieve_data(item)
output(title, time, location, link, description)
end
end
Tags art, output, ruby, talk | no comments