<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -172,6 +172,7 @@ module FriendlyId
       raise ActiveRecord::RecordNotFound, &quot;Couldn't find all #{ name.pluralize } with IDs (#{ ids_and_names * ', ' }) AND #{ sanitize_sql options[:conditions] } (found #{ results.size } results, but was looking for #{ expected_size })&quot; if results.size != expected_size
 
       # assign finder slugs
+      # FIXME set the actual slugs, not the name to &quot;lazy load&quot; later, because that's not lazy!
       slugs.each do |slug|
         result = results.find { |r| r.id == slug.sluggable_id } and
         result.finder_slug_name = slug.name
@@ -183,6 +184,8 @@ module FriendlyId
 
   module SluggableInstanceMethods
 
+    NUM_CHARS_RESERVED_FOR_FRIENDLY_ID_EXTENSION = 2
+
     attr :finder_slug 
     attr_accessor :finder_slug_name
 
@@ -192,7 +195,7 @@ module FriendlyId
     
     # Was the record found using one of its friendly ids?
     def found_using_friendly_id?
-      @finder_slug_name
+      finder_slug
     end
 
     # Was the record found using its numeric id?
@@ -212,7 +215,7 @@ module FriendlyId
 
     # Returns the friendly id.
     def friendly_id
-      finder_slug_name or slug.name
+      slug(true).name
     end
     alias best_id friendly_id
 
@@ -230,10 +233,15 @@ module FriendlyId
 
     # Generate the text for the friendly id, ensuring no duplication.
     def generate_friendly_id
-      slug_text = truncated_friendly_id_base
-      count = Slug.count_matches slug_text, self.class.name, :all, :conditions =&gt; &quot;sluggable_id &lt;&gt; #{ id.to_i }&quot;
-      count += 1 if self.class.friendly_id_options[:reserved].include?(slug_text)
-      count == 0 ? slug_text : generate_friendly_id_with_extension(slug_text, count)
+      base = friendly_id_base
+      opts = self.class.friendly_id_options
+      if base.length &gt; opts[:max_length]
+        base = base[0...opts[:max_length] - NUM_CHARS_RESERVED_FOR_FRIENDLY_ID_EXTENSION]
+      end
+      if opts[:reserved].include?(base)
+        base = &quot;#{base}-2&quot;
+      end
+      Slug.get_best_name(base, self.class)
     end
 
     # Set the slug using the generated friendly id.
@@ -241,12 +249,13 @@ module FriendlyId
       if self.class.friendly_id_options[:use_slug]
         @most_recent_slug = nil
         slug_text = generate_friendly_id
-
+        # Avoids regenerating slug over and over again.
+        # FIXME This could perform pretty badly if a model has tons of similarly-named slugs
+        return if slug &amp;&amp; slug.succ == slug_text
         if slugs.empty? || slugs.first.name != slug_text
-          previous_slug = slugs.find_by_name slug_text
+          previous_slug = slugs.find_by_name friendly_id_base
           previous_slug.destroy if previous_slug
-
-          slugs.build :name =&gt; slug_text
+          slugs.build :name =&gt; generate_friendly_id
         end
       end
     end
@@ -267,33 +276,13 @@ module FriendlyId
 
     private
 
-    NUM_CHARS_RESERVED_FOR_FRIENDLY_ID_EXTENSION = 2
-
     def init_finder_slug
-      raise RuntimeError, 'No slug name is set' if !@finder_slug_name
+      return false if !@finder_slug_name
       slug = Slug.find(:first, :conditions =&gt; {:sluggable_id =&gt; id, :name =&gt; @finder_slug_name})
       slug.sluggable = self
       return slug
     end
 
-    def truncated_friendly_id_base
-      max_length = friendly_id_options[:max_length]
-      slug_text = friendly_id_base[0, max_length - NUM_CHARS_RESERVED_FOR_FRIENDLY_ID_EXTENSION]
-    end
-
-    # Reserve a few spaces at the end of the slug for the counter extension.
-    # This is to avoid generating slugs longer than the maxlength when an
-    # extension is added.
-    POSSIBILITIES = 10 ** NUM_CHARS_RESERVED_FOR_FRIENDLY_ID_EXTENSION - 1
-    def generate_friendly_id_with_extension(slug_text, count)
-      count &lt;= POSSIBILITIES or
-      raise FriendlyId::SlugGenerationError.new(&quot;slug text #{slug_text} goes over limit for similarly named slugs&quot;)
-
-      slug_text = &quot;#{ truncated_friendly_id_base }-#{ count + 1 }&quot;
-
-      count = Slug.count_matches slug_text, self.class.name, :all, :conditions =&gt; &quot;sluggable_id &lt;&gt; #{ id.to_i }&quot;
-      count &gt; 0 ? &quot;#{ truncated_friendly_id_base }-#{ count + 1 }&quot; : slug_text
-    end
   end
 
-end
+end
\ No newline at end of file</diff>
      <filename>lib/friendly_id.rb</filename>
    </modified>
    <modified>
      <diff>@@ -5,50 +5,46 @@ class Slug &lt; ActiveRecord::Base
   validates_uniqueness_of :name, :scope =&gt; :sluggable_type
 
   class &lt;&lt; self
+    
     def with_name(name)
       &quot;#{ quoted_table_name }.name = #{ quote_value name, columns_hash['name'] }&quot;
     end
+    
     def with_names(names)
       name_column = columns_hash['name']
       names = names.map { |n| &quot;#{ quote_value n, name_column }&quot; }.join ','
 
       &quot;#{ quoted_table_name }.name IN (#{ names })&quot;
     end
+    
     def find_all_by_names_and_sluggable_type(names, type)
       names = with_names names
       type  = &quot;#{ quoted_table_name }.sluggable_type = #{ quote_value type, columns_hash['sluggable_type'] }&quot;
       find :all, :conditions =&gt; &quot;#{ names } AND #{ type }&quot;
     end
 
-    # Count exact matches for a slug. Matches include slugs with the same name
-    # and an appended numeric suffix, i.e., &quot;an-example-slug&quot; and
-    # &quot;an-example-slug-2&quot;
-    #
-    # The first two arguments are required, after which you may pass in the
-    # same arguments as ActiveRecord::Base.find.
-    COND = 'name LIKE ? AND sluggable_type = ?'.freeze
-    def count_matches(name, type, *args)
-      name_esc = Regexp.escape name
-
-      with_scope(:find =&gt; {:conditions =&gt; [COND, &quot;#{name}%&quot;, type]}) {
-        find(*args)
-      }.inject(0) do |count, slug|
-        slug.name =~ /\A#{name_esc}(-[\d]+)*\Z/ ? count + 1 : count
-      end
+    # Checks a slug name for collisions
+    def get_best_name(name, type)
+      slugs = find :all, :conditions =&gt; ['name LIKE ? AND sluggable_type = ?', &quot;#{name}%&quot;, type.to_s]
+      return name if slugs.size == 0
+      slugs.map { |x| slugs.delete(x) unless x.base == name }
+      slugs.sort! { |x, y| x.extension &lt;=&gt; y.extension }
+      slugs.empty? ? name : slugs.last.succ
     end
 
     # Sanitizes and dasherizes string to make it safe for URL's.
     #
     # Example:
+    #
     #   slug.normalize('This... is an example!') # =&gt; &quot;this-is-an-example&quot;
     #
-    # Note that Rails 2.2.x offers a parameterize method for stripping
-    # diacritics. This is not used here because at the time of writing, it
-    # handles several characters incorrectly, for instance replacing
-    # Icelandic's &quot;thorn&quot; character with &quot;y&quot; rather than &quot;d.&quot; This might be
-    # pedantic, but I don't want to piss off the Vikings. The last time anyone
-    # pissed them off, they uleashed a wave of terror in Europe unlike
-    # anything ever seen before or after. I'm not taking any chances.
+    # Note that Rails 2.2.x offers a parameterize method for this. It's not
+    # used here because at the time of writing, it handles several characters
+    # incorrectly, for instance replacing Icelandic's &quot;thorn&quot; character with
+    # &quot;y&quot; rather than &quot;d.&quot; This might be pedantic, but I don't want to piss
+    # off the Vikings. The last time anyone pissed them off, they uleashed a
+    # wave of terror in Europe unlike anything ever seen before or after. I'm
+    # not taking any chances.
     def normalize(slug_text)
       # Use this onces it starts working reliably
       # return slug_text.parameterize.to_s if slug_text.respond_to?(:parameterize)
@@ -68,7 +64,19 @@ class Slug &lt; ActiveRecord::Base
     end
 
   end
-
+  
+  def succ
+    extension == 0 ? &quot;#{base}-2&quot; : name.succ
+  end
+  
+  def base
+    name.gsub(/-?\d*\z/, '')
+  end
+  
+  def extension
+    /\d*\z/.match(name).to_s.to_i
+  end
+  
   # Whether or not this slug is the most recent of its owner's slugs.
   def is_most_recent?
     debugger</diff>
      <filename>lib/slug.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,3 @@
 class Post &lt; ActiveRecord::Base
-  has_friendly_id :name, :use_slug =&gt; true, :reserved =&gt; ['new', 'recent']
+  has_friendly_id :name, :use_slug =&gt; true, :reserved =&gt; ['new', 'edit']
 end</diff>
      <filename>test/fixtures/post.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,7 +7,7 @@ class SluggableTest &lt; Test::Unit::TestCase
   def setup
     Post.friendly_id_options[:max_length] = FriendlyId::ClassMethods::DEFAULT_FRIENDLY_ID_OPTIONS[:max_length]
   end
-
+  
   def test_finder_options_are_not_ignored
     assert_raises ActiveRecord::RecordNotFound do
       Post.find(slugs(:one).name, :conditions =&gt; &quot;1 = 2&quot;)
@@ -180,5 +180,26 @@ class SluggableTest &lt; Test::Unit::TestCase
     post = Post.create!(:name =&gt; 'new')
     assert_equal 'new-2', post.friendly_id
   end
+  
+  def test_slug_sequence_is_based_on_highest_extension_rather_than_slug_count
+    assert_equal &quot;test&quot;, Slug.get_best_name(&quot;test&quot;, Post)
+    @post = Post.create!(:name =&gt; &quot;test&quot;, :content =&gt; &quot;stuff&quot;)
+    assert_equal &quot;test&quot;, @post.slug.name
+    
+    assert_equal &quot;test-2&quot;, Slug.get_best_name(&quot;test&quot;, Post)
+    @post2 = Post.create!(:name =&gt; &quot;test&quot;, :content =&gt; &quot;stuff&quot;) # slug should be &quot;test-2&quot;
+    assert_equal &quot;test-2&quot;, @post2.slug.name
+  
+    assert_equal &quot;test-3&quot;, Slug.get_best_name(&quot;test&quot;, Post)
+    @post3 = Post.create!(:name =&gt; &quot;test&quot;, :content =&gt; &quot;stuff&quot;)
+    assert_equal &quot;test-3&quot;, @post3.slug.name
+    
+    assert_equal &quot;test-4&quot;, Slug.get_best_name(&quot;test&quot;, Post)
+    @post.destroy # will destroy slug named &quot;test&quot; along with it
+    # Make sure the next slug is still test-4 and not test-3
+    assert_equal &quot;test-4&quot;, Slug.get_best_name(&quot;test&quot;, Post)    
+    @post4 = Post.create!(:name =&gt; &quot;test&quot;, :content =&gt; &quot;stuff&quot;)
+    assert_equal &quot;test-4&quot;, @post4.slug.name
+  end
 
 end
\ No newline at end of file</diff>
      <filename>test/sluggable_test.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>61ab15579986f9240f3d74a6f8affcaa13ebfa77</id>
    </parent>
  </parents>
  <author>
    <name>Norman Clarke</name>
    <email>norman@randomba.org</email>
  </author>
  <url>http://github.com/norman/friendly_id/commit/f2fac151e73c1649fe5c619a6506ab4aa2e95cbd</url>
  <id>f2fac151e73c1649fe5c619a6506ab4aa2e95cbd</id>
  <committed-date>2008-10-31T12:40:04-07:00</committed-date>
  <authored-date>2008-10-31T12:38:23-07:00</authored-date>
  <message>Fixed logic error with slug generation. Resolves ticket #7. Thanks to Tim
Kadom for reporting the bug that this commit fixes.</message>
  <tree>858beecf4218e0bdad544b30210769ae189380d0</tree>
  <committer>
    <name>Norman Clarke</name>
    <email>norman@randomba.org</email>
  </committer>
</commit>
