ארכיון חודשי: ינואר 2013

לימוד רובי – הדרך להארה

"In Ruby, just like in real life, our world is filled with objects. Everything is an object – integers, characters, text, arrays – everything." — Ruby monk

ישנו אתר מאוד מעניין הנקרא ruby monk אשר מאפשר דרך לימוד אינטרקטיבית עבור שפת התכנות רובי (בטח לא ניחשתם את זה … 🙂 )

האתר מלמד בדרך פחות קונבנציונאלית ממה שנהוג בד"כ, וכתוב בסגנון העושה חיקוי של נזירים בודהיסטים, ולדעתי גם מעט מצחיקה.

העניין הוא, שלא רק שיטת הלימוד שונה, אלא גם יש מספר "ספרים" (כותרות) שניתן ללמוד. יש רמות לימוד שונות, אשר מתבססות על הספרים הקודמים וכיוב' …

הלימוד הבסיסי הוא בחינם, ומכיל 2 ספרים. ישנו גם לימוד מתקדם, אשר הוא בתשלום של 10 דולר לחודש (נכון לכתיבת הפוסט), ומאפשר ללמוד מידע מתקדם יותר, כדוגמת הבנה של metaprogramming באמצעות 2 ספרים נוספים (נכון לכתיבת הפוסט).

אתם מוזמנים להציץ בתוכן הלימוד של הספר הראשון, ולהחליט האם הדרך הזו מושכת אתכם.

שימוש ב DATA של רובי

רובי מאפשרת ליצור איזור מיוחד בסוף קובץ ריצה.

האיזור נקרא __END__ והוא מאפשר לשים טקסט חופשי שם, בלי שהוא ישפיע על הקוד (אלא אם הוא קידוד שפה שונה משאר המידע בקובץ).

אפשר אבל מהקוד עצמו לקרוא את המידע הזה, ולעשות עליו פעולות:

#!/usr/bin/env/ruby
#

DATA.each_line do |l|
  puts l
end

__END__
hello
world

תוכנית זו תציג כל שורה תחת האיזור. DATA הוא קבוע (constant), אשר מחזיק בתוכו מחלקה מסוג File, כך שניתן להשתמש במידע ממש כמו כל סוג IO אחר.
למשל במידה ונרצה לשים template של html אשר יהיה מוטבע בקוד, זו הדרך.
כך תוכלו ליצור למשל ספרייה המציגה הודעת שגיאה ב html ללא צורך לפרוס גם קבצים נוספים.

והנה דרך להציג את התוכנית הקודמת בתצורה אוקטאלית (מבוסס על הקוד מכאן):

#!/usr/bin/env ruby
#

if __FILE__ == $0
  offset = 0
  while (buf = DATA.read(16)) do
    bytes = buf.unpack 'C*'
    puts "%08X: %s\n" % [ offset, bytes.map { |b| " %04o" % b }.join('') ]
    offset += 16
  end
end

__END__
0000000 2123 752f 7273 622f 6e69 652f 766e 7220
0000020 6275 0a79 0a23 440a 5441 2e41 6165 6863
0000040 6c5f 6e69 2065 6f64 7c20 7c6c 200a 7020
0000060 7475 2073 0a6c 6e65 0a64 5f0a 455f 444e
0000100 5f5f 680a 6c65 6f6c 770a 726f 646c 000a
0000117

50 שנים של הדוקטור – או למה DRM ומניעת שימוש פוגעים גם ברשתות ההפצה

סדרת מדע הבדיוני Doctor Who אשר חוגגת יובל, מספקת הרבה מאוד דברים מעניינים.

למשל מרבית הפרקים של העונות הראשונות נעלמו להן מהעולם. יש רק תמונות סטטיות ופסקולים, אבל לא סרטוני הטלויזיה אשר צולמו על ידי BBC 1.

יותר מזה, BBC מוכנה לשלם לכל מי שיש ברשותו עותקים שלמים של אותם פרקים חסרים.

אבל כל זה אינו מפריע לBBC במקביל, להפיץ DRM ולבקש חוקים שימנעו מאנשים להעתיק את התוכן, וזה למרות כי החברה כבר נכוותה קשה בשחזור ואיחזור מידע שהיה ברשותה.

בסופו של דבר, זה אומר כי אם אני כאדם פרטי מעוניין להחזיק עותק מסויים ולבצע בו כרצוני (כל עוד זו לא פעולה מסחרית), אינני יכול.

כיצד זה מתבטא ?
במידה ותרצו לראות סדרות של BBC, ובכן זה תלוי בטוב ליבם, אולי בגלל שאתם מישראל, לא תוכלו לצפות נגיד ב IT Crowed למרות שזו דווקא נמצאת על ידי ה BBC ברשת האינטרנט.

הגבלות בסגנון ה DRM או תלונות על פגיעה בזכויות יוצרים (כאשר אין ספק בכלל כי ה BBC מחזיק בהם) תמיד פוגעות בשימושים לגיטימיים ולעולם לא יפגעו באלו אשר מחליטים לבחור בדרך שהיא טיפה פחות …

אבל זה לא רק מה ש Time lord בן למעלה מ900 שנים מכוכב גליפריי מלמד אותנו בכל פעם שנצפה בעונות הראשונות של הסדרה המקורית, גם ערוץ 1 של מדינת ישראל איבד המון מידע מהארכיון שלו.

מוקדש כחומר למחשבה

Some IX2-200 hacking

I've bought an Iomega IX2-200 NAS as a backup solution. For it's price, it was the best choice imho (but there are much better solutions out there, but a lot more expensive).

It runs Linux (Debian to be exact), but on default it does not enable ssh, and you can choose only sftp or rsync (as a client), but not at the same time.nas_diagnostics_page

Older firmware can open the ssh part as described at the link, however here is how to do it for a newer firmware:

  1. Enter your device
  2. Login
  3. Go to the following address:
    http(s)://<IP ADDRESS>/diagnostics.html
  4. Enable ssh (it might require you to change port due to proftpd acting as sftp server and binded on port 22)
  5. Use the user root with your login password, but provide a prefix of "soho" prior to it. That is, if your password is "1234" (and I hope it is not), then do the following to your password:
    soho1234
  6. Enjoy the access, and remember: "With great power, comes great responsibility" (or something like that)

For my next post, I'll understand how to make it work as an rsync server.

Remember the joy of device hacking 🙂

let's go

הצגתי למאיר בעיה שאני צריך ליצור עבורה פרוייקט, הוא אמר כי שפת go בנוייה (לפחות על הנייר) להתמודד עם מה שאני זקוק לו.

אז הקדשתי שעה (פלוס) להבין קצת יותר לעומק את השפה.

go היא שפת תכנות שבמקור תוכננה על ידי מהנדסי גוגל בשעה החופשית שלהם, והפכה להיות שייכת לחברה.
היא משוחררת בקוד פתוח, ורצה על מגוון מערכות הפעלה, אך בלינוקס היא דורשת קרנל עדכני (יחסית), וגם libc עדכני, מה שאומר שהפצות עם קרנל נמוך מ2.6.23 יהיו בבעיה.

השפה מאוד מעניינת, ולרוב עושה רושם כנחמדה בעיני לתכנות. נראה כי היא מצד אחד מנסה להביא את כל מה שטוב במספר שפות (כן, גם תחבירי פסקל ופיתון מאוד בולטים שם), אבל מצד שני, גם מכניסה מספר דברים מאוד מפחידים אליה. מספיק פחידים בשביל להיות בעייתיים מאוד בעיני.

הנה משהו אחד כזה – משפט if אשר מאוד בעייתי בעיני:

if a := UserAge(); a < 12 {
 ...
}

ניתן (במידה ורוצים) להציב ולבצע מספר פעולות במשפט הif. ד"א לא ניתן לשים סוגריים ל if.

אני מוצא גישה כזו נוראית. בשפות בהן ניתן להציב בתוך ה if אני מתנגד מאוד לשתף פעולה עם גישה כזו, ותראו אותי כותב במשפט לפני כן פעולת הצבה, ורק שורה מתחת להצבה אני בודק.
ב go התחביר מראש מנסה להפריד בין בדיקה ללוגיקה (כיאה לשפות שמכבדות את עצמן כדוגמת פסקל ופיתון), אבל עדיין מאפשרות להציב בתוך משפט תנאי, זה מכניס יותר בעיות מאשר להקל על התכנות. נראה כאילו רצו לרצות כאן מתכנתי C או משהו בסגנון.

לעומת זאת, אחד הדברים המעניינים בשפה (שלמדתי, ישנם דברים שעדיין לא למדתי ושעה זה פחות מידי זמן ללימוד), הוא הלולאה – for. בgo יש רק סוג לולאה אחת, וזו לולאת for כאמור.
למי שאינו יודע, ברוב השפות, לולאת for היא בעצם לולאה הזהה בגישה שלה ללולאת while, רק בלולאת while ניתן לספק תנאי עצירה (ובשפות מסויימות התנאי גם יכול להיות הצבה כתנאי – תתביישו לכם), בgo מראש הבינו את זה, ולכן יש לולאה שתיאורטית מאוד דומה ל c, אבל היא מכילה הרבה יותר יכולות וכוח ממנה, ובעוד אשר משתמשי c או כאלו הרגילים לתחביר מבוסס C יוכלו לתכנת בה באותה צורה (רק בלי סוגריים), חלק מהדברים אינם מחייבים את אותה הגישה.

לשפה יש מצביעים, אבל בניגוד למצביעי C, המצביעים האלו שומרים את כתובת הזיכרון של משתנה, או תוצאה (של פונקציה) בלבד, עד כמה שאני מבין.

לחרדת הקורא צפריר, go מהודרת סטטית לelf (בלינוקס) את קבצי הריצה שלה (אלא אם משתמשים ב gccgo), מה שאומר שהם יכולים לרוץ על כל מערכת המתאימה להם. כלומר גם אם קימפלתם אותם על debian stable, תוכלו להריץ אותם על arch ללא צורך למקפל מחדש.
ואם זה לא מספיק להחריד את צפריר, אז יש לשפה גם מנהל חבילות משל עצמה במקום לסמוך על הפצה כזו או אחרת …

התחלתי ללמוד את השפה באמצעות מערכת הנקראת go-tour, והגעתי שם לשקופית 27.
מעניין מה אחשוב על השפה אחרי מספר ימים או שבועות של שימוש בה, ולא אחרי שעה אחת.

סה"כ שעה אחת אינה מספיקה באמת להתרשם משפה, אבל היה קל מידי להבין את השפה, או אולי זה הניסיון שלי בללמוד שפות תכנות שהקל עלי מאוד. בכל מקרה, אנסה להמשיך ללמוד אותה כשיהיה לי זמן.

בדיקות אוטומטיות אינן התשובות להכל

TDD – ‏Test Driven Development היא גישה או תפיסה האומרת כי כל שורת קוד אותה אנחנו מפתחים, אנחנו קודם יוצרים לה בדיקה שתיכשל, ואז כותבים את הקוד שיגרום לבדיקה לעבור, ובכך מונעים הרבה מאוד בעיות בפיתוח, וכן יכולים לזהות ריגרסיה כאשר זו מגיעה בקוד. כבר כתבתי על הנושא מספר פעמים כאן בבלוג.

לאחרונה היה באג בספרייה בשם ActiveRecord, אשר במצב מאוד מיוחד, היה ניתן לגרום למתודה find_by_‎ לבצע SQL Injection. הבאג ד"א היה ניתן לניצול רק במצבים מאוד מדוייקים של שימוש במתודות, ולכן אם לא היה שימוש בגישה מסויימת, לא היה ניתן לנצל אותם, אבל הבאג היה עדיין קיים, והמליצו לכולם לשדרג לתיקון הבעיה.

בגדול מאוד, הבאג התרחש רק במצב קיצון מסויים. הבאג מאוד "פשוט": הספרייה לא תרגמה תוכן כמחרזות, אלא פרשה את זה כאובייקט רובי, ואז היה ניתן להזריק קוד (בעיקר ל SQL). העניין הוא, כי במרבית השימושים במתודה, הבאג לא התקיים.

כלומר מרבית השימושים בActiveRecord מעולם לא היו גורמים לבעיות או מסכנים אתר מסויים ב SQLi. ועכשיו נשאלת השאלה: האם TDD מסוגל לעלות על מקרה קצה כל כך קיצוני ?

האם ב99.9 אחוז מהבדיקות שתעשה הבדיקה תצא תקינה, האם תדע לכתוב את הבדיקה 0.1 בה תצליח לגלות באג ?

לדעתי האישית, התשובה היא – כי לרוב לא. יש גבול לרמת הבדיקות שניתן לבצע בשביל לעלות על בעיות.
כלומר יש עדיין הרבה מאוד גורמים שלא ניתן לחשוב עליהם מראש שיכולים לגרום לקוד שלנו לדווח על באג, אבל לא ניתן לכתוב אין סוף בדיקות לכך.

בדיקות אוטומטיות כן חשובות, ואינני אומר לרגע אחד לוותר עליהן, אבל הן אינן תחליף לבדיקות נוספות, כדוגמת QA איכותי, וכן מבחן "המשתמש האידיוט" שעושה מה שבא לו בתוכנה, והתוכנה צריכה לדעת להתמודד עם זה.

בדיקות אוטומטיות בסופו של דבר באות להוכיח כי מה שבודקים באמת מתקיים, אבל הן אינן מוכיחות כי אין באגים בקוד, אלא העדר באגים במה שנבדק, אלא אם הבדיקה עצמה מכילה באג זה או אחר.

מיקרוסופט ו PC – סוף עידן (?)

מיקרוסופט השיקה את Windows 8. מערכת ההפעלה הזו מראש הגדירה ממשק אשר מתאים למערכות בעלות מסכי מגע, והחברה הגדירה כי זו הגישה שלה.

השוק, כהרגלו מיהר לאמץ את גרסאות מיקרוסופט גם לשולחן עבודה "רגיל", והחל להתלונן כי הוא לא נוח או מתאים. הבעיה היא לא רק שינוי כללי של תפיסה גרפית, אלא העובדה כי מערכת ההפעלה אינה הותאמה לעבודה עם עכבר, שלא לדבר על עבודה עם מקלדת (וזאת למרות שיש טאבלטים עם מקלדת נוספת אליהם).

ms-surface - windows 8אני חייב לציין כי למעט מספר בעיות קטנות יחסית, כאשר מתעסקים עם Microsoft Surface בחומרה שאליה החברה כיוונה, המערכת מתפקדת מצויין ונוחה מאוד (כאמור למעט בעיות קטנות).

העניין הוא, שכאשר מתחילים לרצות לאנוס את מערכת ההפעלה לעולם שולחן העבודה.
בראש ובראשונה, הממשק אינו מתאים לעכבר, וזאת למרות שניתן לבצע איתו פעולות שונות, אבל ישנם פעולות אשר לא יעבדו לכם בכלל כאשר תעשו זאת, או יתפקדו פחות טוב מאשר האצבע שלכם. להמשיך לקרוא

deadlock טכנולוגי

לאפל ולי יש 2 דברים משותפים – שנינו התעסקנו באותה שפה שהכניסה לנו כסף, ושנינו החלטנו ללכת למערכות מודולאריות במקום המצב שהיה עד אז.

הסיבה של אפל ושלי היא שהגענו לקצה גבול הטכנולוגיה בעולם שהיינו בו, והיינו צריכים למצוא משהו חדש מודולארי יותר.

הייתי פעם מתכנת דלפי בתשלום, וכן גם VB. מיקרוסופט הרגו את השווקים שפיתחתי אליהם, ורצו להציג עולם חדש לגמרי שהיה טוב עבורם עסקית, אבל פגע במקומות שעבדתי בהם, אז החלטתי לעבור סופית לעולם הלינוקס.

מיקרוסופט עושה פעולה כזו ד"א, הרבה מאוד. היא מחליטה מה נוח כלכלית עבורה, וכל מי שמסתמך על העולם שהיא יצרה, צריך ליישר קו, גם אם השקיע 500,000,000 יורו בפיתוח עד עכשיו בטכנולוגיה איקס, עכשיו הוא צריך לזרוק הכל כי מיקרוסופט בחרה בגישה אחרת.

הרבה פעמים הסיבה של מיקרוסופט לעשות את זה, היא פשוטה – החברה אינה רואה עוד עתיד ארוך טווח והם מתכננים את האסטרטגיה הבאה שלהם בנושא.

להמשיך לקרוא

gbak ויכולותיו

ל Firebird ישנם כיום (החל מגרסה 2), שתי תוכנות לביצוע גיבויים של מסד הנתונים, כל אחת מספקת יכולות שונות מחברתה. כאשר לפני גרסה 2, היא החזיקה רק תוכנה אחת בשם gbak.

התוכנה מאפשרת לבצע "גיבוי חם" – כלומר גיבוי בזמן העבודה על מסד הנתונים ללא גרימת נזק כלשהו. אבל זה לא החלק המעניין באמת בנושא.

יש לה הרבה יכולות כדוגמת:

  • המרה בין סביבה לסביבה (למשל Little/Big Endian,‏ 32 מול 64 ביט וכו)
  • דחיסת מידע וניקוי המידע שנשמר ממחיקות (למשל)
  • שינוי פעולות אבטחה, כדוגמת שינוי משתמש ו/או סיסמה למסד הנתונים
  • גיבוי למספר קבצים קטנים את המידע של מסד הנתונים
  • שדרוג בין גרסאות של השרת, ובכך להמיר דברים לפי הצורך

ועוד פעולות נוספות, כדוגמת שחזור אותו המידע 🙂

כמו כן, אין הגבלה בביצוע הגיבויים למיקום של מסד הנתונים, כלומר ניתן להתחבר למסד נתונים שממוקם במקום אחר ולבצע את הגיבוי אצלך על המחשב.

לאחרונה נכתב פוסט המתאר כי שימוש במילים stdin ו stdout מפורשות מילולית, וכתיבתן אינה מתייחסת להתקנים עצמם הקיימים במערכות יוניקס שונות. הסיבה לכך מסתבר, היא שהפעולות שgbak זקוק להן, אינן נתמכות בהתקנים האלו, כדוגמת ביצוע seek על מידע, ולכן כאשר הפקודה רואה את המילים הללו, היא מבינה כי היא צריכה לזרוק פלט למסך או לקרוא קלט ממנו, כלומר התוכנה מתייחסת למילים אלו בצורה מפורשת.

העניין הוא, שFirebird מסוגל לרוץ גם במערכות כדוגמת Windows, בהם אין התקנים כאלו בכלל, אבל עדיין הפקודה תומכת במילים הללו, אשר כאמור מפורשות מילולית.

עוד קצת מידע בנושא מתעוד רשמי של הפקודה.

IPC באמצעות Redis ורובי

Redis הוא מסד נתונים אשר אינו מבוסס SQL וכתבתי עליו רבות כאן בעבר.

לאחרונה נדרשתי לפרק מערכת שלי למספר תתי מערכות, ולהעביר מידע בניהם. כמובן שיש הרבה מאוד דרכים לעשות זאת, כדוגמת Message Queue ‏(RabitMQ, ZeroMQ וכו'), עבודה עם PIPES ביוניקס, עבודה בתצורת client server ועוד הרבה מאוד דרכים כיד הדמיון והמערכת הטובה לכם. אני החלטתי ללכת על Redis.

הסיבה לכך פשוטה: אני צריך לבצע פעולה, ואחרי שתפסתי את האירוע, המידע לא צריך להישמר בזיכרון. יותר מזה, אם וכאשר יש הפסקת חשמל, אתחול או אפילו איבוד מידע, אזי אין לי צורך לבצע שוב את הפעולה. כלומר היא נכונה רק לשלב שבו היא נשלחה.

דבר נוסף, הוא שהמערכות שלי כתובות ברובי, ורק מה שצריך לקבל כאירוע לא נכתב, וRedis גם ככה כבר מותקן, ואני כבר מכיר אותו, ויש לי מעט מאוד זמן לכתוב את השינויים.

לRedis יש מספר דרכים לשלוח ולקבל אירועים, אבל אני הולך לדבר כאן על Publish ו PSubscribe. האחרון מאפשר להאזין להודעות באמצעות תבנית חיפוש (עם כוכביות), במקום שם מלא ומדוייק שאחיו Subscribe מספק.

לשם כך צריך לפחות 2 פרוססים שונים, האחד שולח הודעות, והשני מקבל.

שליחת הודעות מאוד פשוטה:

require 'rubygems'
require 'redis'

redis = Redis.new(:host => 'localhost')

10.times do |i|
  redis.publish("number_#{i + 1}", Marshal.dump({:number => i}))
  sleep( (i + 1) / 10.0)
end

התחברתי לRedis, והתחלתי לרוץ על כל המספרים מ0 ועד 9.
יצרתי מפתח שההתחלה שלו הוא number_‎ ולאחר מכן המספר. המבנה חשוב מאוד בשביל להצליח להאזין לאירוע.
לאחר מכן, יצרתי תצורה בינארית עבור Ruby Hash ושלחתי אותה כמידע שאני זקוק לו לשימוש.
כאשר הפעולה מסתיימת, אני נח כמות של מילישניות לפי המספר, החישוב לדעתי די ברור כאן.

הפרוסס השני צריך להקשיב לאירוע:

require 'rubygems'
require 'redis
redis = Redis.new(:host => 'localhost')

redis.psubscribe('number_*') do |on|
  on.pmessage do |pattern, event, message|
    puts "pattern: #{pattern}, event: #{event}, message: #{Marshal.load(message)}"
  end
end

כאן אנחנו מקשיבים לפעולות אשר נשלחות עם סוגי publish השונים.
יש האזנה לאירועים באמצעות תבנית (pattern) ומכאן המקדם בעצם של הפקודה (p).
הפקודה בעצם מכניסה אותנו לתוך בלוק אשר דרכו אפשר להאזין למגוון סוגים של אירועים.
כן האירוע שצריך הוא pmessage לו אני מאזין, וכאשר הוא מתרחש, הקוד בתוכו מורץ.
אני משחזר כאן את המידע הבינארי חזרה למחלקות של רובי, ובכך העברתי מידע סיריאלי לחלוטין של רובי.
חשוב להבין כי זוהי פעולה שהיא blocking, ולכן כאשר מריצים אותה, אין המשך כלפי מטה לקוד.

התוצר כאן יהיה בסגנון הבא:

pattern: number_*, event: number_1, message: {:number=>0}
pattern: number_*, event: number_2, message: {:number=>1}
pattern: number_*, event: number_3, message: {:number=>2}
pattern: number_*, event: number_4, message: {:number=>3}
pattern: number_*, event: number_5, message: {:number=>4}
pattern: number_*, event: number_6, message: {:number=>5}
pattern: number_*, event: number_7, message: {:number=>6}
pattern: number_*, event: number_8, message: {:number=>7}
pattern: number_*, event: number_9, message: {:number=>8}
pattern: number_*, event: number_10, message: {:number=>9}