DidYouMean 모듈

DidYouMean 모듈

NameErrorNoMethodError를 만나면 Ruby가 "혹시 이거 말고 이거였어요?" 하고 후보를 제시해 주는 경우가 많죠. 그 기능을 담당하는 게 DidYouMean 젬이에요. 메서드명이나 클래스명이 틀렸을 때 그럴듯한 대안 이름을 제안해 줘요. Ruby 2.3 이후로는 시작할 때 자동으로 활성화돼요.

어떤 오류를 잡아 주나요

오타 하나가 어떤 오류를 만드는지 직접 보는 게 가장 빠른데요, 아래 예시들이 전형적인 모습이에요.

methosd
# => NameError: undefined local variable or method `methosd' for main:Object
#   Did you mean?  methods
#                  method

OBject
# => NameError: uninitialized constant OBject
#    Did you mean?  Object

@full_name = "Yuki Nishijima"
first_name, last_name = full_name.split(" ")
# => NameError: undefined local variable or method `full_name' for main:Object
#    Did you mean?  @full_name

@@full_name = "Yuki Nishijima"
@@full_anme
# => NameError: uninitialized class variable @@full_anme in Object
#    Did you mean?  @@full_name

full_name = "Yuki Nishijima"
full_name.starts_with?("Y")
# => NoMethodError: undefined method `starts_with?' for "Yuki Nishijima":String
#    Did you mean?  start_with?

hash = {foo: 1, bar: 2, baz: 3}
hash.fetch(:fooo)
# => KeyError: key not found: :fooo
#    Did you mean?  :foo

did_you_mean 끄기

가끔은 오류 객체 자체의 문제를 디버깅할 때처럼 이 제안 기능을 끄고 싶을 때가 있어요. ruby 명령에 --disable-did_you_mean 옵션을 주면 완전히 끌 수 있어요:

$ ruby --disable-did_you_mean -e "1.zeor?"
-e:1:in `<main>': undefined method `zeor?' for 1:Integer (NameError)

ruby 명령에 직접 접근할 수 없는 경우(예: rails console, irb)에는 RUBYOPT 환경 변수로 옵션을 적용할 수 있어요:

$ RUBYOPT='--disable-did_you_mean' irb
irb:0> 1.zeor?
# => NoMethodError (undefined method `zeor?' for 1:Integer)

원래 오류 메시지 가져오기

때로는 젬을 통째로 끄기보다, 제안 없이 원래 오류 메시지만 얻고 싶을 때가 있어요(예: 테스트). 이럴 땐 오류 객체의 original_message 메서드를 쓰면 돼요:

no_method_error = begin
                    1.zeor?
                  rescue NoMethodError => error
                    error
                  end

no_method_error.message
# => NoMethodError (undefined method `zeor?' for 1:Integer)
#    Did you mean?  zero?

no_method_error.original_message
# => NoMethodError (undefined method `zeor?' for 1:Integer)

보시다시피 message에는 제안이 붙지만, original_message은 순수한 원래 메시지만 돌려줘요.

상수

  • PlainFormatterDidYouMean::Formatter는 이 젬의 기본 포맷터예요. message_for 메서드에 응답하며 사람이 읽을 수 있는 문자열을 돌려줘요.
  • VERSION — 버전 문자열
  • VerboseFormatterPlainFormatter보다 더 상세한 제안을 만들어 주는 포맷터예요.

클래스 메서드

  • correct_error(error_class, spell_checker) — 주어진 spell checker로 오류에 DidYouMean 기능을 더해줘요.

    # File lib/did_you_mean.rb, line 97
    def self.correct_error(error_class, spell_checker)
      if defined?(Ractor)
        new_mapping = { **@spell_checkers, error_class.to_s => spell_checker }
        new_mapping.default = NullChecker
    
        @spell_checkers = Ractor.make_shareable(new_mapping)
      else
        spell_checkers[error_class.to_s] = spell_checker
      end
    
      error_class.prepend(Correctable) if error_class.is_a?(Class) && !(error_class < Correctable)
    end
    
  • formatter() — 현재 설정된 포맷터를 돌려줘요. 기본값은 DidYouMean::Formatter예요.

    # File lib/did_you_mean.rb, line 117
    def self.formatter
      if defined?(Ractor)
        Ractor.current[:__did_you_mean_formatter__] || Formatter
      else
        Formatter
      end
    end
    
  • formatter=(formatter) — 제안을 포맷할 때 쓰이는 기본 포맷터를 갱신해요.

    # File lib/did_you_mean.rb, line 126
    def self.formatter=(formatter)
      if defined?(Ractor)
        Ractor.current[:__did_you_mean_formatter__] = formatter
      end
    end
    
  • spell_checkers() — 오류 타입과 spell checker 객체의 공유 가능한 해시 맵을 돌려줘요.

    # File lib/did_you_mean.rb, line 92
    def self.spell_checkers
      @spell_checkers
    end
    

출처: Ruby 4.0 API - DidYouMean