A Ruby string analyser
The first thing I noticed is that you've defined methods such as word_occurrences, which do nothing but set instance variables (@word_occurrences, etc.). This is a bit redundant, and it would be better to do one of two things:


Define all of your instance variables under the initialize method. or
Take out the @word_occurrences ||= part of the method definition.


I think from a performance standpoint, it might be better to take the first approach, since you don't want to have to calculate word_occurrences every time you call it as a method. I also noticed that you only use @words and @filter once, and that's to define filtered_words, so you can make things simple by just defining @words to be what filtered_words is currently. See below:

def initialize(text, filter)
  @words = text.split.reject {|word| filter.split.include? word.downcase}
  @occurrences = @words.each_with_object(Hash.new(0)) {|word, result| result[word] += 1}
  @lengths = @words.each_with_object({}) {|word, result| result[word] ||= word.length}
end


I've made @words the new filtered_words, shortened word_occurrences to just @occurrences (and made it an instance variable so that we're only evaluating it once, upon initialization of the instance), and added another instance variable @lengths, which represents a part of your longest_words function, a hash of each word and how long it is.

Also, notice that I simplified your use of inject to create a hash of word occurrences and word lengths, by using the handy each_with_object function instead.

I also changed = to ||= for the part where you're building up a hash of word lengths. I believe this will be better for performance, as you're not re-calculating the word length for words you already have.

You mentioned duplication between longest_words and highest_occurring_words -- you could solve this by pulling out the part you reused into a private function, which I would call highest_ranking:

private

def highest_ranking(entries)
  # Takes a hashmap of the form {"foo" =&gt; 100, "bar" =&gt; 45} and returns an array
  # containing the entries (array-ified) with the highest number as a value.
  entries.group_by{|word, occs| occs}.sort.last.last
end


I simplified max_by{|key, value| key}.last to just sort.last.last -- it turns out that Enumerable#sort knows how to properly handle sorting by keys, so getting the highest one is just a matter of sorting the hash and grabbing the last item in the resulting array. (The extra last is there for the same reason the last is there in your function -- to drop the number of occurrences and just return the key/value entries in the result array)

Now that you have this useful helper function, finding the highest occurring words and longest words is simple:

public

def highest_occurring_words
  highest_ranking @occurrences
end

def longest_words
  highest_ranking @lengths
end


Finally, I would refactor your html_list function like this:

def html_list
  list = word_occurrences.sort_by {|word, occs| occs}.reverse.map do |word, occs|
    "  &lt;li&gt;#{word}: #{occs}&lt;/li&gt;"
  end.join("\n")
  "&lt;ul&gt;\n" + list + "\n&lt;/ul&gt;"
end


Instead of starting with list as "" and iterating through each word/occurrences pair to add onto it a little at a time, you can take a functional approach and use map to turn each word/occurrences pair into a string like "  &lt;li&gt;antibubbles: 4&lt;/li&gt;", then use join to turn the resulting array into one long string with "\n" inserted in between each list item. I also inserted two spaces before each list item so that they appear indented in the HTML. :)