משתנים ב CSS
אחד האתגרים הכי גדולים בתחזוקה של CSS גדול הוא היכולת לשמור על קשר בין כללים שונים, כולל בגדלי מסך שונים. ראינו כבר איך יחידות em ו rem עוזרות לנו בהתמודדות זו, ובשיעור זה נמשיך את האתגר עם יכולת חדשה של הגדרת משתנים.
טיפים קצרים וחדשות למתכנתים
אחד האתגרים הכי גדולים בתחזוקה של CSS גדול הוא היכולת לשמור על קשר בין כללים שונים, כולל בגדלי מסך שונים. ראינו כבר איך יחידות em ו rem עוזרות לנו בהתמודדות זו, ובשיעור זה נמשיך את האתגר עם יכולת חדשה של הגדרת משתנים.
אחת הפקודות הפחות מוכרות של גיט היא git revert. תפקידה לבטל קומיט ישן באמצעות העלאת patch הפוך, קצת כמו מנגנון ביטול רעשים באוזניות. בואו נראה כמה דוגמאות ואז נחשוב אם זה משהו שהיינו רוצים להכניס לרוטינה שלנו.
אז תקופת החגים מגיעה ובין כל ההכנות נזכרתי שרציתי לשלוח את הדוור שלנו לנוח במהלך החגים. כן גם תהליכים אוטומטיים רוצים מדי פעם את החופש שלהם לשבת על שפת הים אתם יודעים ולא לדאוג למי צריך לשלוח איזה עדכון על פוסטים חדשים.
אם לא יהיו הפתעות של הרגע האחרון הדוור ישתמש בסקריפט הבא כדי לזהות שחג היום ולחכות עם סיבוב הדואר היומי שלו עד למוצאי החג. זה לא ארוך ואמור לא להתקלקל לעולם אז אולי יעזור גם לכם:
evenings_ok=(
"Pesach I"
"Pesach VIII"
"Shavuot I"
"Tish'a B'Av"
"Rosh Hashana II"
"Yom Kippur"
"Sukkot I"
"Shmini Atzeret"
)
no_emails=(
"Rosh Hashana"
)
for send_in_evening in "${evenings_ok[@]}"
do
# echo "^[0-9/]+ ${send_in_evening}"
if hebcal $(date +"%d %m %Y") | egrep "^[0-9/]+ ${send_in_evening}" >& /dev/null
then
if (( $(date +%"H") < 20 ))
then
exit 1
fi
fi
done
for holiday in "${no_emails[@]}"
do
if hebcal $(date +"%d %m %Y") | egrep "^${holiday}" >& /dev/null
then
exit 1
fi
done
וכמובן אם אתם לא מהאנשים שמחכים לדוור ומעדיפים לקרוא באתר מוזמנים להמשיך לבקר ולקרוא את הפוסטים החדשים מיד כשהם עולים לאוויר בכל בוקר ב 6.
שנה טובה ינון
מה הצעד הכי קטן שאתם יכולים לקחת בפרויקט? ועד כמה היכולת לקחת צעדים קטנים היא משהו שאתם בכלל חושבים עליו?
חברה סיפרה לי השבוע שהיא מבלה הרבה יותר זמן ב ynet ממה שהיא רוצה לבלות שם. היא לא יכולה לעצור את זה: כל פעם שיש לה שתיים-שלוש דקות פנויות בדיקת חדשות ב ynet או קפיצה לפייסבוק נראים כמו הדבר הכי מתאים לעשות.
דרך אחת לשנות פוקוס היא להפוך את הדברים המועילים בפרויקט לקלים יותר. לייצר משימות של 2-3 דקות שרק מחכות לפעם הבאה שמישהו ירצה להיכנס לפייסבוק או ל ynet.
שני הטיפים שלי להיום אם כך:
צרו רשימת משימות ממש קלות בפרויקט. כאלה שאפשר לסיים ב-5 דקות. זה בסדר גם לפצל משימה גדולה להרבה משימות קטנות כדי שתיכנס לרשימה הזו. תחשבו על דברים כמו לכתוב בדיקה ריקה (רק הכותרת) או לתקן שגיאת כתיב שיושבת שם כבר הרבה יותר מדי זמן.
עדכנו את הגדרות ה DNS ברשת הארגונית כך שכשמנסים לגלוש ל ynet או facebook מגיעים לרשימת המשימות הקלות. משהו כמו Captive Portal שמופיע לפעמים ברשתות ומחייב אתכם להסכים לתנאי שימוש כלשהם כדי שתוכלו להגיע לאינטרנט. אפשר להשאיר כפתור קטן שאומר "עזבו אותי ממשימות עכשיו אני רוצה להמשיך לפייסבוק". אפשר גם לא.
אגב חוץ מלקדם את הפרויקט אני חושב שזה גם ייתן למתכנתים שעובדים אתכם הרגשה טובה יותר בסוף יום עבודה.
שמעתי על גיט לראשונה ב 2009 כשאחד החברים לעבודה נכנס למשרד בפנים קורנות לספר שהוא ראה את האור, והאור הזה מתחיל בגימל. אני לא אחד שעף על טכנולוגיות חדשות אז אמרתי לו שנדבר עוד שבועיים כשיעבור לו. אני נשאר עם SVN.
תשע שנים אחרי ועדיין לא עבר לו, וגם אני כבר מזמן מכור.
גיט עשתה שינוי משמעותי קודם כל בזמינות של כלי ניהול גירסאות. לפני שגיט היתה פופולרית מתכנתים רבים הסתבכו עם התקנה של SVN או CVS והעדיפו לוותר על ניהול גירסאות לחלוטין בפרויקטים קטנים. בהיותה מערכת מבוזרת וקלה להתקנה גיט איפשרה לכל אחד לשלב מנגנון ניהול גירסאות גם בפרויקטים הקטנים ביותר.
אבל את היופי האמיתי של גיט מוצאים בפרויקטים גדולים: בזכות היותה מערכת מבוזרת היא מאפשרת לאלפי מתכנתים ברחבי העולם לשתף פעולה בפרויקטים גם בלי ניהול מרכזי. כיום כל עולם הקוד הפתוח כבר מתנהל בגיט וגם פיתוחים רבים בקוד סגור עוברים לשם.
מתכנתים רבים מגיעים ללמוד גיט ומחפשים איך לבצע בגיט את ה-3 פקודות שהם מכירים ממערכת ניהול הגירסאות הקודמת שלהם או דרך Tutorials של "למד את עצמך גיט בעשרים דקות". זה עובד אבל רק חלקית. כשאתם לומדים תוך כדי עבודה ומדלגים על הבסיס התיאורטי וה Best Practices יהיו דברים חשובים שתחמיצו ויהיו דברים שוליים שתקחו יותר מדי ללב.
בוובינר שהעברתי לא מזמן על גיט עלו מספר שאלות מרתקות שהזכירו לי כמה חשובה ההבנה של הכלי בשביל להבין את הפקודות ומתי להשתמש בכל אחת.
בקורס הזה החלטתי לקחת גישה שונה ממה שרוב הקורסים בנושא git לוקחים: במקום ללמד אתכם כמה שיותר מהר איך להסתדר עם הכלי אני רוצה ללמד אתכם כמה שיותר לאט איך להבין את הכלי. אני רוצה לתת לכם את הכלים להבין מצבים מסובכים ולהיות מסוגלים לצאת מהם בלי לעשות נזק ולהבין את שיטות העבודה המקובלות כך שתוכלו לבחור דרך שמתאימה לכם ולפרויקט שלכם.
התוצאה היא קורס גיט מקיף שמתחיל מההתחלה ובונה לאט את ההיכרות עם הפקודות השונות והיכולות השונות של הכלי. זה הקורס שאם הייתי מתחיל ללמוד גיט ממנו היו נחסכות לי הרבה טעויות והרבה תיסכולים.
אני בטוח שתהנו מהקורס ומקווה שתבחרו להצטרף. אם אתם מנויים לאתר יכולים כבר עכשיו להיכנס ולראות את התכנים. אם לא יכולים להצטרף כמנויים או לרכוש רק קורס זה בתשלום חד-פעמי. זה הקישור לקורס עם הסילבוס המלא וכפתור הצטרפות:
https://www.tocode.co.il/bundles/git
נתראה שם, ינון
אתם משתמשים בכלי ניהול גירסאות לפרויקט שלכם? מצוין. בואו נניח שזה גיט אבל באמת שכל כלי עובד. עכשיו לכו להסתכל בעשר הודעות הקומיט האחרונות בלוג:
$ git log --oneline -10
מי שעובד לבד על הפרויקט אולי יכול להיזכר מה כל הודעה אומרת (כי הוא כתב אותה) וגם זה לא תמיד. ומי שעובד בצוות בכלל בצרה.
אז מה מבדיל בין הודעת קומיט טובה לגרועה? אני חושב שהודעה טובה צריכה לעבור 3 מבחנים:
נתחיל עם הסעיף השלישי כי אותו הכי קל להבין.
גם לבדיקות אוטומטיות יש מגבלות וככל שנקדים להכיר במגבלות אלה כך נבזבז פחות זמן בכתיבת בדיקות לא רלוונטיות.
קחו לדוגמא את המידע שאתם שומרים על S3. נניח שיש לכם במערכת מנגנון שמאפשר ללקוח להעלות קובץ ולשמור אותו על S3 ואחרי זה המערכת שולחת באופן אוטומטי מייל לקבוצה אחרת של לקוחות ליידע אותם שיש קובץ חדש.
בשביל לבדוק את זה נצטרך לפעול באחת או יותר מהדרכים:
נוכל לכתוב בדיקות יחידה על כל אחד מהחלקים הקטנים שבונים את המנגנון המורכב שתיארתי. בדיקות היחידה לא יתקשרו באמת עם S3 או עם שרת המיילים אלא יישארו פנימיות למערכת. זה נחמד אבל לא באמת מוודא שהמנגנון עובד כמו שצריך.
נוכל לפתוח חשבונות בדיקה ב S3 ובשרת שליחת המיילים. אחרי זה נוכל לכתוב קוד שידע לקרוא מידע מ S3 וקוד אחר שיידע לקרוא מיילים נכנסים ובעזרתם לבדוק שהרצף שתיארתי למעלה באמת עובד.
נוכל להחליף את S3 ואת שרת המיילים ב Mock Objects במערכת מהם יהיה לנו הרבה יותר קל לקרוא את המידע ולראות שהמידע אכן התקבל ונשלח כמו שצריך.
אף אחת מהדרכים לא אפקטיבית במיוחד: בדיקות יחידה יבטיחו שהקוד שלי לא יתרסק אבל ממילא רוב הבאגים כאן יגיעו בגלל שאני לא עובד נכון עם השירותים החיצוניים ולכן לא יתגלו בבדיקות היחידה. אותה הבעיה מופיעה כשמחליפים את השירותים החיצוניים ב Mock Objects, כי אנחנו די גרועים בלכתוב אותם ולתחזק אותם.
האופציה השניה היא היחידה שמתקרבת לבדיקה אפקטיבית הבעיה שעלות הפיתוח והתחזוקה יכולות להיות מאוד גבוהות. סיכוי לא רע שיהיו באגים גם בקוד הבדיקה ולכן נקבל הרבה False Positives. בנוסף כל שינוי קטן בשירותים החיצוניים יצריך התאמות גם בקוד הרגיל וגם בקוד הבדיקה שלנו.
במצב כזה הדרך היחידה להתמודד היא לשנות גישה: לכתוב בקוד שלנו שכבת תיאום כמה שיותר רזה שמחברת בין פעולות לוגיות אצלי במערכת לפקודות שיישלחו למערכות החיצוניות ובבדיקות להחליף את שכבת התיאום הזו ב Mock Objects. במידה וקוד התיאום כולל מספיק פונקציונאליות כדי לבדוק את עצמו שווה להשתמש בזה. במידה ולא אפשר לעצור כאן.
לדוגמא אם בניתם מחלקה S3 אצלכם בקוד שיש לה פונקציה writeFile ו readFile אין בעיה לכתוב בדיקה שכותבת קובץ לחשבון בדיקות וקוראת אותו משם. בנוסף מומלץ לכתוב מחלקה S3Test שלא באמת תכתוב ותקרא מ S3 אלא רק תעבוד מול הקוד שלכם בבדיקות כדי לוודא ששאר הקוד עובד כמו שצריך.
אל תשברו את השיניים בשביל לבדוק מנגנונים שקשה לבדוק. עדיף לכתוב במקומם מנגנונים שקל לבדוק ולהמשיך משם.
פיתוח משחק זו דרך די טובה ללמוד שפה חדשה - כי היא משלבת מבני נתונים לשמירה של המידע, חלוקה של הקוד למספר קבצים ולפעמים גם ממשק משתמש ובדיקות יחידה.
בקורס פייתון כאן באתר תרגיל הסיכום הוא פיתוח משחק איקס עיגול וכבר הרבה זמן שרציתי להקליט פיתרון מקיף שלו לטובת התלמידים. ביום חמישי הקרוב מתכנן לעשות את זה יחד עם מי מכם שירצה להצטרף.
קצת בדומה לשלט הזה:
גם הערות בקוד מתחילות עם הרבה כוונות טובות, אבל אז אנחנו מתקנים את הבעיה שבגללה כתבנו את ההערה מלכתחילה ושוכחים למחוק את ההערה. שבועיים אחר כך ואף אחד לא מבין מה השלט הזה עדיין עושה שם אבל לאף אחד גם אין אומץ להוריד אותו.
הרבה פעמים אני מתקן באג או כותב פיצ'ר בלי לכתוב בדיקה כי אני בטוח שזה ירוץ הרבה יותר מהר וממילא התיקון הוא קטן ומאוד ברור. הנה דוגמא מהימים האחרונים. הקוד הבא עובד:
def expired?
expires_at = DateTime.new(card_validity_year.to_i, card_validity_month.to_i, 1, 0, 0, 0) + 1.month
Time.zone.today > expires_at
end
והוא גם כל כך פשוט שכשאתה כותב אותו אתה לא מרגיש צורך לכתוב בדיקת תקינות. אין מה לבדוק פה.
אבל הימים עוברים ומישהו (כן זה הייתי אני) חושב שזה טיפשי לייצר אוביקט DateTime על משהו שמייצג תאריך והשעה לא חשובה בו ולכן "מתקן" את הקוד לגירסא הבאה:
def expired?
expires_at = Date.new(card_validity_year.to_i, card_validity_month.to_i, 1, 0, 0, 0) + 1.month
Time.zone.today > expires_at
end
ופה זה כבר כאב כשזה נשבר, כי בשביל ליצור תאריך ברובי מספיק להעביר 3 פרמטרים ורובי לא ידע מה לעשות עם כל ה-6 אז זרק Exception.
את הבדיקה היה לוקח דקה וחצי לכתוב וממילא היא רצה אוטומטית בכל עדכון של המערכת. ככה היא היתה נראית אם אתם סקרנים:
require 'rails_helper'
describe PaymentToken do
describe '#expired?' do
it 'should return true when card is expired' do
p = PaymentToken.new(card_validity_year: 1.year.ago, card_validity_month: 1)
expect(p.expired?).to be(true)
end
it 'should return false when card is not expired' do
p = PaymentToken.new(card_validity_year: 1.year.from_now, card_validity_month: 1)
expect(p.expired?).to be(false)
end
end
end
ככל שאנחנו לוקחים יותר ברצינות את המערכות שאנחנו כותבים כך מגלים שאי אפשר לברוח משיטות עבודה נכונות גם בדברים הקטנים.