26 Nov 2007, 11:13am
/dev/ruby
by

Comments Off

Ruby: Can a Function Know It’s Own Source File?

I got to wondering this weekend: can a function know about it’s own source file in Ruby? The reason I was wondering this is because I’ve found that when working on someone else’s Ruby source code the ease with which mixins can be made sometimes leads to code that’s very difficult to trace.

Stack a few layers of inheritance together, stick some mixins into each level and then try and locate the original location of my_obj.coerce()… I tend to have to resort to TextMate’s global Find to search through the whole project (and woe is me if it contains multiple coerce() functions in different files). True story, that example.

What I’d like, and haven’t yet been able to discern a means to do, is either:

Kernel::source_file( my_obj.coerce )

or

my_obj.coerce.source_file()

or something along those lines which would spit back:

/var/src/my_proj/modules/coercion_functions.rb

Does this already exist? Am I missing something trivial?

7 Oct 2007, 6:11am
/dev/ruby
by

Comments Off

Ruby: Converting an Array to a Hash

Converting a hash into an array in Ruby is easy but what about an array into a hash? This code does the trick quite nicely, and if you don't want to actually extend the Array class this does as well:

RUBY:
  1. a = ["cat","dog","monkey"]
  2. h = Hash[*a.collect { |v| [a.index(v), v] }.flatten]

Update: Speaking of mucking about with arrays, ArraySugar adds a few nice shortcuts to arrays. I like the select syntax (but it seems like overkill to install this as a gem).

12 Jun 2007, 1:28am
/dev/rails /dev/ruby
by

Comments Off

The Power of rake

rake is one of those little things about Ruby on Rails that I suspect most of take completely for granted, running the odd rake tast without really ever thinking much about it. Turns out rake is pretty damned cool.

Gregg over on Rails Envy has written a very nice intro to rake in Ruby on Rails Rake Tutorial (aka. How rake turned me into an alchoholic) :

In this article we're going to discover why Rake was created, and how it can help our Rails applications. By the end you should be able to write your own tasks, and learn how to get piss drunk using rake in no less then three steps.

He's written well enough that by about midway down the article I was already thinking of ways to use rake to do some of the unpleasant maintenance tasks every site needs that I was consequently putting off thinking about. rake: not bad at all.

1 Jun 2007, 1:55pm
/dev/ruby
by

2 comments

Using a hash to count incrementally

A hash is great for keeping track of counts of things if you we already know what those things are, but how to add a new thing without a bunch of cumbersome code? Use fetch():

RUBY:
  1. count[item] = count.fetch(item, 0) + 1

If item already exists then it will add 1 to it. If item doesn't exist, it'll initialize it with a value of 0 and then add 1 to it.

19 Apr 2007, 1:54am
/dev/ruby
by

2 comments

More Trivial Scripting with Ruby

Last Friday, in Trivial Scripting with Ruby on O'Reilly Ruby, Gregory Brown demonstrated a two-line example of the ease with which simple utility scripts can be created from Ruby. In his case a script that takes a filepath and returns the contents with all HTML entities escaped.

I like it, but I don't have any use for this operating on a per-file basis. More useful to me is being able to pass in the HTML text via command line args:

RUBY:
  1. #!/usr/bin/env ruby
  2.  
  3. require "cgi"
  4. ARGV.each { |x| puts CGI.escapeHTML( x ) }

I saved this as esc.rb. To use:

Sagarmatha:~/Documents/utilityscripts chris$ ./esc.rb "<em>Hello & World</em>"
&lt;em&gt;Hello &amp; World&lt;/em&gt;

19 Apr 2007, 12:00am
/dev/ruby
by

4 comments

Updating MySQL Gem to 2.7

While updating my installed gems this evening I ran into an issue with the mysql-2.7 gem:

ERROR: While executing gem ... (Gem::Installer::ExtensionBuildError)
ERROR: Failed to build gem native extension.

ruby extconf.rb update
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lm... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lz... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lsocket... no
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lnsl... no
checking for mysql_query() in -lmysqlclient... no

The solution was to explicitly tell gem where the local MySQL install is:

Sagarmatha:~ chris$ which mysql
/usr/local/mysql/bin/mysql

Sagarmatha:~ chris$ sudo gem install mysql -- --with-mysql-dir=/usr/local/mysql

Caveat: Note the double -- between mysql and --with. That's not a typo (though it may look like a single dash in this page in some browsers).

9 Apr 2007, 12:31am
/dev/rails /dev/ruby
by

Comments Off

Ruby Jobs

Looking for a job working with Ruby and/or Rails?

http://jobs.rubynow.com/

Even better, there's an RSS feed: http://jobs.rubynow.com/rss/feed.xml

(Note to my current co-workers: no worries, I'm not looking, honest)

(Thanks Sam!)

4 Apr 2007, 2:58pm
/dev/rails /dev/ruby
by

2 comments

English-friendly timespan in Rails

Awhile ago I was looking for a function that would convert a number of seconds into a human-readable timespan string. For instance: convert 86400 into 1 day and 172800 into 2 days, accomodating intervals ranging from weeks to minutes.

I didn't find one. Instead, this is what I came up with this, which at its fanciest will output something like 2 days, 14 hours and 15 mins.

RUBY:
  1. # Takes a period of time in seconds and returns it in human-readable form (down to minutes)
  2. def time_period_to_s time_period       
  3.   out_str = ''
  4.      
  5.   interval_array = [ [:weeks, 604800], [:days, 86400], [:hours, 3600], [:mins, 60] ]
  6.   interval_array.each do |sub|
  7.     if time_period>= sub[1] then
  8.       time_val, time_period = time_period.divmod( sub[1] )
  9.          
  10.       time_val == 1 ? name = sub[0].to_s.singularize : name = sub[0].to_s
  11.            
  12.       ( sub[0] != :mins ? out_str += ", " : out_str += " and " ) if out_str != ''
  13.       out_str += time_val.to_s + " #{name}"
  14.     end
  15.   end
  16.  
  17.   return out_str 
  18. end

Obviously it's hard-coded only to support english and ignores seconds (too fine-grained for me) but over-coming either of those limitations should be fairly straight-forward.

Update: I terminated the upper value at weeks because things get complicated when we get to months. We can all agree that a day is 24 hours, a week is 7 days, but how long is a month? Not four weeks, not five weeks.... Predictability dies at months, where the actual timespan becomes relevant, and that's more work than is worthwhile at this point. Feel free to embrace and extend :)

15 Mar 2007, 4:24pm
/dev/rails /dev/ruby
by

Comments Off

Class Objects from Strings

Or "Let the code write the code".

One of the beauties of Rails is that the "convention over configuration" mandate means that not just program execution but program implementation are extremely predictable. This is very handy when you'd prefer to have your code write the code, as I needed to this afternoon when I wanted to call the same function on a bunch of different classes who's names were available for the operation as an array of strings.

I first started out by spending far too much time mucking around with eval() but that felt kludgy and I've never liked using eval() in any language - I'm naturally gun-shy when it comes to arbitrary code execution. I may know all the code paths into that point today but what about six months from now? Will I always remember never to pass user data through there accidentally?

A little more reading of the Kernel module and the Module class revealed a much cooler and ultimately much simpler way to implement the same functionality: const_get().

In a nutshell:

RUBY:
  1. begin
  2.   the_obj = Kernel.const_get( ( activity.model_name ).camelcase )
  3.   activity_events = the_obj.find( :all )
  4. rescue
  5.   the_obj = nil
  6. end

Much nicer but still not as nice as I'd like since I've had to wrap it in the begin / rescue block. For reasons unexplained const_defined? always returns false when used in here.

Note: Some googling afterwards showed up this very nice example by Matt Biddulph in which he used const_get() to walk the ActiveRecord models and create OmniGraffle diagrams of their relationships. That's a nice mash-up.

14 Mar 2007, 3:11pm
/dev/rails /dev/ruby
by

Comments Off

Converting Seconds to Timeclock Display

This seems like functionality that should already be built into a Ruby class (Time?) or a Rails helper but I couldn't find it. This simply converts a chunk of seconds into hh:mm:ss display:

RUBY:
  1. time_str = [ seconds / 3600, seconds / 60 % 60, seconds % 60].map { |t| t.to_s.rjust( 2, '0' ) }.join( ':' )

which outputs 02:44:13.

What I'm really looking for is a conversion from seconds into the likes of

1 day, 8 hours, 44 minutes, 11 seconds

similar to what the Rails DateHelper does with distance_of_time_in_words. If you know of such a thing, please point me to it.