Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Saving People from Typos

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

Saving People from Typos

Avatar for Yuki Nishijima

Yuki Nishijima

June 05, 2015
Tweet

More Decks by Yuki Nishijima

Other Decks in Programming

Transcript

  1. 4 B W J O H  1F P Q

    M F  G S P N  5Z Q P T
  2. !ZVLJ : 6 , *  / * 4 )

    * + * . "  ੢ ౢ  ༔ و
  3. !ZVLJ 4PGUXBSF&OHJOFFSBU : 6 , *  / * 4

    ) * + * . "  ੢ ౢ  ༔ و
  4. !ZVLJ .BJOUBJOFSPGkaminari : 6 , *  / * 4

    ) * + * . "  ੢ ౢ  ༔ و
  5. 4 B W J O H  1F P Q

    M F  G S P N  5Z Q P T
  6. 8 ) :  $ " / ` 5 

    3 6 # :  % 0  5 ) &  4 " . &
  7. 3 6 # :  * 4  % &

    4 * ( / & %  5 0  . " , &  1 3 0 ( 3 " . . & 3 4  ) " 1 1 :
  8. 8 ) :  $ " / ` 5 

    3 6 # :  % 0  5 ) &  4 " . &
  9. 5 I F  d i d _ y o

    u _ m e a n  H F N
  10. require "did_you_mean" "Yuki".starts_with?("Y") # => NoMethodError: undefined method # `starts_with?’

    for “Yuki":String # # Did you mean? start_with? # "Yuki".start_with?("Y") # => true
  11. ) 0 8  % 0 & 4  *

    5  8 0 3 ,
  12. w "  T Q F M M  D

    I F D L F S  w . P O L F Z  Q B U D I F T
  13. w "  T Q F M M  D

    I F D L F S  w . P O L F Z  Q B U D I F T
  14. * / 4 * % &  5 ) &

     4 1 & - -  $ ) & $ , & 3
  15. w %JDUJPOBSZ * / 4 * % &  5

    ) &  4 1 & - -  $ ) & $ , & 3
  16. w %JDUJPOBSZ w $POUSPMNFDIBOJTN * / 4 * % &

     5 ) &  4 1 & - -  $ ) & $ , & 3
  17. w %JDUJPOBSZ w $POUSPMNFDIBOJTN w 0QUJNJ[BUJPO * / 4 *

    % &  5 ) &  4 1 & - -  $ ) & $ , & 3
  18. % * $ 5 * 0 / " 3: w

    "TFUPGXPSET w "TQFMMDIFDLFSNBZIBWFTFWFSBMEJDUJPOBSJFT w 5IFDPOUFOUPGBEJDUJPOBSZNBZEJGGFSEFQFOEJOHPO UIFUZQFPGUIFTQFMMDIFDLFS
  19. $ 0 / 5 3 0 -  . &

    $ ) " / * 4 . w 1JDLTVQUIFNPTUMJLFMZTQFMMJOHDPSSFDUJPO T GPSUIFJOQVU w "TQFMMDIFDLFSNBZIBWFTFWFSBMDPOUSPMNFDIBOJTNT w 6TFTPOFPSNPSFNFUSJDT w TJNJMBSJUZCFUXFFOTUSJOHT FH-FWFOTIUFJO +BSP8JOLMFS  w OHSBN w /BJWFMZTDBOOJOHBMMUIFXPSETJOUIFEJDUJPOBSZXPVMECF QBJOGVMMZTMPX
  20. 0 1 5 * . * ; "5 * 0

    / w *NQSPWFTQFSGPSNBODFBOEPSBDDVSBDZ w 0QUJNJ[BUJPOUFDIOJRVFTNBZCFDPOUFYUTQFDJpD w (SBNNBSBOBMZTJT w 4UBUJTUJDBMNPEFM w 1SPOVODJBUJPOCBTFEDPSSFDUJPO
  21. l 5 I F Z  X F F S

    F  U S B W F M J O H  U I S P V H I  U I F  O J H I U  z
  22. XFSF XIFSF l 5 I F Z  X F

    F S F  U S B W F M J O H  U I S P V H I  U I F  O J H I U  z
  23. XFSF XIFSF ✅HSBNNBUJDBMMZDPSSFDU "JODPSSFDU l 5 I F Z 

    X F F S F  U S B W F M J O H  U I S P V H I  U I F  O J H I U  z
  24. l 5 I F Z  O P  I

    P X  U P  E P  J U  z
  25. LOPX ✅TBNFQSPOVODJBUJPO l 5 I F Z  O P

     I P X  U P  E P  J U  z
  26. 5 I F  d i d _ y o

    u _ m e a n  H F N w %JDUJPOBSZ w "TFUPGTZNCPMT w $POUSPMNFDIBOJTN w 6TFT-FWFOTIUFJOEJTUBODFUPDPSSFDUNJTUZQFEXPSET w 6TFT+BSP8JOLMFSEJTUBODFUPDPSSFDUNJTTQFMUXPSET w 0QUJNJ[BUJPO w $POUFYUCBTFEEJDUJPOBSZ
  27. . J T U Z Q F  $ P

    S S F D U J P O  . J T T Q F M M  $ P S S F D U J P O C O N T R O L M E C H A N I S M
  28. 8IZ/FFET5ZQFTPG$POUSPM.FDIBOJTN .JTUZQFEXPSET w 5IFDPSSFDUTQFMMJOHJTDPSSFDUMZSFNFNCFSFE CVUPOFPSNPSF JODPSSFDUMFUUFSTBSFUZQFECZNJTUBLF w DPSSFDUFECZUIFdid_you_meanHFNVTJOH-FWFOTIUFJOEJTUBODF .JTTQFMUXPSET w

    5IFDPSSFDUTQFMMJOHJTNJTSFNFNCFSFEPSOPUSFNFNCFSFEBUBMM w 5IFpSTUDIBSBDUFSJTBMXBZTDPSSFDU • Yannakoudakis, E.J. and Fawthrop, D., "The rules of spelling errors," Information Processing and Management, vol. 19, no. 2, pp. 87-99, 1983. w DPSSFDUFECZUIFdid_you_mean HFNVTJOH+BSP8JOLMFSEJTUBODF
  29. . J T U Z Q F  $ P

    S S F D U J P O  . J T T Q F M M  $ P S S F D U J P O
  30. - F W F O T I U F J

    O  % J T U B O D F
  31. start_with starts_with - F W F O T I U

    F J O  % J T U B O D F
  32. start_with starts_with - F W F O T I U

    F J O  % J T U B O D F 5IFEJTUBODFJT ✅JOTFSUJPO
  33. first_name full_name - F W F O T I U

    F J O  % J T U B O D F
  34. first_name full_name - F W F O T I U

    F J O  % J T U B O D F ✅ ✅ ✅ TVCTUJUVUJPO
  35. first_name full_name - F W F O T I U

    F J O  % J T U B O D F ✅ ✅ ✅ ✅ 5IFEJTUBODFJT EFMFUJPO TVCTUJUVUJPO
  36. . J T U Z Q F  $ P

    S S F D U J P O  . J T T Q F M M  $ P S S F D U J P O
  37. + B S P  8 J O L M

    F S  % J T U B O D F
  38. + B S P  % J T U B

    O D F   1 S F G J Y  # P O V T
  39. +BSP%JTUBODF m : 10 (the number of matching letters) t

    : 1 (half the number of transpositions)
  40. +BSP%JTUBODF m : 10 (the number of matching letters) t

    : 1 (half the number of transpositions)
  41. m : 10 (the number of matching letters) t :

    1 (half the number of transpositions) 5IFEJTUBODFJT0.9666… +BSP%JTUBODF
  42. I U U Q    H J U

     J P  W 3 E : 8 5 I F  E J E @ Z P V @ N F B O ` T  4 Q F M M  $ I F D L F S
  43. "  T Q F M M  D I

    F D L F S  . P O L F Z  Q B U D I F T ✅
  44. 07 & 3 3 * % * / ( 

    5 ) &  & 3 3 0 3  . & 4 4 "( & module DidYouMean module NameErrorExtension prepend_features NameError def to_s super + Formatter.new(corrections).to_s rescue super end def corrections SPELL_CHECKERS[self.class.to_s].new(self).corrections end end end
  45. module DidYouMean module NameErrorExtension prepend_features NameError def to_s super +

    Formatter.new(corrections).to_s rescue super end def corrections SPELL_CHECKERS[self.class.to_s].new(self).corrections end end end 07 & 3 3 * % * / (  5 ) &  & 3 3 0 3  . & 4 4 "( &
  46. module DidYouMean module NameErrorExtension prepend_features NameError def to_s super +

    Formatter.new(corrections).to_s rescue super end def corrections SPELL_CHECKERS[self.class.to_s].new(self).corrections end end end 07 & 3 3 * % * / (  5 ) &  & 3 3 0 3  . & 4 4 "( &
  47. * / 4 * % &  5 ) &

     * / * 5 * " - * ; & 3 module DidYouMean class ANameChecker include SpellCheckable def initialize(exception) # pull out the user input and generate # a dictionary using the exception object. end end end
  48. NameError#name begin doesnt_exist rescue NameError => error error.name # =>

    :doesnt_exist end begin DoesntExist rescue NameError => error error.name # => :DoesntExist end begin @@doesnt_exist rescue NameError => error error.name # => :@@doesnt_exist end
  49. NameError#name begin doesnt_exist("argument") rescue NoMethodError => error error.name # =>

    :doesnt_exist end begin self.doesnt_exist rescue NoMethodError => error error.name # => :doesnt_exist end
  50. * / 4 * % &  5 ) &

     * / * 5 * " - * ; & 3 module DidYouMean class ANameChecker include SpellCheckable def initialize(exception) @name = exception.name # user input @names = ???? # dictionary end end end
  51. . & 5 ) 0 %  / " .

    & 4 module DidYouMean class MethodNameChecker include SpellCheckable def initialize(no_method_error) # user input @name = no_method_error.name # dictionary @method_names = no_method_error.receiver.methods end end end
  52. module DidYouMean class MethodNameChecker include SpellCheckable def initialize(no_method_error) # user

    input @name = no_method_error.name # dictionary @method_names = no_method_error.receiver.methods end end end . & 5 ) 0 %  / " . & 4
  53. NameError#receiver R uby 2.3! string = "receiver" begin string.doesnt_exist rescue

    NameError => error error.receiver == string # => true end w 6TFEUPCFJNQMFNFOUFEBTB$FYUFOTJPO w *TOPXQBSUPG3VCZ w 3FUVSOTUIFSFDFJWFSXIFSFUIFNFUIPEJTDBMMFEPO
  54. . & 5 ) 0 %  / " .

    & 4 module DidYouMean class MethodNameChecker include SpellCheckable def initialize(no_method_error) # user input @name = no_method_error.name # dictionary @method_names = no_method_error.receiver.methods end end end
  55. . & 5 ) 0 %  / " .

    & 4 module DidYouMean class MethodNameChecker include SpellCheckable def initialize(no_method_error) # user input @name = no_method_error.name # dictionary @method_names = no_method_error.receiver.methods end def candidates { @name => @method_names } end end end
  56. 7" 3 * " # - &  / "

    . & 4 module DidYouMean class VariableNameChecker include SpellCheckable def initialize(name_error) # user input @name = name_error.name.to_s # dictionary r = name_error.receiver @method_names = r.methods + r.private_methods @ivar_names = r.instance_variables @cvar_names = r.class.class_variables @cvar_names += r.class_variables if r.kind_of?(Module) @lvar_names = name_error.local_variables end def candidates { @name => (@lvar_names + @method_names + @ivar_names + @cvar_names) } end end end
  57. 7" 3 * " # - &  / "

    . & 4 module DidYouMean class VariableNameChecker include SpellCheckable def initialize(name_error) # user input @name = name_error.name.to_s # dictionary r = name_error.receiver @method_names = r.methods + r.private_methods @ivar_names = r.instance_variables @cvar_names = r.class.class_variables @cvar_names += r.class_variables if r.kind_of?(Module) @lvar_names = name_error.local_variables end def candidates { @name => (@lvar_names + @method_names + @ivar_names + @cvar_names) } end end end
  58. 7" 3 * " # - &  / "

    . & 4 module DidYouMean class VariableNameChecker include SpellCheckable def initialize(name_error) # user input @name = name_error.name.to_s # dictionary r = name_error.receiver @method_names = r.methods + r.private_methods @ivar_names = r.instance_variables @cvar_names = r.class.class_variables @cvar_names += r.class_variables if r.kind_of?(Module) @lvar_names = name_error.local_variables end def candidates { @name => (@lvar_names + @method_names + @ivar_names + @cvar_names) } end end end
  59. $ - " 4 4  / " . &

    4 module DidYouMean class ClassNameChecker include SpellCheckable def initialize(exception) @class_name, @receiver = exception.name, exception.receiver end def candidates { @class_name => class_names } end # generates a dictionary def class_names scopes.flat_map do |scope| scope.constants.map do |constant| scope == Object ? constant : "#{scope}::#{constant}" end end end def scopes @receiver.to_s.split("::").inject([Object]) do |_scopes, scope| _scopes << _scopes.last.const_get(scope) end.uniq end end end
  60. module DidYouMean class ClassNameChecker include SpellCheckable def initialize(exception) @class_name, @receiver

    = exception.name, exception.receiver end def candidates { @class_name => class_names } end # generates a dictionary def class_names scopes.flat_map do |scope| scope.constants.map do |constant| scope == Object ? constant : "#{scope}::#{constant}" end end end def scopes @receiver.to_s.split("::").inject([Object]) do |_scopes, scope| _scopes << _scopes.last.const_get(scope) end.uniq end end end $ - " 4 4  / " . & 4 5-%3
  61. $ - " 4 4  / " . &

    4 ... class Person ... def address Address.new(raw_address) end class Address ... def zipcode ZipCode.new(raw_zipcode) # => NameError end class Zipcode ... end end end
  62. $ - " 4 4  / " . &

    4 ... class Person ... def address Address.new(raw_address) end class Address ... def zipcode ZipCode.new(raw_zipcode) # => NameError end class Zipcode ... end end end Object.constants Person.constants Address.constants
  63. $ - " 4 4  / " . &

    4 ... class Person ... def address Address.new(raw_address) end class Address ... def zipcode ZipCode.new(raw_zipcode) # => NameError end class Zipcode ... end end end + + Object.constants Person.constants Address.constants
  64. "  T Q F M M  D I

    F D L F S  . P O L F Z  Q B U D I F T ✅ ✅