Author Archive

Pretheory.next

June 1, 2008

Some people have been wondering what Dan and I are up to these days. For various reasons, we wanted to keep quiet about our new project until now.
We’re currently working hard on Devver, which will provide web-based services for Ruby developers. You can keep up with our adventures on our new Devver blog. Since Pretheory/Seekler is on the back burner for the foreseeable future, we won’t be updating this blog, so be sure to check out our new one.
We’re both really excited about this project and can’t wait to release more details about it as it matures. The really great news for us is that TechStars decided to include Devver as one of its teams for its Summer 08 program. We’re incredibly honored that we were chosen and are super pumped about participating in the program.

Moving on…

April 18, 2008

Well, it’s been an incredibly busy month since we last posted on this blog. In addition to coding and pursuing funding, we’ve been thinking hard about our future here at Pretheory.
After a lot of thought, we’ve decided that Seekler, while interesting, is probably not going to be become a viable business for us in the near future. As such, we’ve decided to pursue an entirely new project.
What will happen to Seekler? Our plan is to put it into maintenance mode. We’ll fix critical bugs but won’t be adding any new features for the foreseeable future. We may revisit the project in the far future, but for now, it’ll stay in its current state.
We apologize to those of you who are using Seekler and want us to continue development. We appreciate your support and we don’t make this decision lightly.
But at this point, we feel the best decision is to take all of the lessons we have learned while creating Seekler and pour them into our new venture. This new project is still in stealth mode, but we’ve secured some early funding and have gotten good feedback about the core product. Watch this space for links to more information about this exciting new development.
Finally, I’d like to thank all of our friends, family, and other alpha and beta users who gave us feedback, linked to Seekler, and generally gave us support. We certainly could not made it this far without you.

Finding and Fixing Strange Rails Bugs (or, How I Wasted My Afternoon)

March 13, 2008

Rails is a great framework, but sometimes bugs can be incredibly tricky to track down. Case in point, I spent a significant portion of yesterday tracking down a very strange bug. I’ll post the resolution – but perhaps of more general interest, I’ll outline some tips that you can use when tracking down Rails issues (or really, any issues). These tips may be obvious, but sometimes in my desire to quickly fix a bug, I forget the most basic rules of debugging.
Short Story: Ruby Classifier requires ‘mathn’. As it turns out, requiring ‘mathn’ breaks redirect behavior in Rails. Check out Rails ticket 5433 for more info and a workaround.
(Very) Long Story:
The initial behavior I noticed was that one integration test was failing. The integration test was doing the following


post_via_redirect '/account/login', options
assert_response :success

And was failing with the following error:
Expected response to be a , but was <302>
Not so weird in itself, but the bug only appeared when I ran the tests using autotest. When I ran the test via rake test:integration, everything worked. I looked at the test and the controller and everything seemed normal (and I hadn’t changed anything in the test or in the controller lately), so I started to dig in deeper.
Tip 1: Isolate the changes. My first step was to use svn to track down the exact revision that had caused the bug (I hadn’t caught the bug before checking in because I had been testing using rake, not autotest). Luckily, this is pretty simple – just keep performing svn update -r REVISION_NUM and running the test until you find the revision that causes the test to fail (I wish I had a tool to do this automatically…).
However, in my laziness, I’d forgotten to …
Tip 2: Minimize the Repro. This really should be tip 1, but I’m telling this story chronologically, so you can learn from my mistakes. As I said, this bug only reproed when using autotest. Each time I wanted to test for the bug, I had to wait almost a minute for autotest to run the tests. Due to this long repro, I ended up spending probably ten times the amount of time I needed to figure out which checkin caused the bug.
Usually, when a test fails, you can just use ruby to run a single test. But if you ever have a bug that only seems to repro in autotest, remember that autotest prints out all the commands it runs. In this case, it was simply a case of copying and pasting the code (which ran a ton of test files) from the output of autotest onto the command line. It looked something like this:


ruby -I.:lib:test -rtest/unit
-e "%w[test/functional/resources_controller_test.rb
test/functional/account_controller_test.rb test/unit/merge_test.rb
test/unit/render_state_test.rb test/integration/filter_clears_test.rb
test/unit/item_test.rb test/functional/admin_items_controller_test.rb
test/unit/comment_test.rb test/unit/lists_helper_test.rb
test/functional/lists_controller_test.rb test/functional/items_controller_test.rb
test/unit/list_test.rb test/unit/memory_list_test.rb
test/integration/contact_form_test.rb test/unit/xpath_test.rb
test/functional/contact_controller_test.rb test/unit/resource_test.rb
test/unit/membership_test.rb test/unit/list_classifier_test.rb
test/functional/listgroups_controller_test.rb test/integration/resources_test.rb].each
{ |f| require f }" | unit_diff -u

Yikes. That successfully reproed the bug, but it took way too long. The next step was manually pulling out test files until the bug stopped reproing. That left me with a simpler (and much faster) repro:
ruby -I.:lib:test -rtest/unit -e "%w[test/unit/list_classifier_test.rb test/integration/resources_test.rb].each { |f| require f }"
OK, so it seemed that there is some weird interaction between list_classifier_test and resources_test.
Tip 3: Simplify, Simplify This is really an extension of tip 2, but I’m repeating it because most of debugging is systematically simplifying the problem by removing complicating factors. So, I went to list_classifier and commented out all the tests. The bug disappeared. I put a single test back in – the bug reappeared.
Now I was onto something. I then realized that loading the file under test (list_classifier.rb) was the problem. That code is based on the awesome Ruby Classifier library. As a result, the file begins with require 'classifier'. Interesting. So I tried just requiring ‘classifier’ in the integration test. Bingo. Somehow, using the Classifier library was breaking my integration test.
Tip 4: Follow your gut, but validate. For nasty bugs like this, the interactions are so complicated it would take forever to just step through the code and find the bug. So, you have to develop a gut feeling for what might be causing the bug and follow that first. Of course, you have to develop tricks to quickly confirm or reject your hunches so you don’t go down the wrong path for an hour.
For instance, my initial feeling was that monkey patching was to blame. Specifically, I noticed that both Classifier and Rails had defined a ‘sum’ method on Array (actually, Rails defines ‘sum’ on Enumerable, but Array includes Enumerable). A quick check confirmed that Classifier’s ‘sum’ had overwritten Rails’ ‘sum’ by the time my integration test ran. But I wasn’t sure if the Rails code actually ever called this method.
To check this, I used ruby-debug. I first set a breakpoint right before the troublesome line in the integration test. At the debugger prompt, I turned on trace mode (trace on all) and then continued (c). Trace mode prints out every single line of code that is executed, including the file in which the line appears. It was simple enough to copy/paste the trace into Emacs and search for any line executed in the ‘classifier’ library.
As it turns out, I didn’t find anything. My hunch was wrong – Classifier’s ‘sum’ method wasn’t to blame. But luckily, it didn’t take me too long figure it out.
So, I went back to simplifying the repro. Instead of doing require 'classifier' (which includes the whole gem), I started including specific files from the gem. After doing this for awhile, I found that only requiring the file ‘classifier-1.3.1/lib/classifier/extensions/vector.rb’ caused the bug.
I tried to simplify even more. That file starts with both require 'matrix and require 'mathn'. Could one of those be the problem? I tried requiring each of those files at the top of the integration test and sure enough, require 'mathn' caused the problem.
Tip 5: Google is your friend. All throughout this long process, I’d been Googling to see if anyone had solved this issue. Of course, until the end I didn’t really know what the root cause was, so my searches turned up nothing useful. However, after narrowing it down to integration tests and mathn, I found a this Rails ticket (search ‘rails integration test mathn’). As it turns out, mathn changes the behavior of integer division, which makes the code return the wrong HTTP status code. And the reporter includes a simple, one-line patch which is easy to apply.
In retrospect, it all seems to simple (and in fact, it’s frustrating that it took me so long to solve). When tracking down a bug, it’s very easy to get frustrated or overwhelmed with the sheer size of the search space and forget basic debugging practices. Although tracking down bugs will always remain somewhere between science and art, these tips should help you the next time you encounter a real head-scratcher.

The Maybe Monad in Ruby

February 14, 2008

| [tests]
I’m a little behind on my goal to learn a new programming language each year – I’m still working on really understanding last year’s language, Haskell. Judging from the number of tutorials online, understanding monads is one of the hardest parts of learning Haskell.
I won’t even try to explain monads to you, because (as this post will reveal), I’m still a bit shaky on them myself. However, I will say my attempts to build a Maybe monad in Ruby have helped me learn more about Monads – and have yielded a pretty useful piece of code.
Why should you care about the Maybe monad?
Have you ever written code like this?

if(customer && customer.order && customer.order.id==newest_customer_id)
# ... do something with customer
end

Don’t you wish there was just a way to call customer.order.id without all those intermediate checks for nil objects?
One solution is to obey the Law of Demeter – in other words, to change the call to customer.order_id. Two problems – one, sometimes obeying the LoD is more work than its worth (especially for quick and dirty solutions) and two, it still doesn’t help you if customer is nil.
Still not convinced there is a problem worth solving here? Check out better explanations by Oliver Steele or Reg Braithwaite – they explain the problem better than I did (and present some interesting solutions).
Anyway, here is my solution – a Maybe monad in Ruby. You would change the above code to:

if(Maybe.new(customer).order.id.value==newest_customer_id)
# ... do something with customer
end

How does this work? Without going into too much theory about monads, the basic idea is that Maybe is a container object. It stores (or ‘wraps’) any value and then controls how methods are called on the wrapped object.
More concretely, the call to Maybe.new wraps a value in a Maybe object. Whenever you call methods on the Maybe object, it does a simple check: if the wrapped value is nil, then it returns another Maybe object that wraps nil. If the wrapped object is not nil, it calls the method on that object, then wraps it back up in a Maybe object.
Confused? Here’s a few examples:

>> Maybe.new("10")            # => #<Maybe:0x34cbd5c @value="10">
>> Maybe.new("10").to_i       #=> #<Maybe:0x34c919c @value=10>
>> Maybe.new("10").to_i.value #=> 10
>> Maybe.new(nil)             #=> <Maybe:0x34c4408 @value=nil>
>> Maybe.new(nil).to_i        #=> #<Maybe:0x34c2644 @value=nil>
>> Maybe.new(nil).to_i.value  #=> nil

I’ve started to use this in my code on Seekler and it’s definitely helped me clean up some messy conditional code quite a bit. Try it out and please let me know if there is any way I can improve it. You can find the source code here. You can also check out the tests.
All in all, this project was a lot of fun, helped me understand monads better, and showed me how a powerful concept from Haskell can be a very useful concept in Ruby.
Update: Thanks to the help from people commenting on this post (as well as some help from James Iry), I think I’ve solved the problem I describe below. It turns out I was confusing two different methods that monads should have: fmap (which takes any function and applies it in the monad) and pass (which takes a function that returns a Maybe and applies the wrapped value to it). I’m leaving the issue below in the hopes that others who may be confused can learn from it.
There is one big problem with my code (and it reveals my ignorance regarding monads): this monad doesn’t seem to meet the third monad law. In other words, the following test fails

  def test_monad_rule_3
f = Proc.new {|x| x*2}
g = Proc.new {|x| x+1}
m = Maybe.new(3)
assert_equal m.pass{|x| f[x]}.pass{|x|g[x]}.value, m.pass{|x| f[x].pass{|y|g[y]}}.value
end

I learned about the monad laws from ‘Monads on Ruby’ on Moonbase, but I don’t yet understand how the third monad law works (in his examples at the bottom of this page). If any wise Ruby hacker (or Haskell hacker) can help me bridge this gap in my understanding, I’d really appreciate it.
For the interested reader, there’s some great information out there about this problem in general and Ruby monads in particular.
MenTaLguY has a great tutorial on Monads in Ruby over at Moonbase
Oliver Steele explores the problem in depth and looks at a number of different solutions
Reg Braithwaite explores this same problem and comes up with a different, but very cool solution in Ruby
Weave Jester has another solution, inspired by the Maybe monad
Update: James Iry has a great explanation of monads from the Scala perspective. His explanation of the monad laws (and the difference between map and flatMap, as they are called in Scala, really helped me out)

A New Resource For Colorado Entrepreneurs

February 6, 2008

For the months before we launched Seekler, we were basically holed up in our house, coding night and day. Since we launched, we’ve been making an effort to engage more with the local entrepreneur community. What we’ve found is that this area (Boulder in particular, but Denver as well) has a great ecosystem for tech startups.
Some of our friends in this community recently came up with a great idea – to try and collect all of the knowledge that tech entrepreneurs need to get started in one place – stuff like recommendations on lawyers, accountants, banks, events, health insurance, etc.
This made a lot of sense to us – after all, we spent a lot of time getting Pretheory up and running, precisely because we didn’t have such a resource. And setting up the nuts and bolts of a company is pretty boring – you want to do that stuff as quickly as possible, so you can get to the interesting part – building a technology that people will love.
So, if you’re in the Denver/Boulder area and are looking to found your own startup, I encourage you to check out the Boulder Tech Bootstrap. If you are already up and running, I encourage you to go add some information to help others.
We’ve added some info to the site – and we also have created a few lists that we hope Colorado tech entrepreneurs will find useful. Check them out and let us know if they help you!
Best Boulder/Denver Startup Blogs
Best Startup Events in Boulder/Denver, CO
Seed Funding Opportunities (ideally, we’d like you to stay in the area, but you should explore all your options for funding…)

Creating Super Bowl Lists in Real Time at Seekler.com

February 3, 2008

Today we’re trying something new at Seekler – we’re creating a list of the best Super Bowl commercials of 2008 in real-time while we’re watching the game. If you want, feel free to join us and create your own list of favorite commercials as you watch the big game!
We’ve also got a list of the best Super Bowl commercials of all time that you can vote on as well. Check it out!

Good news for Seekler

January 22, 2008

We have always known that consumer opinions about products and services was an interesting and profitable area to be in. For example, according to some recent statistics in Entrepreneur magazine*:
“62% of shoppers read consumer-written product reviews online.
80%+ say their purchase decisions have been directly influenced by reviews.
70% of shoppers share product reviews with their friends, family, or colleagues.
18% of consumers say they look for more product information online or in other locations in addition to on the product’s packaging.”
Since Seekler’s community-built lists provide an incredibly easy way for shoppers to find and share their opinions about products, this means that
62% of shoppers could be looking at Seekler lists.
80%+ could have their purchase decisions influenced by Seekler lists.
70% of shoppers could be sharing their lists with friends, family, and the whole Seekler community.
18% of consumers could find product information at Seekler.
We know that’s we’re a tad bit optimistic, but it just goes to show how many people are using existing review sites and could find new products and services more quickly and easily by using Seekler.
*February 2008 issue, page 26, source of stats not cited

A new Seekler niche: video games

January 17, 2008

We had some great successes last week promoting our list of the most anticipated movies of 2008, but as that traffic dies down – we’re looking to promote one of our fastest-growing niches: video games. Seekler has a great list of the best video games of all time as well as a list of the best free computer games.
But my favorite is definitely our list of the best online flash games. If you are looking to ease your boredom at work or school, definitely check this list out. But be warned – I unintentionally spent over three hours last night playing these games. Some of them are very, very addictive!

Seekler beta: exciting first week & more press

January 14, 2008

Just a few quick announcements:
Last week was a great one for Seekler. Just a few days after we launched our beta, we got an incoming link from FARK.com’s showbiz section, which brought us a ton of new visitors. Sorry if the site slowed down a bit – we’ll be addressing that this week.
Secondly, Seekler got another nice post at Mashable. Thanks, Kristen!

Seekler (beta) launches!

January 7, 2008

After many months of hard work, we’re proud to unveil the beta version of Seekler. We’ve added lots of features since we released our alpha version, but the most important one is that as of today, user registration is completely open to everyone!
We’ve only gotten this far because of our amazing alpha users and we will continue to be dependent on the support of users like you. If you can, doing any of the following would help us out immensely:
1. Use Seekler! Seekler will be able to attract more users as there is more and more useful data on the site. Making a new list or adding to an existing list would be a huge help. Also, you can add reviews to items on your existing lists to give them more depth.
2. Give us feedback! Seekler is far from finished, but we will continue to need your help to improve it. What features do you wish we had? What tasks are painful to complete? What lists do you wish we had?
3. Tell your friends! If you’ve been waiting to tell your friends about Seekler because it wasn’t easy to create an account, wait no more. Anyone can easily create an account and start making lists.
4. Link to us! Incoming links will help us get traffic and improve our position on search engines like Google. If you have a website, a blog, or even a MySpace of Facebook profile, and incoming link to Seekler would be very much appreciated. You can link directly to http://seekler.com or to a list that interests you. If you’re into movies, a link to our “Anticipated Movies of 2008” list would really help us out since we’re currently trying to promote that list.
Thanks!