ריבוי מילים בפלקס (או… מה שמובן מאליו למתכנתי רובי און ריילס)

SingularPluralSort.pdf.00

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

בעצם זה נשכח, עד השבוע.

השבוע קיבלתי באג במערכת של לקוח שאומר שכאשר הוא בוחר מודול למחיקה הוא רואה (are you sure you want to delete the selected module) והוא רואה את אותה ההודעה בדיוק כאשר הוא בוחר מספר מודולים.

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

 

var module:String = "module";

			if(list.selectedItems.length > 1)
				module = "modules";

			Alert.show(StringUtil.substitute("are you sure you want to delete the selected {0}", module);

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

מה עשיתי?
1. חיפשתי בגוגל ומצאתי שמישהו עשה porting לקוד של מחלקת Pluralization מריילס ועשה אותה בas3
2. שכתבתי את הקוד וחשפתי החוצה פעולה אחת בלבד

הנה הקוד המלא (לשימושכם החופשי):

		public function PluralizationHelper()
		{
		}

		static:{
			PluralizationHelper.initPluralization();
		}

		private static var pluralWords : Array =
			[
				[/$/, 's'],
				[/s$/i, 's'],
				[/(ax|test)is$/i, '$1es'],
				[/(octop|vir)us$/i, '$1i'],
				[/(alias|status)$/i, '$1es'],
				[/(bu)s$/i, '$1ses'],
				[/(buffal|tomat)o$/i, '$1oes'],
				[/([ti])um$/i, '$1a'],
				[/sis$/i, 'ses'],
				[/(?:([^f])fe|([lr])f)$/i, '$1$2ves'],
				[/(hive)$/i, '$1s'],
				[/([^aeiouy]|qu)y$/i, '$1ies'],
				[/(x|ch|ss|sh)$/i, '$1es'],
				[/(matr|vert|ind)ix|ex$/i, '$1ices'],
				[/([m|l])ouse$/i, '$1ice'],
				[/^(ox)$/i, '$1en'],
				[/(quiz)$/i, '$1zes']
			];

		private static var singularWords : Array =
			[
				[/s$/i, ''],
				[/(n)ews$/i, '$1ews'],
				[/([ti])a$/i, '$1um'],
				[/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '$1$2sis'],
				[/(^analy)ses$/i, '$1sis'],
				[/([^f])ves$/i, '$1fe'],
				[/(hive)s$/i, '$1'],
				[/(tive)s$/i, '$1'],
				[/([lr])ves$/i, '$1f'],
				[/([^aeiouy]|qu)ies$/i, '$1y'],
				[/(s)eries$/i, '$1eries'],
				[/(m)ovies$/i, '$1ovie'],
				[/(x|ch|ss|sh)es$/i, '$1'],
				[/([m|l])ice$/i, '$1ouse'],
				[/(bus)es$/i, '$1'],
				[/(o)es$/i, '$1'],
				[/(shoe)s$/i, '$1'],
				[/(cris|ax|test)es$/i, '$1is'],
				[/(octop|vir)i$/i, '$1us'],
				[/(alias|status)es$/i, '$1'],
				[/^(ox)en/i, '$1'],
				[/(vert|ind)ices$/i, '$1ex'],
				[/(matr)ices$/i, '$1ix'],
				[/(quiz)zes$/i, '$1']
			];

		private static var irregularWords : Array =
			[
				['person', 'people'],
				['man', 'men'],
				['child', 'children'],
				['sex', 'sexes'],
				['move', 'moves']
			];

		private static var uncountableWords : Array =
			[
				'equipment', 'information', 'rice', 'money', 'species', 'series', 'fish', 'sheep'
			];

		private static function makePlural (singular : String) : String
		{
			if (uncountableWords.indexOf(singular) != -1)
				return singular;

			var plural : String = new String();
			for each (var item : Array in pluralWords)
			{
				var p : String = singular.replace(item[0], item[1]);
				if (p != singular)
					plural = p;
			}
			return plural;
		}

		private static function makeSingular (plural : String) : String
		{

			if (uncountableWords.indexOf(plural) != -1)
				return plural;

			var singular : String = new String();
			for each (var item : Array in singularWords)
			{
				var s : String = plural.replace(item[0], item[1]);
				if (s != plural)
					singular = s;
			}
			if(singular == "")
				return plural
			else
				return singular;
		}

		[Bindable(event="pluralizationChangedEvent")]
		public static function pluiralize(count:int, word:String):String
		{
			if(count == 1)
				return makeSingular( word );

			return makePlural( word );
		}

		static private function initPluralization() : void
		{
			for each (var arr : Array in irregularWords)
			{
				pluralWords[pluralWords.length] = [arr[0], arr[1]];
				singularWords[singularWords.length] = [arr[1], arr[0]];
			}
		}
	}

איך מפעילים את הקוד מתוך האפליקציה? ובכן, כך:

PluralizationHelper.pluiralize(count, 'custom field')

הקוד הזה הוא חלק מספרייה שאני משחרר שהיא אוסף Helpers כלליים לאפליקציות פלקס.
את קוד המקור של כול הספרייה (שכרגע מכילה רק את הקלאס הזה אבל תתעדכן בהמשך) אפשר למצוא בgit בקישור הבא
http://github.com/KensoDev/Flex-Generic-Helpers

תהנו!

אם יש שאלות, תרגישו חופשי

ניהול זכרון באפליקציות פלקס או איך לשמור על שפיות

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

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

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

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

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

מה גם, שבעולם של היום שהוא עולם של mobile devices, חשוב לנו להבין שהזכרון הוא משאב עוד יותר מוגבל.

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

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

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

דליפות זכרון

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

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

Event Listeners

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

ניקח את פיסת הקוד הזו למשל

//objectX
objectY.addEventListener(some_event, some_function)

יש לנו כאן בעצם שני אובייקטים, יש לנו את אובייקט Y ויש את אובייקט X.

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

כן כן, תנו לזה לשקוע :-)

ניתן בevent Listeners לומר שהlistener  הוא weak, כלומר הוא לא נחשב כreference מאחורי הקלעים, יש ויכוח מאוד גדול כול פעם שהנושא הזה עולה ב StackOverflow או כול פורום אחר, הרבה אומרים שאדובי היו צריכים לעשות שevent listeners יהיו weak כברירת מחדל, אני חייב לומר שאני לא מסכים עם הקביעה הזו, אני חושב שאנחנו כמתכנתים צריכים לדעת לזרוק את ה listeners לפח בסיום השימוש

משתנים סטטיים

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

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

// static variable
public static var _foo:Foo;
//setting the value
_foo = new Foo();
//clean
_foo = null;

Dictionaries

Dictionaries זה בעצם אוסף של אובייקטים בצורה של מפתח-ערך, על כול שורה יש מפתח ולכות מפתח יש ערך.

אני אנסה להסביר את זה בצורה הטובה ביותר שניתן אבל אנא אמצו את עינכם, זה ממש קסמי Adobe ניהול הזכרון באובייקטים האלה :-)

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

אפשר לקבוע weak key כלומר במידה ואין הפניות (references) לאובייקט X, הוא יאסף על ידי הCollector אבל עכשיו יש לנו מצב מוזר, אפילו שיש לנו ערך בלי מפתח, הערך עדיין שם.

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

אשמח עם מישהו מצוות פיתוח פלקס יבהיר את המצב הזה…

נמשיך.

איך מנקים את המצב הזה?
יש האק, אם עושים לולאת For על הDictionary, כול הערכים שאין להם מפתחות ינוקו גם הם, כמובן בכול מגבלות הreferance הקיימות.

סיכום

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

כול ה"טיפים" האלה אולי יכולים להראות קטנוניים אבל כשמבינים מה קורה מאחורי הקלעים כשיש לנו למשל DataGrid שיש לו ItemRenderes שלכול אחד יש reference אחד שלא מנוקה יכול להבין שבסופו של יום מדובר בכמויות היסטריות לחלוטין של זיכרון.

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

שימוש במיקרופון ותצוגה גראפית בפלקס (Flex)

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

שיהיה בכיף :-)

עדכון (18 מאי) העליתי את הוידאו לVimeo שיהיה יותר נוח לצפות בו.

ScreenCast [Hebrew] Getting microphone data and displaying graphics from Avi Tzurel on Vimeo.

אפשר להוריד את הסרטון בMp4 איכותי יותר מכאן

קוד? כמובן! מכאן


שאלות מראיון עבודה – As3

שלום לכולם,

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

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

בשבוע שעבר, היה לי מקרה אחר, ונדרשתי למבחן בAs3, זה היה מוזר :-) אבל כמובן שלא סירבתי ועשיתי את המבחן.

עברתי! :-) חחח

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

המבחן היה מורכב משלוש שאלות:

שאלה ראשונה
תאר את כול הדרכים באמצעותן ניתן להעביר פרמטרים לאובייקט פלאש

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

שאלה שלישית
ישנו קובץ XML שמכיל את הפרמטרים הבאים
X – מיקום הX של האלמנט
Y – מיקום Y של האלמנט
FromS – שנייה שבה צריך להתחיל
ToS – שנייה שבה צריך להפסיק
Url – מיקום הקובץ

כתוב פונקציה, שמופעלת בכול שניה, שאמורה לקחת את הקובץ SWF מתוך הXML ולהציג אותו על אובייקט וידאו

בהצלחה, אשמח לשמוע תגובות.