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

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

תהנו!

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

הטעויות הכי נפוצות (וגדולות) בפיתוח אפליקציות פלקס

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

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

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

1. שימוש בflex כדי לבנות אתרי אינטרנט או אפליקציות מדור ישן

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

אני זוכר, בתור ילד צעיר למדתי קראטה, with great power comes great responsibility, אמצו את זה כאשר אתם ניגשים לפתח אפליקציה. אם אין לכם אפליקציה שדורשת ויזואליות מסוימת, אנימציות, עבודה עם datasets גדולים וכו', עדיף שתשתמשו בטכנולוגיות אחרות כמו html, css ואפילו פלאש.

Flash this not that

Flash this not that

אפשר לראות פוסט באנגלית על זה כאן

מקור התמונה The Flash Blog

2. האטת האפליקציה עד כדי זחילה על ידי שימוש ביותר מדי containers

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

השקף הזה מדבר על ריבוי containers. הרבה אנשים שמגיעים מhtml לתוך עולם הפלקס (וזה קורה המון) לא מבינים שcontainer  בפלקס הוא component בפני עצמו שיש מאחוריו עשרות (לעיתים מאות) חישובים של גודל, מיקום, מיקום ילדים, גודל ילדים. ועוד יותר גרוע מזה, ולידציות וinvalidations.

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

אפשר לראות כאן צילום מסך של תחקיר שעשו חברים בקהילה מאתר Inside RIA

צריכת זכרון כאשר יש יותר מדי Containers

צריכת זכרון כאשר יש יותר מדי Containers

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

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

יותר מדי container באפליקצית flex

יותר מדי container באפליקצית flex

הדרך הראשונה

והקוד:

 <Application layout="absolute">

     <HBox width="100%">

         <Button label="Left"/>

         <Spacer width="100%"/>

         <Button label="Left"/>

     </HBox>

 </Application>
יותר מדי container באפליקצית flex

יותר מדי container באפליקצית flex

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

הקוד שלנו נראה כך:

 <Application layout="horizontal">

     <Button label="Left"/>

     <Spacer width="100%"/>

     <Button label="Left"/>

 </Application>

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

מספר container נכון באפליקצית flex

מספר container נכון באפליקצית flex

הטיוב האחרון שלנו נראה כך:

והקוד נראה כך:

 <Application layout="absolute">

     <Button label="Left" left="5"/>

     <Button label="Left" right="5"/>

 </Application>

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

3. שימוש בXML

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

XML לא רק לוקח יותר זמן לפרסר אלא לוקח יותר זמן להעביר על הwire ויכול לעלות המון כסף של BandWidth.

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

את האפליקציה הזו אפשר למצוא כאן

דוגמאות לסוגי העברת מידע בין Client לServer

דוגמאות לסוגי העברת מידע בין Client לServer

4. שבירת מוסכמות והרגלי גלישה באינטרנט

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

עם פלקס אפשר להגיע לניווט רגיל לגמרי, אפשר לעשות Bookmark, אפשר להגיע לדף ספציפי עם כתובת ספציפית בלי ליצור SWF's נפרדים, אפשר הכול. כאשר אתן ניגשים לבנות את האפליקציה שלכם, שימו לב לזה, לעשות Deep Linking שיהיה אפשר להגיע לכול חלק מיידית, ללא ניווטים וקליקים מיותרים.

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

5. שימוש מוגזם באנימציות ומעברי עמודים

אני אהיה הראשון שאעמוד לימין הframework והיכולת שלו להתמודד עם משימות גראפיות כבדות, אבל אני גם אהיה הראשון לומר ש"My Account" לא צריך לעוף ב720 מעלות מתוך הקצה הימני של המסך, הוא לא צריך לשנות את הצבע שלו ולא צריך להזיז את הכפתור ב200 קמ"ש אחרי שלחצתי.

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

יודעים מה, כבר עשיתם את זה, תאפשרו למשתמש לכתוב את זה.

כנ"ל עם מוזיקה או Audio Effects.

6. לא לפתח סביבת פיתוח תומכת Craftsmanship או Agile או לא לפתח EcoSystem בכלל

זה נכון לכול טכנולוגיה, בין אם זה flex או אפילו Ruby on rails. לגשת ישירות לפיתוח מבלי להתחשב ולא להנהיג שיטות Tdd/Bdd, Unit Testing, Code Coverage וכו' זה פשוט לא אחראי, זה לא אחראי במיוחד באפליקציות פלקס מכיוון שכשלא עושים את זה, צריכים לבדוק את הכול דרך הUI, לחכות לקומפיילר שירנדר את האפליקציה בכול פעם, זה בזבוז זמן ומשאבים אדירים.

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

הנה כמה:

FlexUnit

FlexMonkey

תפתחו שיטות עבודה איכותיות שמעודדות בדיקות, מעודדות Craftsmanship, מעודדות Clean Code ואחריות מפתחים.

7. לא לעשות שימוש בBuild Machine ובContinuous Integration

היות ואפליקציות Flex עוברות קומפליציה, כלומר מקוד מקור לאפליקציה סגורה, קובץ SWF שיטת הpublish צריכה להיות כזו

תהליך פיתוח ומעבר נכון לProduction

תהליך פיתוח ומעבר נכון לProduction

מה זה אומר בעצם?

זה אומר במפתח עושה Commit לקוד שלו, לתוך מכונה יעודית, במקרה הזה עדיף לעשות שימוש בGIT כי אז הוא יכול לעשות local commits וpush רק כשהוא בטוח שיש אצלו הכול.
המפתח כמובן בודק את הקוד שלו, כך שקוד שנכנס למכונה הוא קוד שעבר בדיקות.
על המכונה הקוד עובד בדיקות נוספות, כדי לבדוק Integration של כול המפתחים, במידה וכול הבדיקות עוברות, הקוד מתקמפל לאפליקציה ועולה לאוויר (אם צריך)

יעיל נכון?

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

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

כרגיל, אשמח לשמוע את דעתכם.

תוסף ל-Flash Builder 4 שמתכנתי פלקס חייבים לנסות

Screen shot 2010-10-03 at 8.02.59 PMאני מתכנת פלקס כבר 4 שנים בערך, מתוכן חצי שנה או אולי קצת יותר אני עובד עם Flash Builder 4.

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

אני מתכנן לצלם השבוע screencast על התוסף הזה עם כול מיני טיפים והוראות של איך עושים בו שימוש וממקסמים אותו עד הקצה, אבל בינתיים, כתבתי על הנושא פוסט באנגלית. אתם מוזמנים לקרוא אותו – Make your life easier with this Flash Builder 4 plugin

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

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

צריכת זיכרון באפליקציות פלקסלאחרונה, יש המון דיבור ובאז ברשת ובקהילת הפלקס על ניהול זכרון בפלקס ועל שמירה של זכרון ברמות נמוכות וניתנות לניהול. אל לנו לסמוך רק על 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מגה בשיא העבודה והעומס שלה.

שמירה על קבצי mxml מסודרים על ידי שימוש ב-ViewHelpers

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

היות ואפליקציות פלקס מכילות לוגיקה שקשורה ל-view, לוגיקה שקשורה ל-services וכן הלאה, אני תמיד בעד לשמור דברים מסודרים ונקיים, כחלק מהעקרונות של clean code. לכן, אני מאוד אוהב שקבצי ה-mxml שלי מכילים אך ורק קוד mxml ללא קוד actionscript כלל וכלל.

איך אני עושה את זה?

אני עושה שימוש במשהו שאני קורא לו ViewHelper. אין להתבלבל אגב עם המימוש של Cairngorm ל-ViewHelper מכיוון שזה לא אותו הדבר, אני עושה שימוש ב-custom class רק כדי לשמור על קוד מאורגן ונקי.

את השיטה הזו אפשר לממש ללא קשר לפריימוורק mvc שאתם עושים בו שימוש (אני אגב משתמש ב-RobotLegs).

מה השימוש לViewHelper?

השימוש הוא בעצם לשמור את כול קוד הActionScript מחוץ לקבצי הmxml שלנו. כמו כן, ליצור משהו גנרי שאנחנו יכולים להשתמש בו דרך כול האפליקציה ושתהיה שפה משותפת. כך למשל, אפשר שהViewHelper יכיל אוסף של ולידציות שאפשר להוסיף לView, כמו כן הוא יכיל Event Listeners שקשורים לאותו View וכו'.

אוקיי, אז איך נראית אפליקציה לדוגמא?

כך נראה מבנה הקבצים שלנו בתוך Flash Builder 4

Screen shot 2010-08-19 at 5.26.08 PM

ניתן לראות שיש לנו את האקליקציה המרכזית, יש לנו View component יש לנו את ה helper של ה view component הזה ויש לנו את הקלאס BaseViewHelper שמכיל את כול השדות והפקודות שכול ViewHelper יורש מהן.

אז בואו נראה איך נראה הקוד של BaseViewHelper

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

package com.kensodev.core
{
	import mx.core.UIComponent;

	public class BaseViewHelper
	{
		private var _view:UIComponent;

		public function BaseViewHelper()
		{

		}

		public function init():void
		{

		}

		public function set view(v:UIComponent):void
		{
			_view = v;
		}

		public function get view():UIComponent
		{
			return _view;
		}
	}
}

אז כמו שאפשר לראות יש לנו כאן referance לview שיכול להיות כול uiComponent וזה בערך הכול, אפשר להרחיב את הhelper לכול צורך שאתם רואים, למשל להצמיד לכול View מודל מסוים או dataProvider שצריך להיות bindable ועוד.

עכשיו, איך נראה ViewHelper שיורש מה base class שלנו?
כך


package com.kensodev.views.helpers
{
	import com.kensodev.core.BaseViewHelper;
	import com.kensodev.views.MyViewComponent;

	public class MyViewComponentHelper extends BaseViewHelper
	{
		public function MyViewComponentHelper()
		{
			super();
		}

		public function get myView():MyViewComponent
		{
			return MyViewComponent(this.view);
		}

		public override function init():void
		{
			//TODO Auto-generated method stub
			super.init();
		}

		public function myButton_clickHandler(event:MouseEvent):void
		{
			// TODO Auto-generated method stub
		}
	}
}

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

עכשיו לחלק האחרון, כך נראה הView שלנו

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
		   width="400"
		   height="300"
		   creationComplete="helper.init()"
		   xmlns:helper="com.kensodev.views.helpers.*">

	<mx:Script>
		<![CDATA[

		]]>
	</mx:Script>

	<helper:MyViewComponentHelper view="{this}" id="helper" />

	<mx:Button id="myButton" click="helper.myButton_clickHandler(event)"

</mx:Canvas>

כמו שאפשר לראות, אנחנו כוללים את הhelper בתוך הview מה שגורם לו להיות "מופעל", כמו כן ניתן לראות שבקליק על הכפתור אני מפעיל פונקציה בתוך הhelper במקום לכתוב את הפונקציה ישירות על אותו הקובץ

דרך אגב, יש מוסכמה על השמות, כך שבעצם קל מאוד למצוא את הקבצים האלה במערכת
אם יש לכם view שנקרא myViewComponent אז הhelper שלו יקרא myViewComponentHelper והמודל שלו יקרא myViewComponentModel והקומנד יקרא myViewComponentCommand וכן הלאה.

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

Screen shot 2010-08-19 at 5.36.53 PM

קוד המקור של האפליקציה נמצא בgit, תרגישו חופשי להוריד ולראות איך זה בנוי.
http://github.com/KensoDev/view-helper-example

סטטיסטיקות Flash Media Server

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

זהו קישור לפוסט המקורי – Flash Media Server Statistics Using Sawmill

השילוב הטבעי בין ריילס לפלקס

שלום לכולם,

אחרי הפסקה ארוכה יחסית אני חוזר עם עוד סקרינקאסט.

הפעם, אחרי מעבר למק אני מדבר על פלקס – Flex ועל ריילס – Ruby On Rails, ואיך הם משתלבות ביחד בצורה טבעית מאוד (לפחות לי).

תיהנו! :-)

בהקלטת הסקרינקאסט הזה אני גם מכריז על קטגוריה חדשה בבלוג שלי: Ruby on Rails

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

השילוב (הטבעי) בין ריילס לפלקס from Avi Tzurel on Vimeo.

Streaming לוידיאו ואודיו – כיצד?

שלום לכולם,

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

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

כאשר ניגשים לבצע אתר שצריך להזרים וידאו למשתמשים צריכים לענות ראשית על כמה שאלות מרכזיות.

1. האם אנחנו באמת רוצים Streaming או שמספיק לנו Progressive. רגע רגע, מה זה אומר בעצם?
2. האם יש לנו שרתים ייעודיים או שאנחנו צריכים לקבל את השירות הזה כחלק מService מאחד מנותני השירותים למחשוב ענן.

סטרימינג אמיתי או פרוגרסיב?

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

מה ההבדל?

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

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

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

Progressive – Progressive עדיין יאפשר את הצפייה בוידיאו אך הסרטון או קטע המוזיקה יורדים אל הלקוח (בו זמנית) ואז מתנגנים.
למשל, אם נלחץ על Play נתחיל בתהליך של זרימת מידע ללקוח ושמירה של הקובץ אצלו בTemporary Internet files, גם אם הלקוח ילחץ על Pause הוא ימשיך את תהליך השמירה ללא תלות בשום פעולה אחרת שלו.

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

יתרונות וחסרונות

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

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

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

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

היתרון המובהק הוא השימוש רק במה שצריכים ולא במה שלא

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

יכול להיות שהשרת שלך יהיה יעודי לWEB אבל כול הסרטונים יזרמו מEC של אמזון לדוגמא.

טכנולוגיות

גם בסטרימיג וגם בפרוגרסיב יש לנו את האפשרות לבחור.

Flash

במידה ונבחר בטכנולוגיה זו נצטרך לבצע המרה של כול הסרטונים שלנו לFLV, סרטונים של WMV או Avi לא יוכלו להיות מנוגנים על ידי Flash Player.
ניתן למצוא היום ממירים בתשלום או חינמיים.

ffmpeg-logo

אחד מהממירים הטובים ביותר שקיימים בשוק הוא FFMpeg שמכילה Command Line עשיר באפשרויות שאיתו ניתן לבצע Tweaking להמרה, איכות, גודל, Frame Rate ועוד.
FFMpeg אינו לשימוש מסחרי, במידה ורוצים לעשות בו שימוש מסחרי צריכים לבצע Open Source לכול מה שנוגע בחלק הזה.

אם למשל עשיתם המרה אוטומטית דרך SQL תצטרכו לחשוף את הקוד, אם עשיתם כול דבר אחר גם אותו תצטרכו לחשוף לדורשים אותו, אחרת לא תוכלו לעשות שימוש (הוגן) בתוכנה.flash-media-server

במידה ונבחר בסטרימינג יש לנו את Adobe Flash Media Server.
תוכנה מעולה זו יכולה לשבת על שרת יעודי שלכם ולבצע Stream של וידאו, אודיו, מידע,לנהל משתמשים, Chat's ועוד ועוד.
התכנות לשרת הזה אינו אינטואיטיבי וידרוש בד"כ מתכנת מיומן עם יכולת למידה גבוהה ביותר.

fetch

לסטרימינג של פלאש אפשר למצוא גם את תוכנת קוד פתוח Red5

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

Avi, WMV ושאר ירקות

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

אפשר גם לבצע Streaming על ידי מימוש פרוטוקול MMS ושימוש בWindows media streaming שבא כחלק Windows 2003.

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

silverlight

היכרות עם Flex – שלום עולם

שלום לכולם,

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

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

בעוד שאני מפתח בעיקר לסביבת מיקרוסופט (C#, Sql Server, .Net) בחרתי דווקא בפלקס כסביבה העיקרית שבה אני מפתח אפליקציות עשירות.

למה דווקא פלקס?

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

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

IDE – הIDE של פלקס (Flex Builder 3) הוא מדהים, יש לו עשרות רבות של Features שיכולים לעזור לנו בפיתוח, Debugging, Remote Debugging פקדים מובנים שמקלים ומריצים את זמן הפיתוח.

עיצוב – מעצבים הם כמו נשים אי אפשר איתם ואי אפשר בלעדיהם (הומור כמובן, לא להיעלב), הסביבה שבה מעצבים לפלקס היא אותה סביבה בדיוק שבה המעצב עובד (99% מהם סביר להניח) שזה בעצם Illustrator, Photoshop או כול תוכנה גרפית אחרת. המתחרה העיקרית של Flex – סילברלייט הוציאה סביבה חדשה (שמבוססת על מוצר של חברה שמייקרוסופט קנתה לפני שנים) ועדיין לא הוטמע מספיק אצל מעצבים.
להערכתי בארץ יש מספר זעום של משתמשים בתוכנה הזו ושל מעצבים שיודעים לעבוד איתה.

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

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

שרתים – אהההה, תכנות צד שרת גורם לי לחייך בכול פעם.
פלקס לא מוגבלת בטכנולוגיה – המערכת שאתם בונים יכולה לצרוך Service שנכתב בJava ובאותה מידה לצרוך אחד שנכתב בc#.
Socket – כנ”ל, יכול להיות Windows ויכול להיות Apache אין שום בעיה, אתם בפלקס לא צריכים לשנות כלום, הכול נשאר אותו דבר.

תאימות דפדפנים – צריך להרחיב? יש מישהו שאין לו Flash Player בדפדפן?
כן, אני יודע יש AdBlocker של FireFox אבל כמה באמת משתמשים בו? הרי בסופו של יום אפליקצית RIA היא חלק עיקרי בתוך אתר אינטרנט, פרסומת היא חלק משני ולכן אם אתם תפתחו אפליקציה מדהימה, אנשים ירצו לראות אותה.

השתכנעתם? יופי נמשיך…

ההתחלה

כשAdobe הציגה את פלאש MX היא הביאה לעולם לבטים גדולים לעשרות אלפי מתכנתים כמוני.
מתכנתים שרצו לאמץ את הטכנולוגיה אבל שנהיים ירוקים למראה הIDE האיום והנורא של Flash.

מתכנתים שלא רצו להתעסק עם Symbols ובמות וLayers וTimeFrame וקוד שמפוזר בכול מקום וקשה לשלוט עליו.
היו המון תלונות ואי שביעות רצון… Adobe הקשיבו…

בשנת 2004 יצא Flex 1 שהתבסס על שפת ActionScript 2.
לאחר שנתיים בלבד יצא Flex 2 שכבר התבסס על ActionScript 3 המעולה

בשנת 2008 הגיע לאוויר העולם Flex Builder 3 והיום הוא הסביבה הרשמית של Adobe לפיתוח של Flex.

השפה

flex מתכנתים ע”י שפה שמבוססת על Xml שנקראת Mxml.

כול דבר שתכתבו ב-Mxml יתורגם מאחורי הקלעים לAs3 טהורה ומהירה.

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

צילומי מסך – IDE

6-23-2009 12-47-52 AM []

6-23-2009 12-48-37 AM []

הסביבה תואמת ל-Windows (אני אישית עבדתי איתה על כול גירסה מאז XP כולל על 7, גם בסביבת 32 וגם ב64 ביט) ולמקינטוש.

Show me the m….xml

אוקיי…

דיברנו על פלקס בכללי, דיברנו על mxml בכללי.
עכשיו קצת לקוד.

האמת שהתכנות ל-Flex מאוד דומה לתכנות של Asp.Net ולתכנות של .Net בכלל.

הפקדים מזכירים את הסביבה, הפקודות והכול.

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

אנחנו יכולים לשים מספר Canvas’s בתוך Canvas אחד ללא הגבלה אלא של שטח מסך :-)

בואו ונתחיל ביצירת פרויקט חדש.

יצירת פרוייקט מתבצע באמצעות קליק ימני בחלק הלבן של החלונית “Flex Navigator” ובחירה ב New—Flex Project

קיבלנו את הדיאלוג הזה

6-23-2009 12-57-01 AM []

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

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

פשוט ע”י Drag&Drop נגרור פקד textInput ופקד button לתוך סביבת העבודה שלנו.

6-23-2009 1-03-23 AM []

ע”י החלונית המסומנת באדום ניתן להם מאפיינים.
לפקד הטקסט ניתן ID של txtUserFullName.
לכפתור ניתן ID שנקרא btnSend

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

ניתן לערוך את כול המאפיינים, כולל X,Y או כול מאפיין אחר העולה על רוחכם ונתמך ע”י פלקס (פלקס מציעה Intellisence מעולה!!!)

להלן דוגמא של איך זה נראה

6-23-2009 1-05-49 AM []

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

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

כך למשל, אנו צריכים להאזין לאירוע click על הכפתור באמצעות פונקציה, בואו ונכתוב את הmxml ואת קוד הas הדרוש. כך זה נראה.

6-23-2009 1-19-48 AM []

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

היות והתוצר הסופי שלנו הוא קובץ SWF הוא יכול לרוץ בדפדפן, בואו ונראה את התוצאה הסופית שלנו.

6-23-2009 1-19-38 AM []

סיכום

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

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

אתם יכולים להוריד את Flex Builder 3 לגרסת ניסיון של 30 יום מכאן