From a951729f45c579aa17b76a73f72880f90df9e910 Mon Sep 17 00:00:00 2001
From: Genadi Samokovarov <gsamokovarov@gmail.com>
Date: Tue, 27 Feb 2018 17:06:33 +0200
Subject: Extract Rails::Command::Spellchecker

---
 railties/lib/rails/command.rb              |  1 +
 railties/lib/rails/command/behavior.rb     | 40 ----------------------
 railties/lib/rails/command/spellchecker.rb | 53 ++++++++++++++++++++++++++++++
 railties/lib/rails/generators.rb           |  2 +-
 4 files changed, 55 insertions(+), 41 deletions(-)
 create mode 100644 railties/lib/rails/command/spellchecker.rb

(limited to 'railties/lib/rails')

diff --git a/railties/lib/rails/command.rb b/railties/lib/rails/command.rb
index 078e9f937f..6d99ac9936 100644
--- a/railties/lib/rails/command.rb
+++ b/railties/lib/rails/command.rb
@@ -11,6 +11,7 @@ module Rails
   module Command
     extend ActiveSupport::Autoload
 
+    autoload :Spellchecker
     autoload :Behavior
     autoload :Base
 
diff --git a/railties/lib/rails/command/behavior.rb b/railties/lib/rails/command/behavior.rb
index 7a6dd28e1a..718e2d9ab2 100644
--- a/railties/lib/rails/command/behavior.rb
+++ b/railties/lib/rails/command/behavior.rb
@@ -19,46 +19,6 @@ module Rails
         end
 
         private
-
-          # This code is based directly on the Text gem implementation.
-          # Copyright (c) 2006-2013 Paul Battley, Michael Neumann, Tim Fletcher.
-          #
-          # Returns a value representing the "cost" of transforming str1 into str2.
-          def levenshtein_distance(str1, str2) # :doc:
-            s = str1
-            t = str2
-            n = s.length
-            m = t.length
-
-            return m if (0 == n)
-            return n if (0 == m)
-
-            d = (0..m).to_a
-            x = nil
-
-            # avoid duplicating an enumerable object in the loop
-            str2_codepoint_enumerable = str2.each_codepoint
-
-            str1.each_codepoint.with_index do |char1, i|
-              e = i + 1
-
-              str2_codepoint_enumerable.with_index do |char2, j|
-                cost = (char1 == char2) ? 0 : 1
-                x = [
-                     d[j + 1] + 1, # insertion
-                     e + 1,      # deletion
-                     d[j] + cost # substitution
-                    ].min
-                d[j] = e
-                e = x
-              end
-
-              d[m] = x
-            end
-
-            x
-          end
-
           # Prints a list of generators.
           def print_list(base, namespaces)
             return if namespaces.empty?
diff --git a/railties/lib/rails/command/spellchecker.rb b/railties/lib/rails/command/spellchecker.rb
new file mode 100644
index 0000000000..59ccab4ea2
--- /dev/null
+++ b/railties/lib/rails/command/spellchecker.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module Rails
+  module Command
+    module Spellchecker # :nodoc:
+      class << self
+        def suggest(word, from:, count: 3)
+          from.sort_by { |w| levenshtein_distance(word, w) }.take(count)
+        end
+
+        private
+          # This code is based directly on the Text gem implementation.
+          # Copyright (c) 2006-2013 Paul Battley, Michael Neumann, Tim Fletcher.
+          #
+          # Returns a value representing the "cost" of transforming str1 into str2.
+          def levenshtein_distance(str1, str2) # :doc:
+            s = str1
+            t = str2
+            n = s.length
+            m = t.length
+
+            return m if (0 == n)
+            return n if (0 == m)
+
+            d = (0..m).to_a
+            x = nil
+
+            # avoid duplicating an enumerable object in the loop
+            str2_codepoint_enumerable = str2.each_codepoint
+
+            str1.each_codepoint.with_index do |char1, i|
+              e = i + 1
+
+              str2_codepoint_enumerable.with_index do |char2, j|
+                cost = (char1 == char2) ? 0 : 1
+                x = [
+                  d[j + 1] + 1, # insertion
+                  e + 1,        # deletion
+                  d[j] + cost   # substitution
+                ].min
+                d[j] = e
+                e = x
+              end
+
+              d[m] = x
+            end
+
+            x
+          end
+      end
+    end
+  end
+end
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 6a7175c02b..7248fbbc94 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -276,7 +276,7 @@ module Rails
           klass.start(args, config)
         else
           options     = sorted_groups.flat_map(&:last)
-          suggestions = options.sort_by { |suggested| levenshtein_distance(namespace.to_s, suggested) }.first(3)
+          suggestions = Rails::Command::Spellchecker.suggest(namespace.to_s, from: options, count: 3)
           suggestions.map! { |s| "'#{s}'" }
           msg =  "Could not find generator '#{namespace}'. ".dup
           msg << "Maybe you meant #{ suggestions[0...-1].join(', ')} or #{suggestions[-1]}\n"
-- 
cgit v1.2.3