הקוד הזה היה נראה כמו רעיון טוב בזמנו:
x[:foo].presence || 9
הוא אומר שאם המפתח :foo
קיים באוביקט x אז יש להשתמש בערך שלו (אפילו אם הוא 0, כי אנחנו ברובי ולא ב JavaScript), ואם הוא לא קיים ניקח את ערך ברירת המחדל 9.
אני מודה שהייתי צריך את ההתרסקות כדי לראות את הבעיה.
זה עובד:
3.3.5 :003 > x = {foo: 0, bar: 20}
=> {:foo=>0, :bar=>20}
3.3.5 :004 > x[:foo].presence || 9
=> 0
וגם זה עובד:
3.3.5 :006 > x = {bar: 20}
=> {:bar=>20}
3.3.5 :007 > x[:foo].presence || 9
=> 9
אבל זה כבר לא:
3.3.5 :008 > x = nil
=> nil
3.3.5 :009 > x[:foo].presence || 9
(langlets):9:in `<main>': undefined method `[]' for nil (NoMethodError)
x[:foo].presence || 9
כלומר בדיקת ה presence נכשלת כשהאוביקט עצמו לא קיים. עד לפה זה ברור אבל החלק הבא הצליח להפתיע אפילו את ChatGPT, כי ברובי יש אופרטור &.
שמפעיל פונקציה רק אם האוביקט קיים, כלומר:
3.3.5 :011 > x = 10
=> 10
3.3.5 :012 > x&.abs
=> 10
3.3.5 :013 > x = nil
=> nil
3.3.5 :014 > x&.abs
=> nil
אז אפשר היה לדמיין שנוכל להשתמש באותו אופרטור כדי לגשת לערך באוביקט ולכתוב את הקוד הבא:
> x&.[:foo]
אבל זו שגיאת תחביר ברובי.
מה עושים? פה נכנסת לתמונה הפונקציה dig. לא רק שהיא יודעת להוציא מידע מאוביקטים מקוננים אלא שאפשר לכתוב לפניה את סימן הקריאה המותנית וכך לקבל קוד שלא מתרסק. הנה כמה דוגמאות:
3.3.5 :017 > x = {foo: 10, bar: 20}
=> {:foo=>10, :bar=>20}
3.3.5 :018 > x&.dig(:foo)
=> 10
3.3.5 :019 > x = {foo: {foo: {foo: 10}}, bar: 20}
=> {:foo=>{:foo=>{:foo=>10}}, :bar=>20}
3.3.5 :020 > x&.dig(:foo, :bar, :foo)
=> nil
3.3.5 :021 > x&.dig(:foo, :foo, :foo)
=> 10
3.3.5 :023 > x = nil
=> nil
3.3.5 :024 > x&.dig(:foo, :bar)
=> nil