One day I read a story called the Legend of the Ferret. It seems, at first, like a mighty sad tale but the ferrets have gotten to be experts at scurrying through Mouse Holes looking for things. In fact, I keep a pet ferret myself, just for this finding ability. His name is Herbert and he searches my web history.

Would you like your own pet ferret? I'm sure you would, they're very handy. Start by grabbing a ferret gem:

gem install ferret -y

Before your new friend can move, you'll need a place for your it to store its pages. Here you can borrow my spare den for the time being, until you build your own personalised one. Just save this code into ~/.mouseHole/petferretden.rb:

# petferretden.rb - web history indexer for MouseHole 2
#
# Your pet ferret collects web pages in its den and makes them searchable!
# You'll also need the ferret itself, petferret.rb.
#
require 'ferret'

class PetFerretDen < MouseHole::App
  title "Pet Ferret Den"
  namespace "alex@mugofgrog.com"
  description ("Your pet ferret collects pages from your web browsing and stores them in its den. " +
               "Golly, it's a mess!")
  version "0.1"
  + url("*")

  def untangle(s)
    s = s.gsub(/<(script|style).*?<\/[^>]*>/m, '')
    s.gsub!(/<[^>]*>/m, ' ')
    s.gsub!(/&[^ ;]*;/m, ' ')
    s.gsub!(/\s+/, ' ')
    s
  end
  
  def rewrite(page)
    Ferret::Index::Index.new :path => '+pet_ferret_index' do |index|
      title = untangle $1  if page.body =~ /<title[^>]*>([^<]*)<\//m
      title ||= page.location
      index << { :id => page.location,
                 :title => title,
                 :content => untangle(page.body) }
    end
  end
end

Next you'll need the pet ferret itself, save this code in ~/.mouseHole/petferret.rb:

# petferret.rb - web history search frontend for MouseHole 2
#
# What do you get when you put a ferret in a mouse hole?  Why a personal search
# engine of course!
#
# You'll also need the indexer part, petferretden.rb.
#
require 'camping'
require 'ferret'

Camping.goes :PetFerret

PageSize = 10

module PetFerret::Controllers
  class Index < R '/'
    def get
      if @input[:q]
        @ferret = Ferret::Index::Index.new :path => "+pet_ferret_index"
        parser = Ferret::QueryParser.new(:or_default => false,
                                           :fields => ['title', 'content', 'id'])
        query = parser.parse(@input[:q])
        
        @start = 0
        @start = @input[:start].to_i if @input[:start]
        @topdocs = @ferret.search query, :offset => @start, :limit => PageSize
        
        @total = @topdocs.total_hits
        @end = [@start + PageSize, @total].min
        @start = 0 if @start + 1 > @end
        @results = @topdocs.hits
          
        render :results
      else
        render :index
      end
    end
  end
end

module PetFerret::Views
  def layout
    html do
      head do
        style do
          self << '.result a { display: block; }'
          self << '.result span { color: #55f; display: block; }'
        end
      end
      body do
        h1 "Pet Ferret"
        self << search_box
        self << yield
      end
    end
  end

  def search_box
    form :action => R(Index) do
      input :name => :q, :value => @input[:q]
      input :type => :submit, :value => "Go ferret!"
    end
  end
 
  def index
    p { em "Your ferret idly chews on an ethernet cable while awaiting your command." }
  end
  
  def results
    p do
      strong "Whoosh! "
      em "Your ferret swiftly returns, eagerly dragging some slightly-chewed pages."
    end
    
    p "Results #{@start+1} to #{@end} out of #{@topdocs.total_hits} for \"#{@input[:q]}\""

    for hit in @results
      doc = @ferret[hit.doc]
      p :class => :result do
        a doc[:title], :href => doc[:id]
        self << @ferret.highlight(@input[:q], hit.doc, :field => :content)
        span "#{doc[:id]}"
      end
    end

    if @end < @total
      a "Gimme more...", :href => "?q=#{CGI.escape @input[:q]}&start=#{@end}"
    else
      p "No more results. :-("
    end
  end
end

Startup your Mouse Hole and visit a few pages. If your ferret has settled in happily you should be able visit him at http://mh/petferret/ and he'll be able to find all your pages again.