Onze dagmethode #18: NilClass#method_missing

Geplaatst door Remco van 't Veer vr, 29 feb 2008 07:25:00 GMT

Waarschuwing: alleen gebruiken onder toezicht van een meerderjarige!

We kennen de whiny nil, zoals toegepast in Rails, maar wat nou whiny?! Bij deze de obedient nil:


class NilClass
  def method_missing(*args)
    nil
  end
end

Geen geklooi meer:


person && person.name && person.name.upcase

Niet echt DRY! Nu met de obedient nil:


person.name.upcase

Het is alweer de laatste dag methode. Ode aan Michiel voor de hoeveelheid gave trucs die hij uit z’n hoge hoed heeft kunnen trekken!

Geplaatst in ,  | 12 reacties

Reacties

  1. p3t0r zei ongeveer 3 uur later:

    Safe dereferencing… cool!

    In Groovy kun je dit gedrag sturen door een vraagteken voor de . te plakken:

    person?.name.upcase
    

    eigenlijk vind ik dat mooier dan dit globaal doen.

    Ik heb even zitten denken of ik zoiets ook in Ruby zou kunnen maken, maar iets moois krijgt ik niet bedacht… iemand een idee?

  2. Michiel zei ongeveer 10 uur later:

    Werkt dat ook als person.name nil is?

    Het truukje van vandaag is qua functionaliteit een verbetering. Wanneer wil je nou een NoMethodError? Dit is veel nuttiger. Maar aangezien het alle errors opslokt werkt het alleen als je een perfecte programmeur bent die nooit fouten maakt. Ik houd het dus op de whiny nil.

    Ik heb trouwens al eens een artikel over iets dergelijks geschreven. De lopende band

    Je kunt dan het volgende zeggen: free(maybe(roman).schrijver.adres.postcode.to_coordinates). Maar de syntax is te onhandig.

  3. p3t0r zei ongeveer 12 uur later:

    Werkt dat ook als person.name nil is?

    Eeuhmm… natuurlijk… maar dan moet je daar wel ook safe de-referencen als je dat wilt:

    person?.name?.upcase
    
  4. RemVee zei ongeveer 12 uur later:

    Artikel aangepast:

    person && person.name && person.name.upcase
  5. Michiel zei ongeveer 13 uur later:

    Anders krijg je bij person?.name.upcase toch alsnog een NPE als person nil is?

  6. P8 zei ongeveer 14 uur later:

    ParseTree gebruikt dit ook in een oudere versie, maar Ryan Davis heeft het eruit gesloopt omdat te veel mensen liepen te klagen. Het geeft idd leuke problemen als je het niets vermoedend in je Rails project include.

    @p3tor: misschien zoiets?

    class NilClass
      def method_missing_with_safe_dereferencing(*args)
        nil
      end  
    
      def self.with_safe_dereferencing
        alias_method :old_method_missing, :method_missing
        alias_method :method_missing, :method_missing_with_safe_dereferencing
        yield    
        alias_method :method_missing, :old_method_missing
      end
    
    end
    
    NilClass.with_safe_dereferencing do
      person = nil
      p person.name.upcase
    end
    
    begin
      person = nil
      person.name.upcase
    rescue NoMethodError => e
      p e.message
    end
    
  7. p3t0r zei ongeveer 14 uur later:

    Oeps… je hebt natuurlijk helemaal gelijk. In mijn eerste voorbeeld werd upcase aangeroepen op null en dat kan natuurlijk niet.

    Dit kan allemaal wel:

    class Person{
        def name = "peter" 
        def nilname = null
    }
    
    p = new Person();
    
    assert "PETER" == p.name.toUpperCase()
    assert null == p.nilname
    assert null == p.nilname?.toUpperCase()
    
    p = null
    
    assert null == p?.name?.toUpperCase()
    

    Maar goed, het was een Ruby weblog… ;)

  8. p3t0r zei ongeveer 14 uur later:

    Ai, wat volgorde problemen… tja die oplossing met dat block… zet dan gewoon

    
    begin
      person.name.upcase
    rescue
    end
    
    
  9. RemVee zei 1 dag later:

    Dan wordt het:

    person.name.upcase rescue nil

    Geen slechte notatie, hoewel een method_missing oplossing een stuk sneller is op MRI en waarschijnlijk ook Rubinius, JRuby e.a.

  10. Matthijs Langenberg zei 1 dag later:

    http://ozmm.org/posts/try.html

    person.try(:name).try(:upcase)
  11. Roderick van Domburg zei 1 dag later:

    Dank voor alle dagmethodes! Goede serie met leuke idee├źn.

  12. Michiel zei 1 dag later:

    Wat een reacties opeens. Maar ik heb nog niks gezien (of bedacht) dat beter is dan person && (pname = person.name) && pname.upcase

    Maar voor de volledigheid (in Haml):

    
        %div{:style => "text-transform: uppercase"}
          = Person.find(p_id).name
    

    Tada! Geen null pointer problemen ;-)

(Laat url/e-mail achter »)

   Voorvertoning