ch
Feedback
ToCode

ToCode

前往频道在 Telegram

טיפים קצרים למתכנתים מאת ינון פרק

显示更多
1 421
订阅者
+224 小时
+17
+130
帖子存档
ToCode
1 420
בוקר טוב בגלל באג ב Pipeline (יותר כמו טעות אנוש) הפוסט היום לא נשלח כמו שצריך תוכלו למצוא את הפוסט היומי בקישור: https://www.tocode.co.il/blog/2021-05-aws-lambda-gateway

ToCode
1 420
# בין Implicit ל Explicit במערכות תוכנה אנחנו מכנים שפת תכנות, ספריה או כלי מסוים Implicit אם הכל מצליח "להבין" אותנו בלי שנצטרך לכתוב הוראות מפורשות, ו Explicit כשהכלי דורש שניתן הנחיה מדויקת לביצוע כדי לעשות דברים. שני המושגים הם קצוות של סקאלה ואין אף שפת תכנות שהיא לחלוטין Implicit או לחלוטין Explicit. תמיד יהיו דברים שנציין ודברים שהמחשב יצטרך להבין לבד. והרבה פעמים שפה תהיה Implicit במקומות מסוימים ו Explicit במקומות אחרים. בואו נראה כמה דוגמאות למיקומים על הסקאלה ואז ננסה להבין באיזה צד שווה לנו לבחור ומתי. ## איך נראה ההבדל בין Implicit ל Explicit שפת התכנות python נחשבה יותר Explicit מ perl קודם כל בגלל הכוונה המוצהרת של כותבי שתי השפות, וכמובן בגלל חלק מהבחירות הטכניות בעיצוב השפות. כבר הסעיף השני ב Zen של פייתון קובע ש: > Explicit is better than implicit. ובצד השני לארי וול, היוצר של פרל, כותב ש: > Natural language is naturally punctuated by the pitches, stresses and pauses we use to indicate how words are related. So-called body language'' also comes into play here. Some of this punctuation is written in English, but much of it is not--or is only approximated. Some computer language designers seem to think that punctuation is evil; I doubt their English teachers would agree. השפה, לפי לארי וול, היא לא רק המילים אלא גם מערכת הסימנים שמסביב שנותנת משמעות למילים. דברים כמו שפת גוף שלא מופיעים בדיבור המפורש. דוגמה לבחירה טכנית תהיה לולאת ה foreach של שתי השפות, כאשר ב perl יש לנו את:
for(<"*.txt">) {
    say
}
שמדפיסה את השמות של כל הקבצים שמסתיימים ב txt בתיקיה הנוכחית, ובגירסת הפייתון שלה נראית כך:
from glob import glob

for filename in glob("*.txt"):
    print(filename)
ב perl כתבתי פחות סימנים והשפה השלימה את הדברים שדילגתי עליהם. בפרט ב perl לא הייתי חייב לבחור שם למשתנה הרץ בלולאה אבל ב python הכרחי לבחור אחד. במעבר ל Front End ריאקט היא ספריה יותר Explicit מ Vue וב Back End ריילס היא Implicit ו Django הרבה יותר Explicit. לפעמים אותו כלי יכול לתת לנו ממשקי עבודה שונים, לדוגמה עבודה עם גיט דרך ממשק גרפי לרוב תדרוש ממני לציין דברים הרבה פחות במפורש בהשוואה לעבודה דרך ממשק ה CLI. פיתוח קובץ Makefile הוא תהליך הרבה יותר Explicit מאשר פתיחת פרויקט חדש ב Visual Studio ובניה מתוך הממשק הגרפי. ## אז מה יותר טוב? ככל שנרשום לעצמנו יותר דוגמאות נגלה שקשה לבחור בצורה מוחלטת איזו גישה עדיפה. הרבה חברים שאוהבים את הצדדים המפורשים של Django עדיין מעדיפים לעבוד עם גיט דרך ממשק גרפי. בגדול אפשר לשים לב לכמה נקודות בבחירת ההעדפה שלנו: 1. בעבודה עם כלים Implicit קל יותר לראות תוצאות לפני שמבינים את הכלי עד הסוף. לארי וול כתב על זה באותו טקסט וציין שם שזה בסדר שיהיו לשפת תכנות מספר רמות של הבנה ושהשפה "תסתיר" ממך את החלקים היותר מורכבים עד שתהיה מוכן להגיע אליהם. 2. בעבודה עם כלים שדורשים יותר פרטים, כלומר Explicit, אני יכול לזהות הרבה יותר מהר כשיש לי פער בהבנה. כל האמת מופיעה לי מול העיניים ואם אני לא מבין שורת קוד מסוימת אני יודע לחפש על זה. 3. ההבדל בין הגישות קטן ככל שאני מבין טוב יותר את עולם התוכן. עודף ההקלדה שאני צריך כדי לעבוד עם git משורת הפקודה לא באמת לוקח לי יותר זמן מאשר ללחוץ על כפתורים ב GUI. מה שלוקח זמן זה ההבנה ולימוד הכלי. 4. תחזוקה של קוד Implicit לאנשים שמבינים מה כתוב בו עשויה להיות קלה יותר מתחזוקה של קוד מקביל בשפה יותר Explicit, כי אנחנו צריכים לשנות פחות סימנים כדי להגיע לתוצאה הרלוונטית ויש לנו פחות קוד לקרוא כדי להבין מה בלוק מסוים עושה. אז נכון שאין לי העדפה גורפת לגבי המיקום על הסקאלה בשפות בהן אני עובד ביום יום, אבל כן קל לראות שכשאני צריך ללמוד טכנולוגיה חדשה עדיף לי לבחור בגירסה ה Explicit שלה. בגישה זו אני נתקל בטעויות בשלב הרבה יותר מוקדם, ואני רואה מול העיניים את כל הדברים שאני צריך לקרוא עליהם בשביל להבין מה כל שורת קוד עושה.

ToCode
1 420
# בין Implicit ל Explicit במערכות תוכנה אנחנו מכנים שפת תכנות, ספריה או כלי מסוים Implicit אם הכל מצליח "להבין" אותנו בלי שנצטרך לכתוב הוראות מפורשות, ו Explicit כשהכלי דורש שניתן הנחיה מדויקת לביצוע כדי לעשות דברים. שני המושגים הם קצוות של סקאלה ואין אף שפת תכנות שהיא לחלוטין Implicit או לחלוטין Explicit. תמיד יהיו דברים שנציין ודברים שהמחשב יצטרך להבין לבד. והרבה פעמים שפה תהיה Implicit במקומות מסוימים ו Explicit במקומות אחרים. בואו נראה כמה דוגמאות למיקומים על הסקאלה ואז ננסה להבין באיזה צד שווה לנו לבחור ומתי. ## איך נראה ההבדל בין Implicit ל Explicit שפת התכנות python נחשבה יותר Explicit מ perl קודם כל בגלל הכוונה המוצהרת של כותבי שתי השפות, וכמובן בגלל חלק מהבחירות הטכניות בעיצוב השפות. כבר הסעיף השני ב Zen של פייתון קובע ש: > Explicit is better than implicit. ובצד השני לארי וול, היוצר של פרל, כותב ש: > Natural language is naturally punctuated by the pitches, stresses and pauses we use to indicate how words are related. So-called body language'' also comes into play here. Some of this punctuation is written in English, but much of it is not--or is only approximated. Some computer language designers seem to think that punctuation is evil; I doubt their English teachers would agree. השפה, לפי לארי וול, היא לא רק המילים אלא גם מערכת הסימנים שמסביב שנותנת משמעות למילים. דברים כמו שפת גוף שלא מופיעים בדיבור המפורש. דוגמה לבחירה טכנית תהיה לולאת ה foreach של שתי השפות, כאשר ב perl יש לנו את:
for(<"*.txt">) {
    say
}
שמדפיסה את השמות של כל הקבצים שמסתיימים ב txt בתיקיה הנוכחית, ובגירסת הפייתון שלה נראית כך:
from glob import glob

for filename in glob("*.txt"):
    print(filename)
ב perl כתבתי פחות סימנים והשפה השלימה את הדברים שדילגתי עליהם. בפרט ב perl לא הייתי חייב לבחור שם למשתנה הרץ בלולאה אבל ב python הכרחי לבחור אחד. במעבר ל Front End ריאקט היא ספריה יותר Explicit מ Vue וב Back End ריילס היא Implicit ו Django הרבה יותר Explicit. לפעמים אותו כלי יכול לתת לנו ממשקי עבודה שונים, לדוגמה עבודה עם גיט דרך ממשק גרפי לרוב תדרוש ממני לציין דברים הרבה פחות במפורש בהשוואה לעבודה דרך ממשק ה CLI. פיתוח קובץ Makefile הוא תהליך הרבה יותר Explicit מאשר פתיחת פרויקט חדש ב Visual Studio ובניה מתוך הממשק הגרפי. ## אז מה יותר טוב? ככל שנרשום לעצמנו יותר דוגמאות נגלה שקשה לבחור בצורה מוחלטת איזו גישה עדיפה. הרבה חברים שאוהבים את הצדדים המפורשים של Django עדיין מעדיפים לעבוד עם גיט דרך ממשק גרפי. בגדול אפשר לשים לב לכמה נקודות בבחירת ההעדפה שלנו: 1. בעבודה עם כלים Implicit קל יותר לראות תוצאות לפני שמבינים את הכלי עד הסוף. לארי וול כתב על זה באותו טקסט וציין שם שזה בסדר שיהיו לשפת תכנות מספר רמות של הבנה ושהשפה "תסתיר" ממך את החלקים היותר מורכבים עד שתהיה מוכן להגיע אליהם. 2. בעבודה עם כלים שדורשים יותר פרטים, כלומר Explicit, אני יכול לזהות הרבה יותר מהר כשיש לי פער בהבנה. כל האמת מופיעה לי מול העיניים ואם אני לא מבין שורת קוד מסוימת אני יודע לחפש על זה. 3. ההבדל בין הגישות קטן ככל שאני מבין טוב יותר את עולם התוכן. עודף ההקלדה שאני צריך כדי לעבוד עם git משורת הפקודה לא באמת לוקח לי יותר זמן מאשר ללחוץ על כפתורים ב GUI. מה שלוקח זמן זה ההבנה ולימוד הכלי. 4. תחזוקה של קוד Implicit לאנשים שמבינים מה כתוב בו עשויה להיות קלה יותר מתחזוקה של קוד מקביל בשפה יותר Explicit, כי אנחנו צריכים לשנות פחות סימנים כדי להגיע לתוצאה הרלוונטית ויש לנו פחות קוד לקרוא כדי להבין מה בלוק מסוים עושה. אז נכון שאין לי העדפה גורפת לגבי המיקום על הסקאלה בשפות בהן אני עובד ביום יום, אבל כן קל לראות שכשאני צריך ללמוד טכנולוגיה חדשה עדיף לי לבחור בגירסה ה Explicit שלה. בגישה זו אני נתקל בטעויות בשלב הרבה יותר מוקדם, ואני רואה מול העיניים את כל הדברים שאני צריך לקרוא עליהם בשביל להבין מה כל שורת קוד עושה.

ToCode
1 420
# חלומות והסחות דעת הם מאוד דומים. בשני המקרים אנחנו מדמיינים את עצמנו במקום אחר מזה שאנחנו עכשיו, ובשני המקרים אנחנו אוהבים את התמונה שבדמיון. ההבדל הוא הנכונות להתאמץ והמסלול הריאלי מאיפה שאני נמצא למקום אליו אני רוצה להגיע. כך למישהו אחד לרוץ מרתון יכול להיות חלום שנותן כח להמשיך בשיגרת האימונים, ולמישהו אחר אותו מרתון יכול להסיח את הדעת מהדבר האחר שהוא עובד עליו. הסחת הדעת היא מסוכנת בדיוק בגלל שהיא לא מושגת (לפעמים כי באמת אי אפשר; לפעמים כי את לא באמת מוכנה לעשות את ההקרבה הנדרשת), אבל היא כן מפתה מספיק כדי שנעשה קצת כדי להשיג אותה ובדרך נפסיד את הדברים הטובים שכן אפשר להשיג. לדוגמה מתכנת שמחפש איכות חיים ולא רוצה לעבוד 18 שעות ביום יכול לחלום על אקזיט גדול ולהצטרף לסטארט-אפ. אבל בגלל שהוא לא באמת מוכן להקרבה הנדרשת הוא יעזוב את הסטארט-אפ לפני הזמן ויפסיד גם את האקזיט וגם את איכות החיים. חלום אמיתי צריך ליישר קו עם מי שאתם ועם מי שאתם רוצים להיות, והדרך להגשמה צריכה להסתדר עם אותם הערכים. דמיונות יפים אחרים עדיף להשאיר בצד.

ToCode
1 420
# לא יודע לכתוב בדיקות "אני לא יודע איך לעשות X" זו עמדת ברירת המחדל, המצב ההתחלתי. יש המון המון דברים שאני לא יודע וזה לא מעניין לדבר עליהם. אבל ברגע שהנושא עולה בפעם הראשונה זה משנה הכל. עכשיו, היכולת לכתוב בדיקות הפכה מעוד מיומנות בערימה האינסופית של דברים שאני לא יודע, לחסם שמפריע לי בחיים. לא קראתי את המאמר כי אני לא יודע לכתוב בדיקות. לא בניתי סט בדיקות אוטומטיות לפיצ'ר כי אני לא יודע לכתוב בדיקות. לא התקבלתי לעבודה כי הם היו צריכים מישהו עם ניסיון ב TDD ואני לא יודע איך לכתוב בדיקות. הבעיה עם חסמים זה שככל שהם חוסמים אותנו יותר פעמים, כך הם נראים יותר מפחידים והעלות השקועה נעשית גדולה יותר. העלות השקועה כאן היא אוסף כל ההזדמנויות שפספסתי בכך שלא למדתי איך לכתוב בדיקות מוקדם יותר. בשביל לא ליצור לעצמנו חסמי ידע, ובהינתן שעמדת "אני יודע לכתוב בדיקות" לא זמינה לך כי אתה באמת לא יודע לכתוב בדיקות, שווה לזכור שהמרחק בין "אני לא יודע לכתוב בדיקות" ל- "אני לומד לכתוב בדיקות" הוא בסך הכל רבע שעה עבודה. מספיק Tutorial אחד, וידאו אחד, שינוי שורת קוד אחת - בשביל לכתוב את הבדיקה הראשונה. ובניגוד ל-"לא יודע לכתוב בדיקות", עמדת "אני לומד לכתוב בדיקות" היא כולה פתיחת דלתות: כשאני לומד משהו אני שמח לקרוא עליו מאמרים, לשמוע עליו פודקסטים ולנסות לכתוב עוד בדיקה גם לפיצ'ר הקטן ההוא. דווקא כיף העסק הזה של בדיקות. אני שמח שהחלטתי ללמוד אותו.

ToCode
1 420
# "לי זה לא יקרה" לי זה לא יקרה. אז מה אם האייפון מתלונן שהסיסמה שלי חלשה מדי או שהופיעה במאגר סיסמאות שנפרצו. אין לחץ דווקא היום להחליף סיסמה. הוא בטח אומר את זה לכולם. לי זה לא יקרה. אז מה אם לא ביצעתי Pen Testing מסודר כבר ארבע שנים. ואז מה אם אף פעם אין לי זמן לשדרג את התלויות. המערכת שלי היא לא מסוג המערכות שנפרצות, זה פשוט לא מעניין אף אחד. לי זה לא יקרה. אז מה אם רוב העובדים בהייטק סופגים ירידה בשכר וקושי במציאת עבודה כשמגיעים לגיל מסוים. אני עדיין צעיר וממילא הרבה יותר חכם מכולם. לי תמיד יציעו עבודה, מקסימום יבקשו ממני ללמוד איזה כלי חדש. לי זה לא יקרה. אני יודע בדיוק מה כל שורה במערכת שלי עושה, ולכן כשאני מתקן שורה אין מצב שאני שובר משהו במקום אחר. מתכנתים אמיתיים מעלים מהסביבה המקומית שלהם ישר לפרודקשן. ועכשיו ברצינות. הדרך היחידה להקטין את הסיכון (ושימו לב כאן לבחירת המילה - להקטין - סיכון תמיד יהיה. אסונות קורים). בקיצור הדרך היחידה להקטין סיכונים היא לעבוד נכון: 1. להחליף סיסמאות כל X זמן וכמובן כל פעם שתוכנה אוטומטית זיהתה סיסמה חלשה. 2. לדאוג לשדרג תלויות באופן תדיר ולעבוד לפי תהליכי פיתוח מאובטח שכבר קיימים בשוק. 3. ללמוד כל הזמן טכנולוגיות חדשות ולהישאר בחזית הטכנולוגיה, גם דרך עבודה על פרויקטי קוד פתוח או קורסים אם מקום העבודה חוסם אתכם. 4. לכתוב בדיקות אוטומטיות, לעבוד עם אנשי QA, להריץ Deployment מדורג ולייצר תהליכים אוטומטיים שיזהו כשהמערכת נשברת אצל המשתמשים ויאפשרו לחזור גירסה. עבודה נכונה זה לא משקולת שאתם תקועים איתה ושמונעת מכם לעשות את הקסם שתכננתם לעשות. עבודה נכונה היא המסגרת שמאפשרת את הקסם. מתכנתים טובים יודעים להשקיע את הזמן ביצירת המסגרת בתוכה קל לנו לעבוד.

ToCode
1 420
    argument :id, ID, required: true
  end

  def meeting(id:)
    Meeting.find(id)
  end
end
פה יש כמה דברים חדשים: 1. הפונקציה field מקבלת בלוק (במקום אוסף פרמטרים). 2. בתוך הבלוק אני מפעיל פונקציה בשם argument כדי לציין שה meeting הזה תלוי בארגומנט id 3. הפונקציה meeting שמוגדרת במחלקה עם שם זהה לשם השדה היא האחראית על שליפת המידע. היא מקבלת כפרמטר את הארגומנט שהשדה קיבל וצריכה להחזיר אוביקט שדרכו GraphQL יוכל להמשיך לחשב את התשובה לכל השאילתה. 4. לא הראיתי כאן, אבל אותה פונקציה meeting יכולה לקבל גם אוביקט Context שעובר ישירות מהקונטרולר. אנחנו משתמשים באוביקט זה בשביל להעביר למשל את מזהה המשתמש הנוכחי ולוודא שלמשתמש יש הרשאה לגשת למידע. ## חלק 3: הגדרת ה Controller וה Route בשביל שנוכל להעביר שאילתות מבחוץ לשרת ה GraphQL שלנו צריך לבנות Controller שיקבל את השאילתות ויריץ אותן. ה Generator של ה gem בונה אחד אוטומטית וכך נראה הקוד שלו:
class GraphqlController < ApplicationController
  # If accessing from outside this domain, nullify the session
  # This allows for outside API access while preventing CSRF attacks,
  # but you'll have to authenticate your user separately
  protect_from_forgery with: :null_session

  def execute
    variables = prepare_variables(params[:variables])
    query = params[:query]
    operation_name = params[:operationName]
    context = {
      # Query context goes here, for example:
      # current_user: current_user,
    }
    result = MainappSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
    render json: result
  rescue => e
    raise e unless Rails.env.development?
    handle_error_in_development e
  end

  private

  # Handle variables in form data, JSON body, or a blank value
  def prepare_variables(variables_param)
    case variables_param
    when String
      if variables_param.present?
        JSON.parse(variables_param) || {}
      else
        {}
      end
    when Hash
      variables_param
    when ActionController::Parameters
      variables_param.to_unsafe_hash # GraphQL-Ruby will validate name and type of incoming variables.
    when nil
      {}
    else
      raise ArgumentError, "Unexpected parameter: #{variables_param}"
    end
  end

  def handle_error_in_development(e)
    logger.error e.message
    logger.error e.backtrace.join("\n")

    render json: { errors: [{ message: e.message, backtrace: e.backtrace }], data: {} }, status: 500
  end
end

שווה לשים לב: 1. אם אנחנו רוצים לפנות ל API מתוך Service פנימי, כדאי להשתמש בשורה:
protect_from_forgery with: :null_session
שמאפסת את ה Session כי ממילא אין Session בגישה מסרביס פנימי. 2. בקוד שנוצר אוטומטית ה context הוא אוביקט ריק, אבל באופן כללי מידע שנעביר שם יגיע לאוביקטי הטיפוסים שהגדרנו בסכימה והם יוכלו להשתמש במידע כדי לוודא שלמשתמש יש הרשאות לגשת למידע. בדרך כלל נרצה להעביר שם את המשתמש הנוכחי, כמו שמופיע בהערה בקוד שנוצר אוטומטית. ## חלק 4: דוגמה לשאילתה קוד ה Node.JS הבא מתחבר ל API שיצרנו ומריץ שאילתה שמחזירה את הפרטים על פגישה מספר 1 ואת רשימת האימיילים של אנשי הקשר שמשתתפים בה:
const { request, gql } = require('graphql-request');

const query = gql`
    {
        meeting(id: 1) {
            title
            participants {
                email
            }
    }
}
`
request('http://localhost:3000/graphql', query).then((data) => {
    console.log(data.meeting.participants);
})
שימו לב איך השאילתה מושכת את השדות title ו participants, וגם מציינת איזה שדות מתוך participants לקחת. ב GraphQL יהיה לנו מאוד קל להוסיף סרביסים חדשים או לעדכן את הקוד של סרביסים קיימים כך שימשכו בדיוק את המידע שהם צריכים, בלי שנצטרך לעדכן את אפליקציית הריילס המקורית כל פעם שלקוח צריך מידע חדש.

ToCode
1 420
# איך לבנות GraphQL API לאפליקציית ריילס גרף-קיו-אל מספק דרך מודרנית יותר מ REST לשליפת מידע דרך API, ובצורה בה ה Client קובע איזה מידע הוא יקבל. באופן הזה במקום לפנות ל Endpoint מסוים כדי לקבל את רשימת אנשי הקשר, ואז ל Endpoint אחר כדי לקבל את מספרי הטלפון של כולם, אפשר לפנות ל Endpoint אחד ולבקש לקבל את כל הפרטים במכה אחת. בעבר פרסמתי פה מדריך לשליפת מידע משרת דרך GraphQL. אני ממליץ לקרוא את המדריך בקישור לפני שממשיכים לפוסט של היום, שמראה איך לבנות שרת GraphQL. ## חלק 0: התקנת ה Gem בשביל לעבוד עם GraphQL ביישום ריילס נצטרך להתקין gem ולהפעיל generator. כל ההוראות בדף ה gem בקישור https://graphql-ruby.org/. בקצרה יש להוסיף ל Gemfile את השורה:
gem 'graphql'
ואז להריץ:
$ bundle install
$ rails generate graphql:install
הקוד מייצר את כל התשתית ל GraphQL בתוך היישום שלנו. ## חלק 1: הגדרת הטיפוסים בשביל שהשרת יוכל לענות על שאילתות GraphQL אנחנו צריכים להגדיר את אוסף השדות שאנחנו מאפשרים ללקוחות למשוך, ואת היחסים בין שדות אלה. ב GraphQL זה נקרא הגדרת טיפוסים והגדרת סכימה. סכימה היא המבנה שמארגן את הטיפוסים, וכל טיפוס מתאים למודל במערכת או לטבלה בבסיס הנתונים. לדוגמה אם יש לנו יישום של יומן פגישות נוכל להגדיר את הטיפוס הבא כדי לייצג פגישה:
# app/graphql/types/meeting_type.rb
module Types
  class MeetingType < Types::BaseObject
    description 'A meeting'
    field :id, ID, null: false
    field :title, String, null: false
    field :starts_at, GraphQL::Types::ISO8601DateTime, null: false
    field :participants, [Types::ContactInfoType], null: true
  end
end
כל field מגדיר מאפיין מסוים ש client יוכל לבקש בתוך השאילתה. הפרמטר השלישי לפונקציה field הוא סוג השדה. נשים לב ש: 1. הסוג הראשון ID מייצג פשוט מזהה מספרי 2. הסוג השני, String מייצג מחרוזת 3. הסוג השלישי, GraphQL::Types::ISO8601DateTime מייצג תאריך 4. הסוג הרביעי שרשום בתוך סוגריים מרובעים מייצג מערך של טיפוס מסוג אחר, במקרה שלנו זה מערך של Types::ContactInfoType. אנחנו יכולים לחשוב על טיפוס בתור משהו ש"מתאים" למודל במערכת (ותכף נראה את החלק בקוד שיוצר מודל ומחבר אותו לטיפוס הפגישה), ואז כל field ממופה לפונקציה על אותו המודל. כלומר הקוד הזה יעבוד בגלל שיש לי במערכת מודל של Meeting עם הפונקציות id, title, starts_at ו participants. המודל שמתאים לטיפוס נראה כך:
class Meeting < ApplicationRecord
  has_many :contact_info_meetings
  has_many :participants, class_name: :ContactInfo, through: :contact_info_meetings, source: :contact_info
end
והטבלה בבסיס הנתונים נראית כך:
  create_table "meetings", force: :cascade do |t|
    t.string "title"
    t.datetime "starts_at"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end
הטיפוס השני ביישום הוא הטיפוס שמתאים לאנשי הקשר - Types::ContactInfoType. הקוד שלו נראה כך:
module Types
  class ContactInfoType < Types::BaseObject
    description 'A person in a meeting'
    field :id, ID, null: false
    field :email, String, null: false
    field :phone, String, null: false
  end
end
ועם שני הטיפוסים האלה אנחנו מוכנים להמשיך להגדיר את הסכימה. ## חלק 2: הגדרת הסכימה סכימת GraphQL היא נקודת ההתחלה שמחברת בין האוביקטים במערכת שלנו למידע ש GraphQL API צריך להחזיר ללקוחות. במקרה שלנו אנחנו יודעים שיש מודל בשם Meeting והוא מתאים ל Types::MeetingType, ואנחנו יודעים של meeting יש פונקציה בשם participants שמחזירה רשימה של אוביקטי ContactInfo שיתאימו בתורם ל Types::ContactInfoType. זה אומר שאם יש לי meeting מסוים ביד שקיבלתי מ Meeting.find, אני יכול להשתמש בו כדי להחזיר ללקוח כל מידע שהוא ירצה לגבי מאפייני הפגישה או האנשים שמשתתפים בה. הסכימה אם כן היא נקודת ההתחלה והיא שמחברת בין בקשה שמגיעה מלקוח לאוביקט Meeting במערכת. קוד הסכימה נראה כך:
# app/graphql/mainapp_schema.rb
class MainappSchema < GraphQL::Schema
  query(Types::QueryType)

  field :meeting, MeetingType, null: false do
    description 'Find a meeting by ID'

ToCode
1 420
# דוקר קומפוז לפיתוח הנה טריק דוקר פשוט שחסך לי הרבה זמן היום וחשבתי לשתף. דמיינו שיש לכם מערכת שמורכבת ממספר מיקרו סרביסים ומספר בסיסי נתונים ותורי הודעות כדי לחבר את כל הבלאגן הזה. עכשיו בסביבת פרודקשן הכל טוב יש לנו אימג'ים מסודרים לכל המערכות וכולם שמחים. אבל מה קורה בפיתוח? במצב פיתוח אנחנו משנים קוד כל הזמן ורוצים לראות את התוצאה מיד בלי לבנות מחדש את כל האימג'ים. והטריק בשביל להשיג את האפקט הזה נקרא Docker Volumes. כלומר במצב פיתוח, במקום להריץ את הקוד מהאימג' אנחנו ניקח את הקוד מכונן על המחשב המארח. כל שינוי קוד אצלי על המחשב המארח אוטומטית ייקלט בתוך הקונטיינר ויגרום לטעינה מחדש של הקוד. הנה קובץ docker-compose.yml קטן להמחשה:
version: "3.9"

services:
  mongo:
    image: mongo

  rabbitmq:
    image: rabbitmq

  web:
    build: ./mainapp
    command: ["/usr/bin/wait-for-it.sh", "rabbitmq:5672", "--", "bundle", "exec", "rails", "s", "-p", "3000", "-b", "0.0.0.0"]
    volumes:
      - ./mainapp:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - rabbitmq

  service:
    build: ./service
    command: ["/usr/bin/wait-for-it.sh", "mongo:27017", "--", "/usr/bin/wait-for-it.sh", "rabbitmq:5672", "--", "nodemon", "main.js"]
    volumes:
      - ./service:/myapp
    depends_on:
      - rabbitmq
      - mongo
הקובץ מתאר בסיס נתונים מונגו, תור הודעות rabbitmq, אפליקציית ריילס ראשית ו Micro Service יחיד ב node.js. הקובץ מותאם לסביבת פיתוח ובסביבת פרודקשן יהיה לנו docker-compose.yml אחר. הקובץ מתייחס לשתי תיקיות שנמצאות איתו באותו מיקום: בתיקיה service נמצא Micro Service שכתוב ב Node.js ובתיקיה mainapp נמצאת אפליקציית ריילס. לכל אחד מהם יש Dockerfile משלו ובגלל זה השימוש ב build. אבל אותו Dockerfile מגדיר את התיקיה /myapp בתור תיקיית הבית באמצעות הפקודה:
WORKDIR /myapp
עכשיו כשמגיעים להריץ את הפקודה הראשית של הקונטיינר מה שהקונטיינר רואה בתור תיקיית הבית זה בעצם התיקיה service שנמצאת אצלי על המכונה. ה nodemon שרץ בתוך הקונטיינר עוקב אחרי שינויים בקוד שקורים אצלי על המכונה, וכשאני משנה את הקוד בסרביס אוטומטית nodemon יטען את הקוד מחדש. עם קובץ ה docker-compose.yml הזה מספיק להפעיל פעם אחת:
$ docker compose build
ואחרי זה בתחילת הפיתוח:
$ docker compose up
והמערכת עולה עם כל הסרביסים והאפליקציות ובסיסי הנתונים ומה לא. יש לכם טריקים נוספים לפיתוח קל עם docker-compose או docker? אשמח לשמוע וללמוד, מוזמנים לשתף בתגובות.

ToCode
1 420
# ומה למדתם היום? בתי חרושת עובדים מסביב לשעון וכל יום מייצרים דברים בקצב קבוע וידוע מראש. בתי ספר ניסו לחקות את הטכניקה וכך נוצרו תוכנית הלימודים, השיעורים, המבחנים וגם השאלה שההורים אוהבים לשאול את הילדים בסוף היום: "ומה למדת היום בבית ספר?". עם השנים הפנמנו את המסר ובמעבר לעולם העבודה התחלנו לשאול את עצמנו "ומה בנית היום?", ולצפות שהשקעה תייצר התקדמות. לצפות שאם צריך לכתוב מערכת של אלף שורות אז כל מה שצריך הוא לכתוב 100 שורות ביום כדי לסיים ב 10 ימים. איזה קישקוש. קידוד, כמו הרבה מקצועות יצירתיים, אינו לינארי ואינו צפוי. זאת הסיבה שאנחנו "מפשלים" בהערכות זמנים וגרועים כל כך בישיבות הסטטוס. אני יכול לשבת שבוע על משימה שתיקח למישהו אחר שעה, ובהזדמנות אחרת אותו מישהו אחר ישב שבוע על משימה שתיקח לי שעה. אני לפעמים בטוח שמשימה מסוימת תיקח גג חצי שעה ובסוף כשהיא לוקחת שבועיים אני מסתכל על הקוד ובעצמי לא מבין מה קרה שם. זה לא באג בשיטה, ככה השיטה עובדת. "מה למדת היום", במשמעות האותנטית שלה לא בתור "כמה התקדמתם היום בתוכנית הלימודים" אלא באמת "מה למדת היום?", "למה שמת לב היום?", "מה ראית היום?" - אלה השאלות שכדאי לשאול אחד את השני ואת הילדים שלנו, כדי ליישר קו עם האופי של העבודה היצירתית שמחכה להם.

ToCode
1 420
# אם לא תדע מה הבעיה שיחקתי היום עם המדריך הזה כדי ליצור Web API ב C#. וכמובן שאחרי כמה צעדים לפי ההוראות פתאום באחד הסעיפים לא בדיוק קיבלתי את הפלט הנכון ובמקום אחר פקודה לא בדיוק עבדה. בשלב הבדיקה קיבלתי מסך לבן. לא לדאוג - אני מתכנת. יש המון דברים שאפשר לעשות כדי לגרום לקוד לעבוד: 1. אפשר להדביק את הודעת השגיאה בגוגל ולקוות שיעלה איזה דף מ Stack Overflow. 2. אפשר למחוק את הכל ולהתחיל לעקוב מההתחלה אחרי ההוראות. 3. אפשר לנסות Tutorial אחר. 4. אפשר להיכנס לפרויקט ולהתחיל לשנות קוד באקראי או להוסיף הודעות הדפסה. אבל האמת שיש רק דבר אחד מעניין לעשות כאן - להבין מה הבעיה. וזה קשה, כי אם אתה לא מכיר את הטכנולוגיה בכלל אז ה"להבין מה הבעיה" הולך לקחת אותך למסע כדי לבדוק איזה קוד מתחבר לאיזה Route, ומה זה בכלל Controller, ואיזה Middlewares נמצאים בשימוש ביישום ASP.NET דיפולטי, ומה זה בכלל appsettings.json, ואיך הוא מחליט על איזה פורט להקשיב, ולמה הם כותבים מילים בתוך סוגריים מרובעות מעל שמות של מחלקות או מתודות. במבט ראשון זאת לא נראית כמו הדרך הקצרה ביותר לגרום ליישום לעבוד. במבט שני קל לראות שכשיודעים לענות על כל השאלות שראינו ויודעים לחפש את הבעיה, גם הרבה יותר קל לפתור אותה עבור יישום זה ועבור היישומים הבאים שנכתוב.