Inflector fun with datamapper and merb

Uh, really awkward. I've been posting so little that i've actually forgotten what the standard post structure is like, and am now forced to steal despicably from my other posts. I'm a dirty thieving bastard :(

But, i digress.

As of late, I've been messing around with merb for a personal project of mine, as old men do in the privacy of their own homes. Being the original fellow that we all know i am, my project has nothing to do with lolcats. Wanting to enforce that as thoroughly as possible, i decided to send a clear message to everybody, and screw up the inflector for 'lolcat'.

Long version ( filled with passion and romance )

It seemed like a good idea at the time, although i must admit, alcohol was involved.

Lawfully, i read the init.rb file, and found the code i was craving:

# Here we define erratum/errata exception case:
#
# Language::English::Inflector.word "erratum", "errata"

"If it works for a fancy word like that, it's sure to work for 'lolcat' !", i thought, conspiring to the demise of humorous felines. Alas, having uncommented that piece of code, i was greeted by my old friend, the shit storm. Do say hello

/Users/zmack/Projects/omgpron/config/init.rb:133: uninitialized constant Language::English::Inflector (NameError)
        from /Library/Ruby/Gems/1.8/gems/merb-core-0.9.4/lib/merb-core/bootloader.rb:304:in `load'
        from /Library/Ruby/Gems/1.8/gems/merb-core-0.9.4/lib/merb-core/bootloader.rb:304:in `load_initfile'
        from /Library/Ruby/Gems/1.8/gems/merb-core-0.9.4/lib/merb-core/bootloader.rb:253:in `run'
        from /Library/Ruby/Gems/1.8/gems/merb-core-0.9.4/lib/merb-core/bootloader.rb:65:in `run'
        from /Library/Ruby/Gems/1.8/gems/merb-core-0.9.4/lib/merb-core/server.rb:51:in `start'
        from /Library/Ruby/Gems/1.8/gems/merb-core-0.9.4/lib/merb-core.rb:87:in `start'
        from /Library/Ruby/Gems/1.8/gems/merb-core-0.9.4/bin/merb:12
        from /usr/bin/merb:19:in `load'
        from /usr/bin/merb:19

Dug around a bit, turns out the class was Language::English::Inflect. No worries, we just change it to that and it does the trick. Well, not really.

>> "locat".pluralize # omg omg ?
=> "locats" # gnoes :(

Elegantly, i break out the hammer, and mightily unleash it upon the unsuspecting merb

require 'ruby2ruby' # => true
"lolcat".method(:plural).to_ruby # => "def plural\n  English::Inflect.plural(self)\nend"

What is this treachery ? I was strictly instructed to use Language::English::Inflect and now this doppelgänger comes to haunt me ?

The trouble is, apparently, that Datamapper's extlib, being the eager little beaver that it is, also implements a pluralize method, and uses the english gem with no namespacing.

Using English::Inflect in init.rb results in more fail, as Datamapper apparently isn't loaded by that time. The thing that came to my mind by this point was to just stick the inflector rule in a model. If it's loading a model, Datamapper is bound to be loaded, wouldn't really make sense to be in the model without Datamapper, so our Inflector is bound to be in reach down there.

And now, after a hard day's work, it is finally time to admire the fruits of our labor

"lolcat".pluralize # => "lolyourmum"

Awesome !

Short version

If you're using merb and want to add a custom inflector, the init.rb comments are bunk, and what you have to use is

Language::English::Inflect.word "erratum", "errata"

Moreover, if you're also using Datamapper, the method on String gets monkeypatched, so you need to patch DM's inflector as well, by sticking the following in one of your models ( well, _near_ them anyway )

English::Inflect.word "erratum", "errata"

class SomeModelOmg

Today is not today

Hello there, kind and generous readers. It's been a while since my last post, and i'm quite sure i have been sorely missed. I would really like to say something along the lines of 'the past few months have been really really hectic and i have been immensely busy' but that would be somewhat untrue. The fact of the matter is that i've been doing lots and lots of javascript nothing really seemed that blogworthy i'm a lazy slob.

But from today onwards, i have decided to be a lazy slob no more ! Regular, coherent blogging here i come ! Well, mostly regular, can't quite guarantee coherency on my part. But, i digress. Today's topic is the lovely rubygems and the most disturbing fact that in version 1.0.1,

Today is not today

Just so you know what i'm talking about, in case you have rubygems 1.0.1 installed and ruby 1.8, fire up irb and do the following :

require 'rubygems'
Time.today == Time.today
The thing that'll probably show up will most likely be something along the lines of "=> false". There is a slim chance that it'll return true, but more on that later. Actually, scratch that; more on that now. If you turn that lovely today into a float, you'll be greeted by a lovely bunch of numbers. They may be quite similar to the following:
irb(main):003:0> Time.today.to_f         
=> 1201384800.62603
What may have already gotten your senses tingling should be that floating point part. What's that doing in our upstanding, fixed, and morally responsible time for the present day ? Does our day start at a random millisecond ? Nay, i say ! It does not.

Ok, so we know what the problem is, namely that somebody forgot to trim the floating point part, and we know roughly where it is, namely somewhere inside rubygems. After some grepping, you should find this little gem ( very subtle pun ! ) inside specification.rb:

# Time::today has been deprecated in 0.9.5 and will be removed.
if RUBY_VERSION < '1.9' then
  def Time.today
    t = Time.now
    t - ((t.to_i + t.gmt_offset) % 86400)
  end unless defined? Time.today
end
This is your typical good news - bad news scenario. The good news is that this thing's getting removed, the bad news is that it's buggy, it's in your code, and it's making the very fabric of time collapse upon itself. The way to fix it is quite complex, and instead of giving a rather lengthy and enlightening explanation of its inner workings, i'll just paste the fixed code.
# Time::today has been deprecated in 0.9.5 and will be removed.
if RUBY_VERSION < '1.9' then
  def Time.today
    t = Time.now
    t - ((t.to_f + t.gmt_offset) % 86400)
  end unless defined? Time.today
end
Having read all that reworked and finely retuned code, please take a moment to grab a cup of tea, watch a video, or do whatever you find most relaxing. The last thing i would like to do is strain the honorable reader.

So that's basically it. I've submitted the bug here, and in case you'd like to fix your version before the new one comes out, look for specification.rb in a folder resembling "/usr/lib/ruby/site_ruby/1.8/rubygems". It doesn't have to be identical to that, but that "site_ruby" part is pretty much a given.

A mighty factory worker

In my last post i ranted slightly about having a factory instead of fixtures and why i think it's worth doing. The problem with Dan Marges' Factory is, however that it tends to create a whole lot of junk you don't need.

Allow me to explain. Let's take this snippet

def self.create_newspaper(attributes = {})
  default_attributes = {
    :customer => create_customer,
    :headline => "Read all about it!"
    :paperboy => create_paperboy
  }
  Newspaper.create! default_attributes.merge(attributes)
end
Let's say we had a paperboy already created in this particular situation and we passed it to the method via the attributes param. After this call, like it or not, we'll be stuck with two paperboys. Add to this validations and the fact that the point of factory is to write less test data than you would with fixtures and you'll most likely see my point.

So let's recap what we'd like. We'd like something that creates all attached models to the one we call, and we'd like it to do so without screwing up every single thing in the universe. Sounds easy enough.

Let's say we have pickle which belongs to jar which belongs to shelf. When we call create_shelf we'd like our mighty class to also create pickles and jars to go along with our nice green shelf.

Now at the time i had realised the simplicity of my needs, i had already gone into a great deal of trouble and writing to order my functions in a 'factory' sort of way. I had create_pickle, create_jar and create_shelf all ready, and waiting for my signal. Not wanting to reformat all my code, i namely my newly-created method 'clever_create'. Its story was as follows

  def self.clever_create(klass, existing = {})
    call_stack = [:pickle, :jar, :shelf]

    create_params = {}

    call_stack.each do |current|
      create_params[current] = existing[current] || eval("self.create_#{current}(create_params)")
      break if current == klass
    end

    create_params
  end
That should pretty much take care of creating your objects for you and return your whole bunch of stuff in one neatly packed hash. The only problem is that, since you're doing a merge on the create_params in your create functions, some of those keys might not have methods in your models. As such, you should change your merge to an exclusive merge, which, oh noes, is not present in the manual ! What are we to do ? One option would be to run around screaming in anguish and dismay until we feint, another would be, as we are in ruby, to just create one.
class Hash
  def exclusive_merge(other)
    self.each_key do |key|
      self[key] = other[key] if other.has_key?(key)
    end
  end
end
So there it is. If there is one already, i couldn't find it because it isn't called 'exclusive_merge'. If there isn't, then it's all good.

There would be more to add, but sadly my alcoholic beverage of choice is getting quite warm by now, and i must continue another time. As such, i bid you farewell and good fortune !

On testing - part one of many

Hey there, people. As you may or may not have noticed, i haven't really been writing all that much for the past few weeks, but now i have returned and am fully prepared to deliver upon the afore-promised bacon !


So i've been reading and writing a lot of tests for an existing application, and the grand conclusion i have come to is that fixtures are rubbish. There are a number of tests one can not carry out without using fixtures, but for the greater part using a Factory-based approach is more fun to both read and write.


The reason i like creating objects in my test rather than having fixtures is that it's easier to keep track of the things i have in my database and it makes the models easier to understand for people who are new to the codebase. For you it may be quite obvious that you need to create a Widget before you create a Kludgel and a Porkchop that need to be linked to a Fricasee and a Frisbee with a diameter greater than 15, but having to swim around the codebase to gather that whole structure would take a lot more time and be considerably more boring than just having it laid out in front of you in a test and being able to play around with.


One thing that doesn't really work that well when you're writing tests for an existing codebase is mocking. You generally want to write code with mocking and ease of testing in mind. What that usually leaves you with is a lot of skinny methods just ripe for mocking, stubbing, and isolating the liver out of your unit tests.


Also, another thing to look out for is that posts containing no code at all look surprisingly smaller than posts containing code. As such, I shall return soon bearing gifts of both code and posts !

Even more metaprogramming

Well, it's been a couple of weeks since i've posted anything and i do feel quite guilty about it. Therefore, i shall post more metaprogramming ! In the project i'm currently working on, it has been my great displeasure to encounter blocks similar to the following

def xml_goats
  configuration_node.root.elements["goats"].text.strip
end
  
def xml_apples
  configuration_node.root.elements["apples"].text.strip
end
  
def xml_cherries
  configuration_node.root.elements["cherries"].text.strip
end

I don't know about you, but to me, that just screams "metametametameta !!!". And it's not just because i'll use any excuse to stick some metaprogramming in my classes, but it genuinely looks like a load of nasty copy-paste malarkey. As such, i proceeded with my evil deeds

class Margerine
  %w[goats apples cherries].each{|x| 
    class_eval(%Q{
      def xml_#{x}
        configuration_node.root.elements["#{x}"].text.strip
      end
    })
  }
end

This by all means pretty much works as expected, and defines all those lovely functions for me. However, it still leaves me feeling a bit empty and unsatisfied. What if someday, somehow, we have more of these functions and they require a different set of method calls on the given node ? What will happen to our beautiful code then ? Will it be thrown to the gutter, like an old mistress with a dirty stocking ? Will it be fed to the wolves, like a lovingly furry squirrel who has eaten one too many hazelnuts ? That would most certainly not do. As such, we must make our code better, stronger, and even more delicious.

class Margerine
  def self.attr_xml_reader(items,submethod = "text.strip")
    items.each{|item|
      class_eval( %Q{
        def xml_#{item}
          configuration_node.root.elements["#{item}"].#{submethod}
        end
      }	)
    }
  end

  attr_xml_reader %w[goats apples cherries], "text.strip.downcase"

end

Now we are indeed satisfied with our work, and knowing that all damsels and squirrels the world over are safe and sound, we rest to code another day

Tasting the sweet nectar of metaprogramming

Metaprogramming is something you hear a lot about when you're around ruby, and with good reason. It's insanely powerful and hands down the most fun you can have with a programming language. Given that it's quite a complex topic, i'll start off slow - custom getters and setters. If you've used ruby for more than 20 minutes, you've probably seen attr_reader / attr_writer / attr_accessor. They're snippets of metaprogramming that just generate methods. Take attr_accessor, for instance. It gets you something to the tune of
def #{name}
  @#{name}
end

def #{name}=(value)
  @#{name} = value
end
But let's say you need something slightly more complex than that. Let's say you're writing an application which features sock possession reporting for animals with silly names. At least in my line of work, that's quite a common requirement. Being the magnificent human being you are, you'd like to write as little as possible. Therefore, you define a module
module Sockless
  def no_socks_for( *names )
    names.each { |name|
      class_eval("
        def #{name}_has_no_socks
          @#{name} + ' is a ghastly beast with no socks'
        end

        def #{name}=(value)
          @#{name} = value*2
        end")
    }
  end
end
Now you just have to extend your desired class with the Sockless module and you're home free
class TheSockZoo
  extend Sockless
  no_socks_for :pig, :sheep
end
Subsequently, you can amaze all sentient beings within a 10 mile radius by just adding these three lines of code and viewing the wonders of modern technology in action
r = TheSockZoo.new
r.pig,r.sheep = "gi","dee"
p r.pig_has_no_socks, r.sheep_has_no_socks

Using block helpers to make your views pretty

I was watching the code review that Jamis Buck and Marcel Molina did for the Mountain West Ruby Conference and one really nice idea that grabbed my attention was using block helpers to separate sections in views.

Let's say you have a view and you need to render a partial with some content which only people who love delicious pickles should see.

<% if @user.loves_delicious_pickles? %>
  <%= render :partial => "pickles" %>
<% end %>
That's perfectly fine, but should your pickle logic get more complicated, you'll be stuck with an ugly looking conditional statement in your view.

The alternative would be to add a helper to your controller and move all your logic in there

def lover_of_delicious_pickles
  yield if @user.loves_delicious_pickles?
end
Which would turn your view into something to the tune of
<% lover_of_delicious_pickles do %>
  <%= render :partial => "pickles" %>
<% end %>
Granted, this isn't the best example but the idea itself is really good, and should at least make your code easier to understand.

More on this here.

Syndicating other people's content for your own selfish use

I've grown to love and adore google reader. It's the only tab that stays open for the entire duration of the day in my browser, mainly because if you subscribe to the right feeds, there's always something good to read. The thing is, feeds have made me lazier than i should be, namely, checking sites for stuff i want has suddenly become a chore. It's like calling the post office every day asking Mr. Wabu, the postal server, if any letters have arrived for you, instead of having the kind-hearted Mr. Wabu drop by your house every day and just deliver your loving bills in your lovely mailbox.

As such, i decided this could not go on any longer. Mr. Wabu had to come by my house. Enter the Ruby !

In case i've been too abstruse up to now, what i mean to do is syndicate the content of a website which, while relevant to me, has no rss feed of its own. For this particular example i'm using Hpricot for the ingenious act of spidering, and the omnipotent builder to generate the xml. I'm quire sure there are a number of gems/libraries out there that can generate rss much easier than builder, but this was the first one that came to mind and it is quite delightful to work with.

Ze Spider

I'd used to do spidering using just open-uri and scan, as i found writing 3-row regular expressions for my desired data a delight. This time, however, i wanted to do something wild, so i went with Hpricot. This involves doing something to the tune of

open(address) { |file|
		doc = Hpricot(file)
		doc.search('td.content table table table tr') { |match|
As you can probably figure, the parameter for search is a css-style selector for a horribly built table layout. We want this field's prized possession to become our own, so rob it of its data and see if it is of any use to us
if ( match.innerText =~ /pickles/ )
Yes, we are looking for pickles.
Upon having found said pickles, we store their title, address and description somewhere, so that we can get them when we build the feed. I used a hash
date = match.children[0].innerText.split('/')
				date = Date.new(2000+date[2].to_i,date[1].to_i,date[0].to_i)
				results << { 	:title => "#{title} #{match.children[2].innerText}",
								:link => match.children[5].search("a").attr("href"),
								:body => match.children[5].search("a").attr("href"),
								:date => date
							}
I was looking into a row, and the sixth child contained the link so, not being interested in the link's inner text, i just used Hpricot to extract the link's href.

Ze Feed

Moving on, let's say we have the results we need, so now we have to generate our feed. I used builder to get this done, mainly because it's easy to understand and the syntax is pretty much gorgeous. The feed is rss 0.91 because i was looking for something really simple, which google reader could understand. If you want to generate some other kind of RSS you can find the specs here.

def midgets_to_rss(midgets)
	xml = Builder::XmlMarkup.new
	xml.rss(:version => "0.91") {
		xml.channel {
			xml.title("Picklez")
			xml.description("Your picklez, foo !")
			midgets.each { |midget|
				xml.item {
					xml.title( midget[:title] )
					xml.link( midget[:link] )
					xml.description( midget[:body] )
				}
			}
		}
	}
end

You have your RSS 0.91 generated and all pretty, just waiting for to be stored somewhere. A number of options present themselves to you now, the main ones being that you either run your script on request as a CGI, or you stick it into a cron task and have it store its results in an XML for you. You could go either way, i chose to go with a cron task that sticks the feed into a file once an hour, because the data i'm intrested in isn't very urgent.

Should you want the source of my awful thief of a script, and together with it the terrible secret of which cartoons i watch, you can find them here

Also, one thing you should note is that in case you're running your script from a shared host and your ruby gems are stored in the local directory, you should probably set the ruby gem home dir in the script before you add your requires and what not.

ENV['GEM_HOME']='/home/#{whatever}/ruby/gems/'