ToCode
رفتن به کانال در Telegram
1 419
مشترکین
اطلاعاتی وجود ندارد24 ساعت
-27 روز
-530 روز
آرشیو پست ها
1 419
דוגמת דינו: שמירת תמונות מויקיפדיה
כבר כמה ימים שאני משחק עם קוד כדי להוריד תמונות מויקיפדיה בשביל בוט מילון שאני כותב, וכמו הרבה פעמים בפרויקטי צד אני אוהב לבחור טכנולוגיות שמעניינות אותי כדי לראות איך זה עובד בעולם האמיתי. הפעם לקחתי את דינו ורציתי לראות אם אני יכול להשתמש ב Deno2 לפרויקט ובעיקר מה יהיו הקשיים. סיכום הניסוי בפוסט כאן.
מה בונים
בשביל לשלוח תמונה דרך טלגרם היא צריכה להיות לא גדולה מדי אבל הבעיה שתמונות בויקיפדיה יכולות להגיע בכל גודל. לכן נבנה מיקרו סרביס ב Deno שמקבל מאמר מויקיפדיה (שם הנושא והשפה), מושך את כל התמונות, מקטין אותן ושומר לתיקייה מקומית. אחרי זה אפשר יהיה לקחת את התמונות מהסרביס במקום מויקיפדיה כדי לקבל את הגירסה הקטנה שגם מתאימה לטלגרם.
אפשר לראות את הקוד המלא בגיטהאב:
https://github.com/ynonp/wikipedia-image-fetcher
הקובץ main.ts
פריימוורק ה Web שבחרתי נקרא Hono. כתבתי עליו כאן וכבר הרבה זמן שאני רוצה לבנות עוד דברים בעזרתו. הונו מהיר וקל לשימוש ואני מרגיש שהוא גירסת אקספרס שמתאימה ל 2024. הנתיב הראשון שאני מכניס לפרויקטים פשוט מחזיר שהכל בסדר:
app.get('/up', (c) => {
return c.json({ok: true})
})
וזה יעזור אם יום אחד נרצה לשלב את הפרויקט באיזה מערכת Zero Downtime Deployment כדי שהיא תדע שהכל עלה כמו שצריך, אבל גם באופן כללי בשבילנו שנדע שדברים עובדים.
הנתיב הבא הוא היותר מעניין. הוא מקבל שפה ונושא, בודק אם יש לי כבר תמונות על הנושא הזה. אם אין תמונות הוא יוריד אותן ואם יש הוא רק יחזיר את רשימת התמונות:
app.get('/images', async (c) => {
const { topic, lang } = c.req.query();
if (await lib.hasSavedImages(lang, topic)) {
const images = await lib.getSavedImages(lang, topic);
return c.json(images);
} else {
const images = await lib.downloadWikipediaImages(lang, topic);
return c.json(images);
}
})
ב hono אין בעיה לכתוב קוד טיפול בנתיב אסינכרוני ולהחזיר json. הדבר היחיד שהייתי צריך להתרגל אליו זה שיש פרמטר אחד לפונקציות הטיפול במקום שני פרמטרים כמו באקספרס.
הונו עובד ב Deno אבל גם ב node.js ו bun. יש לו ביצועים מצוינים ובינתיים אין לי שום תלונות לגביו. אנחנו מפעילים את השרת עם:
Deno.serve(app.fetch)
שמירת תמונות ושינוי גודל
הקובץ השני בפרויקט נקרא wikipedia_fetcher.js ושם כתבתי את כל הלוגיקה, ושם גם דברים התחילו להסתבך.
בעיה ראשונה שהיתה לי עם דינו היא שדינו יודע למשוך ספריות מ-3 מאגרים: יש לו את המאגר הרשמי שנקרא JSR, שם אין כמעט חבילות אבל נראה שה Toolchain של דינו עובד איתו הכי טוב. יש מאגר רשמי ישן יותר שנקרא Deno land, שם יש יותר חבילות אבל זה לא נתמך מכל הכלים ויש לו תמיכה בספריות מ npm שם הכלים עובדים די טוב אבל הספריות עצמן לא תמיד נתמכות במלואן. וכך יוצא שמצד אחד אי אפשר להגביל את עצמנו רק ל JSR כי אין שם מספיק חבילות אבל עבודה עם מאגרים אחרים יוצרת בלאגן בפרויקט.
ככה מתחיל קובץ הלוגיקה בפרויקט שלי, ושוב אני מזכיר זה פרויקט ממש קטן שכתבתי בכמה שעות:
import * as fs from "@std/fs";
import * as path from "jsr:@std/path";
import wiki from "wikipedia";
import {basename} from "https://deno.land/std@0.224.0/url/mod.ts";
import {
ImageMagick,
initialize,
MagickGeometry,
} from "https://deno.land/x/imagemagick_deno@0.0.31/mod.ts";
הקובץ כולל ספריות מ JSR, ספריות מ Deno land ועוד ספריות שהוספתי בעזרת פקודת deno add ולכן נשמרו בתוך הקובץ deno.json ברשימת ה import-ים:
"imports": {
"@std/fs": "jsr:@std/fs@^1.0.5",
"hono": "jsr:@hono/hono@^4.6.6",
"wikipedia": "npm:wikipedia@^2.1.2"
},
עכשיו ברור שהבעיה המרכזית פה היא שאני לא מספיק מסודר ופשוט זורק ספריות פנימה לפי קוד הדוגמה באותה ספריה. אבל אולי אם האקוסיסטם היה יותר מאורגן גם אני הייתי משקיע יותר.1 419
פרודוקטיביות (או: מתי לוותר על טייפסקריפט)
הפינוקים של טייפסקריפט באמת עוזרים לכתוב קוד מהר יותר ולעדכן אותו עם פחות טעויות. השלמה אוטומטית חוסכת לעבור לחלון של התיעוד. בדיקת טיפוסים עוזרת לשים לב לטעויות בזמן הכתיבה. ההיכרות של ה IDE עם הקוד מאפשרת שינוי שמות במהירות ובביטחון.
הבעיה היא שעל העלות של טייפסקריפט יותר קשה לדבר כי היא תלוית פרויקט.
בפרויקט שמשתמש במעט ספריות או שהספריות כתובות בעצמן בטייפסקריפט ומגיעות עם הגדרות טיפוסים המחיר יהיה מאוד נמוך. כן קצת Setup ראשוני ומדי פעם להיעזר ב ChatGPT כדי לכתוב הגדרות טיפוסים למשהו מסובך, אבל הרבה אנשים יגידו שזה חלק מתהליך הפיתוח ובכלל המחשבה על הטיפוסים עוזרת להתמקד בקוד שאנחנו צריכים.
בפרויקט יותר ישן או שמשתמש בהרבה ספריות חיצוניות, חלקן ישנות, הסיפור עשוי להיות יותר מסובך. האם כדאי לעשות fork לספריה חיצונית רק בשביל לתקן את הגדרות הטיפוסים שבה? האם עליי להישאר עם גירסה ישנה של טייפסקריפט רק בגלל שאיזה ספריה חיצונית לא תומכת בגירסה החדשה? וכמה זמן צפוי להתבזבז על קריאת הודעות שגיאה קריפטיות בגלל הגדרות טיפוסים רקורסיביות?
העוינות שיש לאנשים לגבי טייפסקריפט יכולה בחלקה להיות מוסברת על ידי פערים בציפיות ובעלויות, ובניסיון העבר שלנו עם השפה. יש אנשים שעבדו שנים עם טייפסקריפט על פרויקטים מסוג מסוים ושילמו עלות נמוכה מאוד עבור הערך שקיבלו מבחינת פרודוקטיביות, ולעומתם אנשים שעבדו על פרויקטים מסוג אחר משלמים מחירים עצומים ולא רואים את הערך (אולי כי הפרויקט קטן או דורש מעט שינויים). טייפסקריפט לא מתאימה לכל פרויקט וזה בסדר לוותר עליה בשביל להרוויח פרודוקטיביות בפרויקטים מסוימים. האתגר הוא להשאיר בראש את התמונה הגדולה. לראות את ההשלכות גם לטווח הרחוק ולקבל החלטה חכמה - האם טייפסקריפט באמת לא שווה את המחיר, או שאני רק מתעצל עכשיו ואשלם על זה בעתיד.
1 419
שיחות עם ChatGPT
- הי אני צריך קוד סקאלה שיוציא את כל התמונות מפוסט בויקיפדיה
- אין בעיה אתה יכול להשתמש ב REST API של ויקיפדיה. הנה הקוד (זורק מלא קוד על המסך).
- שמע זה עובד אבל מדפיס רק את התמונה הראשית של העמוד.
- נכון! באמת לקחתי רק את התמונה הראשונה ברשימה. הנה גירסה נוספת של הקוד שעוברת בלולאה על כל התמונות. (זורק גירסה חדשה של הקוד שלא מתקמפלת).
פה אני יכול לבחור אם להתעקש איתו כדי לקבל גירסה שכן מתקמפלת של הקוד או לעצור ולחשוב. הלכתי לקרוא את הקוד במקום רק להעתיק ושמתי לב שאפילו אם הלולאה שלו היתה נכונה הנתונים שמגיעים מה API כוללים רק תמונה אחת. חזרה ל ChatGPT.
- שומע אני חושב שיש בעיה ב URL שבחרת, יש שם רק את התמונה הראשית מהעמוד.
- וואו כל הכבוד ששמת לב! באמת יש URL אחר שנותן את המידע על כל התמונות, אבל בלי ה URL-ים של התמונות עצמן. זה לא נורא כי אני יכול לפנות לעוד Endpoint עם רשימת התמונות ולקבל רשימה של URL-ים. (זורק עוד מלא קוד שלא מתקמפל).
הייתי רוצה לספר לכם שפה היה לי את השכל לעצור ולהיפרד מה ChatGPT אבל האמת שזה לקח לי עוד דקות ארוכות בהן תיקנתי את הקוד שהוא כתב עד שהבנתי שהוא לגמרי לא בכיוון. כן אפשר לגרום לזה לעבוד אבל חייבת להיות דרך טובה יותר. אז עברתי לתיעוד של ויקיפדיה, מצאתי את ה Endpoint הנכון וחזרתי ל Chat GPT לעמת אותו עם המציאות.
- שומע לויקיפדיה יש URL שנותן את כל המידע של התמונות ואפשר עם String Manipulation פשוט לקבל את ה URL-ים של כל התמונות. (מדביק קוד קצר שעובד).
- אתה ממש צודק ואני שמח שמצאת את זה. (זורק סיכום והסבר ארוכים של הקוד שלי).
נ.ב. אם יום אחד תצטרכו ה URL הזה נתן לי את כל השמות של התמונות בפוסט מסוים:
https://en.wikipedia.org/w/api.php?action=query&titles=table&generator=images&format=json
המידע שמגיע הוא מערך של אוביקטים ולכל אוביקט יש title שהוא שם התמונה, לדוגמה
File:Disambig gray.svg. בשביל להפוך את שם התמונה ל URL בונים URL מיוחד לפי התבנית:
https://en.wikipedia.org/wiki/Special:FilePath/file/{title}
בדוגמה שלנו הקישור לתמונה הוא:
https://upload.wikimedia.org/wikipedia/en/5/5f/Disambig_gray.svg1 419
אבל תמיד זה עבד
מיכה שטרית שר-
> פעם יכולתי לבלוע כדור ארץ שלם.
> רכבתי על אופניים כשהידיים באויר.
> פעם הייתי חיה ואת היית מותק.
> פעם חייתי באמת.
פעם אולי הצלחת בקלות למצוא עבודה בפיתוח PHP. היום אולי כבר צריך לדעת ריאקט בשביל אותה משרה.
פעם אולי אף אחד לא היה צריך אתר שיעבוד גם במצב Offline, או יתחשב בכלל ברוחב הפס של הגולש. היום הציפיות הן שונות.
פעם חשבנו ששפות דינמיות הן ה-דבר, וש JavaScript רק צריכה להיות יותר דינמית. היום קשה למצוא מפתחים שיוותרו על ה Static Typing של TypeScript. וכן זה משפיע על המערכות שנעבוד עליהן וסוגי הבעיות שנצטרך לפתור.
רק בגלל שפעם שיטה מסוימת עבדה לא אומר שהיא טובה או שהיא תמשיך לעבוד. השאלה היא לא מה עבד פעם ואפילו לא למה זה כבר לא עובד. יש דברים שעבדו ועכשיו הם כבר לא עובדים. עכשיו בואו נתקדם ונראה מה עושים עם זה.
1 419
ראספברי פי או VPS או אולי בכלל Serverless - איפה לארח פרויקט צד?
העלייה של פיתרונות Serverless בשנים האחרונות הביאה לשינוי מעניין בהרגלי פרויקטי הצד שלנו. מצד אחד מאוד קל להעלות פרויקט ל Deno deploy או Next ולקבל איחסון בחינם, מצד שני VPS-ים עולים גרושים ובסכומים של 5-10 דולר לחודש אפשר לקבל שרת שיריץ כמעט כל פרויקט צד שתחשבו עליו, ומצד שלישי מחשבים ביתיים רק הופכים לקטנים וחזקים כשאפילו מכשיר כמו Raspberry Pi מגיע כבר עם 8 ג'יגה זיכרון ומעבד 4 ליבות. הנה כמה שיקולים שאני לוקח בחשבון בפרויקטים קטנים:
העלאה לפלטפורמות Serverless
נתחיל עם האופציה הכי פופולרית שהיא העלאה לפלטפורמות Serverless. פלטפורמות אלה מגיעות בשיטת "מתחיל זול ממשיך יקר" ונותנות לכם להעלות פרויקט לאוויר בלחיצת כפתור ובחינם, אבל כשיתחילו להגיע המשתמשים המחיר יעלה.
יתרונות:
1. קל מאוד להתחיל, במיוחד אם הפלטפורמה מסונכרנת עם סביבת הפיתוח שלכם (למשל Vercel לפרויקט next.js).
2. קל מאוד להגדיל כח חישוב כשמגיעים עוד משתמשים, וכך אם במקרה המוצר שלכם הופך ויראלי האתר לא יתרסק.
3. מנגנון CI/CD מובנה שהרבה פעמים יכריח אתכם לעבוד נכון יותר.
חסרונות:
1. על איחסון פרויקט לא מאוד קטן נשלם הרבה יותר מאשר בכל אופציה אחרת.
2. חלק מהעניין בפרויקט צד זה ללמוד, ושימוש בפלטפורמה יכול לפגוע בהיבט הלימודי כי אנחנו מתעסקים בפחות דברים.
3. לפעמים נדרשת יותר עבודה כדי לעשות דברים בצורה שונה ממה שנתמך בפלטפורמה.
העלאה ל VPS
חברות כמו Linode ו Digital Ocean מציעות שרתי VPS במחירים מאוד זולים. באופציה זו אנחנו מקבלים מכונה וירטואלית שכוללת רק מערכת הפעלה ואנחנו צריכים להתקין את כל השאר.
יתרונות:
1. התקנה של שרת היא אחת הדרכים הכי טובות ללמוד איך מערכות אינטרנט עובדות.
2. אנחנו יודעים בדיוק מה מקבלים וכמה זה עולה, ויכולים להשתמש במשאבי המחשוב של השרת לכל צורך שיהיה.
חסרונות:
1. הקמת סביבת CI/CD ומערכת על סביבת פרודקשן לוקחת זמן והשקעה, ולפעמים אנחנו רק רוצים שדברים יעבדו במיוחד בפרויקט צד כשהזמן הוא משאב מוגבל.
2. הקמה עצמאית של כל הסביבה עלולה לחשוף אותנו לבעיות אבטחה בגלל טעויות בקונפיגורציה.
3. אם פתאום המוצר הופך ויראלי ולא התכוננו למצב זה מראש אנו עלולים להסתבך בעת הגדלת הסביבה (הוספת שרתים וחיבור Load Balancer).
ראספברי פי בארון
אופציה שלישית ולא רעה בכלל עם השיפור בביצועים, בצריכת החשמל ובשקט של מחשבים ביתיים היא להריץ את הפרויקט על Raspberry PI שיושב באיזה ארון.
יתרונות:
1. מבחינת לימוד הקמת הסביבה מספקת הכי הרבה הזדמנויות ללמוד איך דברים עובדים. המחיר הנמוך אומר שאנחנו יכולים לארגן Cluster של מספר מכונות, ואפילו להפעיל כלים מתוחכמים כמו קוברנטיס וכמעט בלי עלויות.
2. אין תשלום חודשי על ה"שרת". אחרי ההקמה הפרויקט ממשיך לרוץ לנצח.
חסרונות:
1. אם יום אחד הפרויקט יצליח אנחנו בצרה. נצטרך להוציא את כל המידע ששמור על ה Pi ולהעלות אותו לאחסון אמיתי.
2. ההתעסקות עם הקמת כתובת IP סטטית וחיבור דומיין ל Raspberry Pi שיושב מאחורי Router אצלנו בבית יכולה להיות מייאשת.
3. אין גיבויים ו Raspberry PI יכול פתאום להפסיק לעבוד כי SD Card.
4. הפסקות חשמל וניתוקי רשת הרבה יותר נפוצים אצלך בבית מאשר בחוות שרתים של VPS-ים או פלטפורמות Serverless.
סיכום והמלצות
אז איפה שמים את הפרויקט הבא? בואו נסכם:
1. אם יש לכם פרויקט שמתאים בשפת התכנות למשהו שנתמך על ידי פלטפורמת Serverless לכו על הפלטפורמה.
2. אם יש לכם זמן פנוי ואתם אוהבים שליטה על כל מה שקורה בפרויקט שלכם העלאה ל VPS היא הפיתרון הטוב ביותר.
3. אם אתם אוהבים ללמוד איך דברים עובדים ולהקים לעצמכם סביבות Deployment יצירתיות ובעלות נמוכה, ולא כזה חשוב לכם שהפרויקט יעבוד בצורה אמינה, Raspberry Pi יהיה הפיתרון הטוב ביותר.
1 419
ניסוי Vue - משיכת מידע מקומפוננטה אסינכרונית
בדומה לריאקט גם ויו כבר תומך בקומפוננטות אסינכרוניות ואצלם כרגיל התחביר יותר פשוט ממה שאנחנו מכירים מריאקט. במקום להוסיף פונקציה סודית שמקבלת Promise, הם פשוט מאפשרים לכתוב את setup בצורה אסינכרונית. עשיתי ניסוי עם קומפוננטה פשוטה כדי לראות איך זה עובד. התוצאה בלינק:
https://asyncvue-nr26xan9r48q.deno.dev/
קוד הקומפוננטה
קוד הקומפוננטה יצא מאוד פשוט - יש לייצא אוביקט שמגדיר פונקציית setup אסינכרונית שמייצאת את אוביקט המידע שיגיע ל template. באופן אוטומטי ויו יפעיל את הפונקציה ורק כשהמידע יהיה מוכן ירנדר את התבנית. זה הקוד:
<template>
<h2>({{ person.id }}) {{ person.name }}</h2>
<p>{{ person.url }}</p>
</template>
<script>
import { ref } from 'vue'
export default {
async setup() {
const person = ref({
id: 2,
name: '',
url: ''
})
try {
await fetch(\https://swapi.dev/api/people/${person.value.id}\)
.then(response => response.json())
.then(data => {
person.value.name = data.name
person.value.url = data.url
})
} catch (err) {
console.error(err);
}
return { person }
}
}
</script>
הקוד שמפעיל
בקומפוננטה שקוראת לקוד האסינכרוני לא צריך בכלל להתייחס לסיפור האסינכרוני בזכות קומפוננטת Suspense, וכן פה זה כבר ממש כמו בריאקט:
<script setup lang="ts">
// This starter template is using Vue 3 <script setup> SFCs
// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup
import HelloWorld from './components/HelloWorld.vue'
import Person from './components/Person.vue'
</script>
<template>
<h1>Header</h1>
<suspense>
<person />
</suspense>
<h1>Footer</h1>
</template>
נשים לב שקומפוננטה אסינכרונית יכולה להופיע רק בתוך קומפוננטת suspense, וזה הגיוני כי ה suspense בעצם "מחכה" שה Promise תתבצע כדי להציג את המידע.
מה עוד נשאר
הקוד עובד אבל יש עוד לאן להמשיך בניסוי הזה, לדוגמה אפשר לקבל את ה id מהקומפוננטה החיצונית ולשלוח בקשת רשת נוספת כל פעם שה id משתנה, אבל אם אנחנו כבר עושים את הצעד אולי נרצה להשתמש בספריה כמו vue-query שעושה את זה אוטומטית וגם שומרת cache של בקשות ישנות.1 419
טיפ LLM - איך זה עובד ב X ?
אחד הקשיים בכניסה לפרויקט קוד פתוח, בין אם בשביל לתרום קוד או בשביל לדבג בעיה נקודתית בשימוש בספריה, הוא שספריות וותיקות הן פשוט ענקיות ולכן יכול לקחת המון זמן להבין איזה חלק בקוד הספריה בכלל קשור לאתגר שלנו. מנועי חיפוש לא ממש יעילים פה, כולל החיפוש המובנה בגיטהאב, כי בעבודה כזאת אין לנו באמת מושג מה מחפשים.
מסתבר שגם כאן Chat GPT וחבריו יודעים לעבוד כמו מנוע חיפוש משודרג ולכוון אותנו לפיתרון. אני ניסיתי את זה עם קלוז'ר ועם ספרייה בשם Reagent. רציתי להבין למה כשאני כותב:
[:p :foo/bar]
ה HTML שנוצר משמיט את התחילית foo ומראה לי על המסך רק את הטקסט bar. רק בשביל קונטקסט באופן כללי בקלוז'ר אפשר להשתמש בפונקציה str כדי להפוך דברים למחרוזות וכתיב כזה:
[:p (str :foo/bar)]
באמת גורם ל Reagent להציג את הטקסט המלא :foo/bar.
וכך הגעתי לקוד של Reagent ולשאלה פשוטה - איפה בתוך ערימת הקוד שם נמצאת הפונקציה שמכינה את האלמנטים לריאקט? ועם זה הלכתי ל Chat GPT ושאלתי את השאלה הבאה:
given reagent code
https://github.com/reagent-project/reagent
what part of the code causes reagent to convert the element's content to HTML, assuming the content is a keyword
that is what part converts the expression [:p :foo/bar] to React's createElement ?
התשובה כללה הפנייה מדויקת לפונקציה שאני צריך שנקראת as-element ולקובץ הרלוונטי שנקרא reagent.impl.template.
אחרי שמצאנו איפה הקוד המעניין אפשר להמשיך לעוד כמה שאילתות כדי להרחיב את הסיפור:
1. Check the commit history of the project and list all commit messages related to this line
2. Check the issues of the project and find me all issues that might have led to this line
ככל שנשתמש ב Chat GPT כמנוע חיפוש נוכל להגיע למידע מעניין ולחסוך זמן יקר, בלי שהוא ינסה כל הזמן לענות בעצמו על השאלות וישקע בהזיות.1 419
שלושה צעדים לפיתרון בעיה
1. להשתכנע שיש בעיה ולמצוא את הקריטריון לפיתרון - כלומר להבין איך צריך להיראות העולם כשהבעיה פתורה.
2. להבין מה גורם לבעיה.
3. להציע שינויים שיביאו אותנו לפיתרון ולבחור את הפיתרון המתאים ביותר.
דוגמה קלה - יש לי אתר שנטען לאט. צעד ראשון הוא להבין איך אני מודד את זמן הטעינה של האתר, מה זמן הטעינה הנוכחי ומה זמן הטעינה הדרוש. צעד שני הוא להבין איזה חלקים בקוד האתר גורמים לטעינה האיטית והצעד השלישי הוא לשנות אותם כדי שהאתר ייטען במהירות הדרושה. בכל שינוי אני מודד מחדש את זמן הטעינה וכך אני מבין איזה שינויים רלוונטיים ויכול לבחור את השינוי הנכון למקרה שלי.
דוגמה יותר קשה - אף אחד לא מחזיר לי טלפון כשאני שולח קורות חיים למשרות. צעד ראשון הוא להבין מה מודדים פה. זה יותר קשה כי "אף אחד" זה כנראה סובייקטיבי ותלוי לאיזה אנשים שלחת מראש את הקורות חיים. צריך למצוא דרך לאפיין איזה משרה בדיוק אני מחפש, לארגן רשימה של כמה עשרות משרות פוטנציאליות ואז לשלוח לקבוצה של 5 משרות ולראות כמה מהם יחזרו אליי. אחרי שהשתכנעתי שאנשים רלוונטיים לא חוזרים אליי אני יכול לעצור ולהבין, מה האנשים האלה מחפשים, מה הם מצפים למצוא בקורות חיים של מועמדים. אפשר לדבר עם חברים שעובדים בתחום בתור מגייסים או עם נציגי חברות השמה ולהבין מה הקריטריונים שלהם בסינון. אחרי שהבנו את זה יש לנו רשימה של פערים ואפשר לנסות לתקן את הקורות חיים כדי לסגור את הפערים, תוך המשך מדידה תוך כדי התיקון. בדיוק כמו בסיפור של זמן טעינה, יש שינויים שאפשר לעשות יחסית מהר, ויש שינויים שדורשים השקעה גדולה.
הרבה פעמים הבעיות שגורמות לנו הכי הרבה סבל הן בכלל לא בעיות שאפשר לפתור. נסו את המשחק הזה עם "אין לי מספיק כסף בחשבון בנק", "אין לי כח לקום לעבודה" או "אין לי זמן לעשות את הדברים שאני אוהבת" ותראו שמאוד קשה למצוא קריטריון הצלחה או להבין בצורה אובייקטיבית מה גורם לבעיה. אולי הדבר הכי טוב לעשות עם התחושות האלה הוא להבין איזה בעיות אפשר לפתור שגורמות לנו להרגיש כך.
1 419
דברים טובים קורים כשמחכים (נוד 23 ו await)
יש לנו הרגל מוזר בתוכנה, כשאין פיתרון טוב לבעיה אנחנו כותבים קוד שעושה משהו אחר ועל הדרך גם פותר את הבעיה שלנו. רוב הזמן אנחנו גם משלמים על זה מחיר מתישהו בעתיד, אבל לא תמיד יש אופציה טובה יותר.
וכך אני גם קורא את השורה:
await "good things come to those who await"
שעשויה להופיע בתור השורה הראשונה בקובץ JavaScript בתקופה הקרובה. מה שקורה זה ש node 23 יודע לטעון קבצי JavaScript שמשתמשים בכתיב ה import/export בעזרת require, כלומר כשאני כותב בקוד:
const utils = require('./utils.js');
זה הולך לטעון מעכשיו את הקובץ utils.js בין אם הוא משתמש בכתיב import/export או אם הוא משתמש בכתיב CommonJS. נו זה מצוין רק הבעיה שקובץ שכתוב בכתיב ESM עלול לכלול קוד אסינכרוני ברמה העליונה ביותר, ו require לא יכול להריץ קוד אסינכרוני. מה עושים? פה הסיפור קצת מסתבך ובעצם הקוד שכתבתי יטען את utils.js רק אם הוא קובץ CommonJS או אם הוא קובץ ESM שלא מכיל קריאה ל await ברמה העליונה ביותר.
עכשיו נמשיך ונדמיין ספריה חיצונית שכתובה בכתיב ESM ולא מכילה await ברמה העליונה ביותר ונניח שאני טוען אותה מתוך קוד שלי בעזרת require. בעוד כמה זמן כותב הספריה מוציא עדכון שכולל קריאה ל await ברמה העליונה ביותר של אחד הקבצים בספריה או באחת התלויות שלה. בום. הקוד שלי מפסיק לעבוד אחרי שידרוג כי require זורק שגיאה אם הוא מנסה לטעון קובץ ESM שמכיל await ברמה העליונה ביותר.
לכן כותבי ספריות שלא רוצים שיטענו את הספריות שלהם עם require (כדי שאנשים לא יתעצבנו כשתצא גירסה חדשה שכוללת await ברמה העליונה ביותר), יוכלו להוסיף await כזה מזויף, כלומר:
// prevent people from loading this with "require"
// so their code won't break if I add await in some later time
await "good things come to those who await"
export function twice(x) {
return x * 2;
}
ההערה מעל אגב היא ניסוח שלי.1 419
כמה מילים על חיבור חברתי ומסך כניסה חדש לאתר
העליתי אתמול גירסה חדשה של מסך הכניסה לאתר עם אפשרות לכניסה דרך גיטהאב או לינקדאין. מוזמנים לנסות את זה כאן (לא כזה מלהיב, אבל עובד):
https://www.tocode.co.il/login
אני משתף כמה הערות טכניות לגבי המימוש:
1. בכל המדריכים שראיתי הסבירו שצריך להוסיף לטבלת המשתמשים עמודה של "provider" ועמודה של "identifier", וכך כשמשתמש נוצר דרך לוגין חיצוני אפשר למלא את הערכים מתוך המידע שקיבלנו מאותו לוגין חיצוני, כדי שבלוגין הבא דרך אותו שירות נוכל לחבר את הפעולות לאותו משתמש. אבל השיטה הזאת לא מספיק טובה כי בעולם שלנו יש כבר משתמשים קיימים באתר והם ישתמשו בלוגין החברתי כדי להתחבר לחשבון הקיים שלהם, וגם אפשר לדמיין משתמשים שיתחברו פעם מגיטהאב ופעם מלינקדאין. לכן במקום להוסיף עמודות יצרתי טבלה חדשה של "זהויות" וכל פעם שמשתמש מתחבר דרך לוגין חיצוני אני מוסיף שורה לטבלת הזהויות עם הפרטים. כל שורה בטבלת הזהויות מחוברת לשורה בטבלת המשתמשים, וכך למשתמש אחד יכולות להיות מספר זהויות.
2. הקוד הבא מופעל אחרי שמשתמש עשה לוגין דרך שירות חיצוני כדי להבין מי המשתמש שהגיע:
def self.create_from_provider_data(auth)
authorization = Authorization.find_by(provider: auth.provider, uid: auth.uid)
return authorization.user if authorization
user = User.find_or_create_by(email: auth.info.email) do |user|
user.name = auth.info.name || auth.info.full_name
user.password = Devise.friendly_token[0, 20]
end
user.authorizations.create(provider: auth.provider, uid: auth.uid)
return user
end
הקוד מחפש שורה בטבלת הזהויות, אם הוא מוצא אז הכל קל ומחזירים את המשתמש שמחובר לאותה שורה. אם לא מצא אז הוא מחפש (או יוצר) משתמש עם האימייל שקיבלנו, יוצר שורה חדשה בטבלת הזהויות ומחבר בין השורה החדשה בטבלת הזהויות למשתמש.
3. שימו לב שהקוד יוצר סיסמה לכל משתמש. זה מעניין כי משתמש שהגיע דרך לוגין חיצוני לא נרשם עם סיסמה. הסיפור כאן הוא של תאימות לאחור, כי בטבלת המשתמשים העמודה של סיסמה אינה יכולה להיות ריקה. זה לא נורא כי זו סיסמה אקראית שאף אחד ממילא לא יודע. אם המשתמש ירצה להתחבר יום אחד עם שם וסיסמה הוא יוכל ללחוץ "שכחתי סיסמה" ולאפס את הסיסמה. אם הייתי מתחיל מאפס את הפיתוח היום כנראה שגם את האפשרות לחיבור עם אימייל וסיסמה הייתי שם בטבלת הזהויות.
4. החיבור עצמו בריילס ללוגינים חברתיים כמעט לא דורש קוד ומבוצע דרך ג'ם שנקרא omniauth.
5. במקור רציתי לפתוח לוגין מגוגל וגיטהאב, אבל גוגל החליטו שאני צריך לאמת את זהותי כדי להשתמש בלוגין שלהם וביקשו מכתב הסבר בצירוף וידאו שאמור להראות מה בדיוק אני עושה עם האימייל שיגיע אליי. בינתיים ויתרתי על התענוג. לינקדאין וגיטהאב אפשרו להגדיר לוגין חברתי בלחיצת כפתור.
6. ובאמת מילה על אתרי הלוגין החברתי - הסיפור זהה בכולם כי כולם משתמשים באותו פרוטוקול: פותחים אפליקציה, מקבלים client_id ו client_secret ומכניסים אותם ליישום שלנו. כן חשוב להגדיר מה כתובת האתר שלנו ביצירת האפליקציה כי הם מוכנים לשים מסך לוגין חברתי רק לאנשים שהגיעו מהאתר שמתאים לאפליקציה שפתחתם. בכל אתר דף ההגדרות בו יוצרים אפליקציה מתחבא במקום אחר, בגיטהאב זה היה ב Developer Settings ובלינקדאין יש פורטל של מפתחים ובו יוצרים אפליקציה. מה שעוד היה מבלבל באתר של לינקדאין זה שצריך לבחור בטאב Products את המוצר Sign In with LinkedIn using OpenID Connect בשביל שהלוגין יעבוד.
סך הכל בזכות omniauth כל החיבורים עבדו יחסית חלק ובזכות טיילווינד הארגון מחדש של מסך הכניסה גם היה פשוט.
اکنون در دسترس! پژوهش تلگرام ۲۰۲۵ — مهمترین بینشهای سال 
