Double escaping backslashes in ruby.
If you have a string that looks like:
\
and you want it to look like:
\\
in ruby, you have to do the following:
-
-
irb(main):070:0> str = "\\"
-
=> "\\"
-
irb(main):071:0> puts str
-
\
-
=> nil
-
irb(main):073:0> puts str.gsub("\\", "\\\\\\\\")
-
\\
-
=> nil
-
In Ruby, you can use perl-style regular expression numbered group references in replacement strings (ie \\1 = the first captured group of the regexp). So, to make a literal backslash in gsub, you need “\\\\”. And to make the second one, you need “\\\\\\\\”.
Slightly better YAMLization of Ruby Exceptions.
Consider the following irb session:
-
-
irb(main):001:0> e = Exception.new("Jerk!")
-
=> #<Exception: Jerk!>
-
irb(main):002:0> e.message
-
=> "Jerk!"
-
irb(main):003:0> require ‘yaml’
-
=> true
-
irb(main):004:0> YAML.load(e.to_yaml).message
-
=> "Exception"
-
That sucks. Where did the “Jerk!” go? YAML ate it. If you include this code, you can keep your “Jerk!”:
-
-
cpm@juno:~/helpticket-dev/trunk$ cat lib/better_exception_yaml.rb
-
require ‘yaml’
-
-
class Exception
-
def Exception.yaml_new( klass, tag, val )
-
o = YAML.object_maker( klass, {})
-
val.each_pair do |k,v|
-
o.instance_variable_set("@#{k}", v)
-
end
-
o.exception(val["message"])
-
end
-
end
-
-
cpm@juno:~/helpticket-dev/trunk$ irb
-
irb(main):001:0> require ‘lib/better_exception_yaml’
-
=> true
-
irb(main):002:0> YAML.load(Exception.new("JERK").to_yaml).message
-
=> "JERK"
-
It still sucks because the backtrace is not preserved, but that’s because Exception#to_yaml doesn’t serialize that information. YAML was storing the message, but by default it stores it into a @mesg instance variable instead. Quite odd.
I wasn’t inclined enough to read up on the right way to yamlize new data. Maybe later I’ll add backtracing.
Installing eventmachine on older FreeBSD’s.
I’m replacing some backgroundrb workers that do long, slow TCP connections with async eventmachine code in one of my Rails webapps. I ran into some problems installing eventmachine 0.7.2 on FreeBSD 4.8-STABLE with no root access, so I’m documenting how I got around these problems. I’m certain this isn’t the best way to fix this problem, but it worked for me.
First of all, “gem install eventmachine” fails because on FreeBSD, gcc expects “-pthread” when using the pthreads library. Linux gcc normally uses “-lpthread”. Instead, download and unpack the .tar.gz.
You’ll notice that “ruby setup.rb config” fails with the same error as “gem install eventmachine”. Open ext/extconf.rb in the editor of your choice and search for
-
-
unless have_library(‘pthread’)
-
exit
-
end
-
Comment this entire block of code out. It is dead to us. Now when you run “ruby setup.rb config”, a Makefile will be generated. Huzzah! Now, open ext/Makefile and look for a place to add -pthread. I added it to the LIBS environment variable.
Now, you may or may not be able to run “ruby setup.rb setup && ruby setup.rb install” without problems.
If so, great!
If not, I have nothing else to share with you. Best of luck with your problems.
My mind is gone.
Today I decided to finally start teaching myself Python. Out of curiosity, I tried the following:
-
-
>>> class NoneType:
-
… def __str__(self):
-
… return ""
-
…
-
>>> None
-
>>> str(None)
-
‘None’
-
Is it sick that I find these results disappointing? Has Ruby corrupted my mind?
A few years ago I would have never even thought about overriding methods of a language’s null type.
Double-you-tee-eff, Gmail?
I just noticed that if you rename or remove a label, filters don’t get updated appropriately.
For example, let’s say that I don’t like having MySpace or FaceBook notifications in my inbox, so I create a MySpace label and and
FaceBook label and set up filters that archives mail and labels it appropriately.
Let’s skip ahead a few months. I now have filters and labels set up for MySpace, FaceBook, Virb, Friendster, LiveJournal and many other websites. I’m starting to experience label overload. To try to keep things under control, I rename the MySpace label “Social Networking Crap”.
This is what I would expect to happen:
- I should get some sort of warning that FaceBook, Virb, Friendster, LiveJournal, etc. labels are referenced in filters, so they shouldn’t be deleted.
- Mail previously filtered under MySpace should notice the name change and now filter under “Social Networking Crap”.
This is what does happen:
- FaceBook, Virb, Friendster, and LiveJournal mail just gets archived without any labeling. This is sub-optimal in my opinion, but somewhat understandable.
- Mail previously filtered under MySpace is also archived without any labeling. WTF?
Of course, the real solution in this hypothetical scenario is to not participate in as much social networking crap. But I digress.
Update Did I speak too soon? It seems like sometimes the expected MySpace behavior works, while other times it doesn’t? Odd.
Broken static methods in PHP make me sad.
A few weeks ago, I tried implementing ActiveRecord in PHP. What a fool I was. Consider the following:
-
-
<?php
-
class ActiveRecord {
-
return "SELECT * FROM " . self::$table;
-
}
-
}
-
-
class User extends ActiveRecord {
-
}
-
?>
-
Sadly, User::findAll() returns “SELECT * FROM NOTABLE”.
I guess in order to implement ActiveRecord in PHP, you need to use some sort of combination of Singleton and Factory patterns? Gross.
Add this to the list of things that make me angry when I work in PHP. (The first item in that list is $a ||= “default” doesn’t work. Bah!)
6 < 4.8?
Like any good developer, I write code on a development server and then push the code to the production server. (Yay, SVN.) While testing out something, I noticed an odd inconsistency between the two.
Consider the following:
-
sock = TCPSocket.new( ip, port )
Fairly simple. When I run it on my test server, I got this result:
-
Errno::EINVAL: Invalid argument - connect(2)
When I see something like that, I presume that I made a blunder. Perhaps ip was an odd string, or port was a negative number. Who knows? I double checked myself. The ip and port were both valid.
Then I tried it out on our production server, just for the heck of it. I got this back:
-
Errno::ECONNREFUSED: Connection refused - connect(2)
Turned out the server I was connecting to wasn’t up. A few minutes later, the server is back up and I am able to create sockets from both machines. What?
One possibility that crossed my mind was perhaps I was working with two different versions of Ruby. Check out the results from ruby -v:
dev: ruby 1.8.4 (2005-12-24) [i386-freebsd6]
production: ruby 1.8.4 (2005-12-24) [i386-freebsd4.8]
The only difference is the version of freebsd. I can only guess that freebsd 6’s standard library gives less specific error messages than freebsd 4.8. Either that, or our host upgraded the standard library without bumping the freebsd version number.
How peculiar.
Leaky Abstractions.
You ever have a programming “Doh!” moment? I had one the other day. You could say I fell face first into a leaky abstraction.
For those of you who are unfamiliar with the phrase “leaky abstraction”, Joel Spolsky coined it in his classic essay titled “The Law Of Leaky Abstractions“. Go read it. Just open a new tab. I’ll still be here.
This week at work, I was writing a prototype for an algorithm in Ruby. Once everything was working in memory, I had to see how well things would work when the algorithm iteratively stored it’s results into a database. I am a lazy man, so I just took my existing classes and made them into ActiveRecords.
Consider the following:
-
-
class Student < ActiveRecord::Base
-
def number_of_days_available_between(start_date, end_date)
-
start_date = start_date.at_beginning_of_day
-
end_date = end_date.to_end_of_day
-
-
count = (start_date .. end_date).inject(0) do |sum, date|
-
params = { :start => date.at_beginning_of_day, :endd => date.to_end_of_day }
-
sum += 1 unless self.vacations.find( :first, :conditions => ["start_date >= :start AND end_date ,= :endd", params ] )
-
sum
-
end
-
end
-
-
def probability_for_clinic
-
available_days = self.number_of_days_available_between(start_date, end_date)
-
-
# if student has no days left, there’s no chance! return negative infinity
-
return -1/0.0 if available_days == 0
-
-
assignment_count = Assignment.count(:conditions => ["student_id = :me AND clinic_detail_id = :clinic", {:me => self.id, :clinic => clinic.id}] )
-
-
clinic_days_left = ( clinic.duty_length - assignment_count ).to_f
-
clinic_days_left / available_days
-
end
-
end
-
-
students = Student.find(:all)
-
student_probabilities = students.inject({}) do |hsh, student|
-
hsh[student] = student.probability_for_clinic( clinic, start_date, end_date)
-
hsh
-
end
-
-
students = students.sort {|s1, s2| student_probabilities[s2] <=> student_probabilities[s1] }
I started it up and waited for what felt like an eternity. Much frustration and a ^C later, I realized I went wrong somewhere.
That’s when I remembered that ActiveRecord could be asked to write everything it was doing to STDOUT. Boy was I shocked!
I restarted the program to be greeted by a sea of “SELECT * FROM Vacactions” statements. As previously written, my script was generating about 90! selects to the vacations table for each student. Even with just over 1000 students, this took a silly ridiculous amount of time. My poor little script was in dire need of caching.
My new version isn’t as pretty to read and it could still use a bit more work, but it is much quicker:
-
sql = "clinic_detail_id = :clinic AND student_id IN (:students) AND day >= :start_date AND day <= :end_date"
-
args = { :students => students, :clinic => clinic.id, :start_date => start_date, :end_date => end_date }
-
cached_assignments = Assignment.find( :all, :conditions => [ sql, args ] )
-
-
sql = "student_id IN (:students) AND start_date >= :start_date AND end_date <= :end_date"
-
cached_vacations = Vacation.find( :all, :conditions => [ sql, args ] )
-
students = Student.find(:all)
-
student_probabilities = students.inject({}) do |hsh, student|
-
# find the number of days student will be at school in semester.
-
available_days = (start_date .. end_date).inject(0) do |sum, date|
-
sum += 1 unless cached_vacations.detect { |v| v.student_id == student.id && ( v.start_date .. v.end_date ).include?( date ) }
-
end
-
-
# if there are no days left, there is no chance.
-
if available_days == 0
-
hsh[student] = -1.0/0.0
-
else
-
assignments_served = cached_assignments.select { |a| a.student_id == student.id && ( start_date .. end_date ).include?( a.day ) }.size
-
hsh[student] = ( clinic.duty_length - assignments_served ).to_f / available_days
-
end
-
hsh
-
end
-
-
s = students.sort { |s1, s2| student_probabilities[s2] <=> student_probabilities[s1] }
Do note that if you squint hard enough, these two algorithms look about the same in terms of “big O”.
Sometimes the devil really is in the details. The devil in this case was hiding a 90! constant under an abstraction. Jerk!
Abstractions lie. Be prepared to deal with it.
Or, in the oft spoken words of Tim Gunn, “Make it work.”