Tag Archive for 'XML'

onEnterFrame #29

  • Кто хочет получать 150k USD в год?
  • Как Одноклассники API сам себя перехитрил
  • А мне нравится Windows 7, Magic Mouse — говно, Logitech лучше всех
  • Спасибо macphoria.ru за клавиатуру
  • Как я выпрашивал закрытый бета тест Путь Героя
  • Компьютерная академия Шаг
  • Баг Flash Player 10.1 и веселые баги в Адобовском Багтрекере
  • Как надо писать книги
  • Написал бота для своей игры
  • Новые демки Unity 3D
  • Статьи Тома Крха по P2P во флэше
  • Немного про Оracle и Android
  • Joa Ebert написал свой Flash Player (JITB)

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

Скачать

Чертов баг в Flash Player 10.1.

Вот XML.

<data xmlns:Bla="bla"><Bla:bla></bla:bla></data>

Очевидно, что он неправильный. И 10.0 это говорит прямо, тогда как 10.1 пофиг. Вчера это вылезло в неплохую головную боль на полдня.

Как в Adobe написать про баг?

Скорость vs OOP

Как выяснилось, AS3 компилятор необычайно туп и не проводит никаких оптимизаций. Ну, может быть какие-то совсем простые.

Довелось мне посидеть оптимизировать небольшую библиотечку, на которой стоят все мои остальные. В ней происходит разбор XML, да через e4x, но все же разбор и создание некоторых объектов на основе этого XML. При этом, проходят некоторые проверки функциями isЧто-то(XML), которые представляют собой всего 1-3 строчки. Вызываются такие функции для каждого элемента, то есть часто.

А теперь смотрим тест.

var t: int;
var v: int = 5;
var w: int = 6;

for ( var i: int = 0; i &lt; 5; i++ )
{
  t = getTimer();
  for ( var j: int = 0; j &lt; 1000000; j++ )
  {
    var k: Boolean = func( v, w );
    //var k: Boolean = v &gt; w;
  }
  trace( getTimer() - t );
}

private function func( v: int, w: int ): Boolean
{
  return v &gt; w;
}

Выполняется у меня под 400ms.

Если раскомментировать вторую строку и закомментировать первую, то время падает до 90-100ms. В 4 раза. Полмиллисекунды тут, полмиллисекунды там и уже набираются десятки. Вспомним, что при 30fps на 1 кадры выделяется 33 миллисекунды. А ведь еще столько кода нужно выполнить.

Более того,

var t: int;
for ( var i: int = 0; i &lt; 5; i++ )
{
  t = getTimer();
  for ( var j: int = 0; j &lt; 1000000; j++ )
  {
    var k: int = value;
    //var k: int = _value;
  }
  trace( getTimer() - t );
}

public function get value(): int
{
  return _value;
}

та же фигня. Геттер по сути та же функция с теми же проблемами.

Я пошел и заменил все вызовы этих функций на сами функции. Любой программист знакомый с ООП сочтет меня дураком, потому что я копирую код. Пойдет на форум и напишет какой я тупой и не знаю ООП. Запихнет все эти скопированные куски в функции и будет горд собой. Вот он и вопрос. Скорость или ООП? По крайней мере пока компилятор остается таким говнистым, что сам не соображает как лучше сделать.

Dependency Injection

Dependency Injection и принцип Inverse of Control в программировании используются уже давно. Я как понял DI — это термин, который Мартином Фаулером используется для обозначения того же IoC. Что же это такое и как его использовать нам, простым флэш разработчикам.

Все просто. Основная идея — при создании объекта ему даются (инжектируются) уже готовенькие объекты, в которых он нуждается (зависимости). Каждый из нас, интуитивно полагая, что это гуд, использовал Dependency Injection в той или иной мере. Вот простой код.

public class One{
    public function One( two: ITwo )
    {
        this.two = two;
    }
}

public class Two implements ITwo {
    ...
}

new One( new Two() );

Как видно, объекту класса One мы передаем объект с интерфейсом ITwo при создании. Перед нами простой пример так называемого Constructor Injection. Также есть Property Injection, когда класс предоставляет public свойство или сеттер, через который ожидает получить некоторый объект, в котором нуждается. Идея проста до безобразия и позволяет проще переиспользовать классы, которые друг на друга не завязаны.

Противоположный подход — создавать все dependency внутри класса, при этом классы становятся привязанными друг к другу, что усложняет внесение изменений.

Интереснее же все становится, когда на сцену выходит IoC Contaner. Под контейнером понимается некоторое пространство для объектов, который по определенным правилам эти объекты сам конфигурирует и сам подставляет dependencies куда и кому нужно. Проще говоря, некоторый фреймворк, который сам соображает (с нашими подсказками) как собрать те или иные объекты и какие зависимости им нужны.

AS3.

Речь пойдет о чисто AS3 проектах. Они обычно небольшие в отличие от Flex проектов, которые можно сравнить с Java Enterprise проектами. Если в Java существует толпа IoC фреймворков (как я понимаю самый популярный Spring Framework), то для ActionScript до недавнего времени все было как-то грустно. Конечно, AS3 язык гораздо моложе, чем Java, скажете вы.

Между тем я случайно наткнулся на Syringe, который мимикрирует со Spring, повторяя метод конфигурации через XML, но последняя версия выпущена в бородатом 2006м. Видим, что какие-то попытки перенести функциональность Spring на ActionScript3 велись, но не были поддержаны сообществом, потому что сообщество не было к этому готово. И, конечно же, потому, что большинство AS3 проектов небольшие, на одного программиста и дизайнера.

Какое-то время уже существует официальный Spring ActionScript (бывший Prana). Этот фреймворк вроде бы развивается и сейчас остановился на версии 0.8.

Favour of the month безусловно является Parsley, версия 2 которого радикально изменилась и поддерживает множество вкусных фишек. В том числе и для pure AS3 проектов.

Применение.

Как-то давно на основе Parsley 1.* я набросал себе небольшой фреймворк, который использую при создании и конфигурировании видео плееров и фото галерей.

Значит, допустим у нас есть видео плеер с архитектурой MVC. Разные проекты предусматривают разное расположение визуальных элементов и разные функции. Чтобы каждый раз не копипастить все это, делаем максимально decoupled компоненты и собираем их IoC контейнером при помощи XML конфига.

Вот пример такого конфига для одного из плееров. Видим команды контейнеру по сборке объектов. Любую можно убрать или добавить и плеер пересоберется подругому. Порой, даже не нужно перекомпилировать проект. Конечно, данный пример не использует таких мощных фишек контейнера, как инжектирование по типу, просто мой старенький этого не умеет.

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;container&gt;
&lt;commands&gt;
&lt;!-- Скин плеера --&gt;
&lt;object name=&quot;skin&quot; class=&quot;com.qq.videoplayer.skin.PPSkin&quot; /&gt;
&lt;!-- Сам плеер --&gt;
&lt;object name=&quot;player&quot; class=&quot;ru.valyard.media.mediaplayer.VideoPlayer&quot;&gt;
&lt;constructor-args&gt;
&lt;object ref=&quot;skin&quot; /&gt;
&lt;/constructor-args&gt;
&lt;!-- Контейнер для контролов --&gt;
&lt;view name=&quot;controls&quot; class=&quot;ru.valyard.upa.mvc.views.FixedContainer&quot; /&gt;
&lt;!-- Компонент показывающий картинку до проигрывания --&gt;
&lt;view name=&quot;preview&quot; class=&quot;ru.valyard.media.mediaplayer.views.controls.PreviewView&quot; parent=&quot;controls&quot;&gt;
&lt;property name=&quot;widthRule&quot;&gt;W&lt;/property&gt;
&lt;property name=&quot;heightRule&quot;&gt;H&lt;/property&gt;
&lt;/view&gt;
&lt;!-- Анимашка, которая моргает во время буферизации --&gt;
&lt;view name=&quot;buffering&quot; class=&quot;ru.valyard.media.mediaplayer.views.controls.BufferingView&quot; parent=&quot;controls&quot;&gt;
&lt;property name=&quot;xRule&quot;&gt;W*.5&lt;/property&gt;
&lt;property name=&quot;yRule&quot;&gt;H*.5&lt;/property&gt;
&lt;/view&gt;
&lt;!-- Большая кнопка Play в центре --&gt;
&lt;view name=&quot;bigPlay&quot; class=&quot;ru.valyard.media.mediaplayer.views.controls.BigPlayButton&quot; parent=&quot;controls&quot;&gt;
&lt;property name=&quot;widthRule&quot;&gt;W&lt;/property&gt;
&lt;property name=&quot;heightRule&quot;&gt;H&lt;/property&gt;
&lt;/view&gt;
&lt;!-- Экран как видео проигралось --&gt;
&lt;view name=&quot;stopView&quot; class=&quot;ru.valyard.media.mediaplayer.views.controls.StopView&quot; parent=&quot;controls&quot;&gt;
&lt;property name=&quot;xRule&quot;&gt;W*.5&lt;/property&gt;
&lt;property name=&quot;yRule&quot;&gt;H*.5&lt;/property&gt;
&lt;/view&gt;
&lt;!-- Верхние контролы --&gt;
&lt;view name=&quot;dynamicpanel2&quot; class=&quot;ru.valyard.media.mediaplayer.views.containers.DynamicContainer&quot; parent=&quot;controls&quot;&gt;
&lt;property name=&quot;yRule&quot;&gt;10&lt;/property&gt;
&lt;property name=&quot;xRule&quot;&gt;5&lt;/property&gt;
&lt;property name=&quot;inactiveYRule&quot;&gt;-70&lt;/property&gt;

&lt;/view&gt;
&lt;view name=&quot;top&quot; class=&quot;ru.valyard.upa.mvc.views.HorizontalContainer&quot; parent=&quot;dynamicpanel2&quot;&gt;
&lt;constructor-args&gt;
&lt;int&gt;0&lt;/int&gt;
&lt;/constructor-args&gt;
&lt;/view&gt;
&lt;!-- Fullscreen --&gt;
&lt;view name=&quot;fullscreen&quot; class=&quot;ru.valyard.media.mediaplayer.views.controls.FullscreenButtonView&quot; parent=&quot;top&quot;/&gt;
&lt;!-- Share --&gt;
&lt;view name=&quot;shareButton&quot; class=&quot;ru.valyard.media.mediaplayer.views.controls.ShareButtonView&quot; parent=&quot;top&quot;/&gt;
&lt;view name=&quot;popout&quot; class=&quot;ru.valyard.media.mediaplayer.views.controls.PopoutView&quot; parent=&quot;top&quot;/&gt;
&lt;!-- Основные контролы --&gt;
&lt;view name=&quot;dynamicpanel&quot; class=&quot;ru.valyard.media.mediaplayer.views.containers.DynamicContainer&quot; parent=&quot;controls&quot;&gt;
&lt;property name=&quot;yRule&quot;&gt;H-42&lt;/property&gt;
&lt;property name=&quot;inactiveYRule&quot;&gt;H&lt;/property&gt;
&lt;/view&gt;
&lt;view name=&quot;panel&quot; class=&quot;ru.valyard.upa.mvc.views.HorizontalContainer&quot; parent=&quot;dynamicpanel&quot;&gt;
&lt;constructor-args&gt;
&lt;int&gt;2&lt;/int&gt;
&lt;/constructor-args&gt;
&lt;/view&gt;
&lt;!-- Watermark --&gt;
&lt;view name=&quot;watermark&quot; class=&quot;ru.valyard.media.mediaplayer.views.controls.SymbolView&quot; parent=&quot;dynamicpanel&quot;&gt;
&lt;constructor-args&gt;
&lt;string&gt;Watermark&lt;/string&gt;
&lt;/constructor-args&gt;
&lt;property name=&quot;xRule&quot;&gt;W&lt;/property&gt;
&lt;/view&gt;
&lt;!-- Play/pause buttons --&gt;
&lt;view name=&quot;playpause&quot; class=&quot;ru.valyard.media.mediaplayer.views.controls.PlayPauseView&quot; parent=&quot;panel&quot;/&gt;
&lt;view name=&quot;prev&quot; class=&quot;ru.valyard.media.mediaplayer.views.controls.PreviousView&quot; parent=&quot;panel&quot;/&gt;
&lt;view name=&quot;next&quot; class=&quot;ru.valyard.media.mediaplayer.views.controls.NextView&quot; parent=&quot;panel&quot;/&gt;
&lt;view name=&quot;line&quot; class=&quot;ru.valyard.media.mediaplayer.views.controls.LineView&quot; parent=&quot;panel&quot;&gt;
&lt;property name=&quot;widthRule&quot;&gt;W-playpause.width-sound.width-next.width-prev.width&lt;/property&gt;
&lt;property name=&quot;leftSpace&quot;&gt;1&lt;/property&gt;
&lt;property name=&quot;topSpace&quot;&gt;12&lt;/property&gt;
&lt;property name=&quot;rightSpace&quot;&gt;4&lt;/property&gt;
&lt;property name=&quot;scrollerDist&quot;&gt;6&lt;/property&gt;
&lt;/view&gt;
&lt;!-- Sound on/off button --&gt;
&lt;view name=&quot;sound&quot; class=&quot;ru.valyard.media.mediaplayer.views.controls.HorizontalSoundView&quot; parent=&quot;panel&quot;/&gt;
&lt;!-- Share window --&gt;
&lt;array name=&quot;sizesArray&quot;&gt;
&lt;string&gt;640x480&lt;/string&gt;
&lt;string&gt;480x360&lt;/string&gt;
&lt;string&gt;320x240&lt;/string&gt;
&lt;/array&gt;
&lt;view name=&quot;shareView&quot; class=&quot;ru.valyard.media.mediaplayer.views.controls.ShareView&quot; parent=&quot;controls&quot;&gt;
&lt;property name=&quot;xRule&quot;&gt;W*.5&lt;/property&gt;
&lt;property name=&quot;yRule&quot;&gt;H*.5&lt;/property&gt;
&lt;property name=&quot;sizes&quot; ref=&quot;sizesArray&quot;/&gt;
&lt;/view&gt;
&lt;!-- Ad --&gt;
&lt;view name=&quot;ad&quot; class=&quot;ru.valyard.media.mediaplayer.views.thirdparty.TremorMediaAdsView&quot; parent=&quot;controls&quot;&gt;
&lt;property name=&quot;widthRule&quot;&gt;W&lt;/property&gt;
&lt;property name=&quot;heightRule&quot;&gt;H&lt;/property&gt;
&lt;property name=&quot;id&quot;&gt;123&lt;/property&gt;
&lt;/view&gt;

&lt;module class=&quot;com.qq.videoplayer.modules.PlayCount&quot; name=&quot;playcount&quot;&gt;
&lt;constructor-args&gt;
&lt;string&gt;http://qq.com/qq.php&lt;/string&gt;
&lt;/constructor-args&gt;
&lt;/module&gt;

&lt;!-- Debug actions --&gt;
&lt;action class=&quot;ru.valyard.media.mediaplayer.actions.TestAction&quot; name=&quot;*&quot; priority=&quot;3&quot; /&gt;

&lt;!-- XML loader class --&gt;
&lt;object name=&quot;loader&quot; class=&quot;ru.valyard.media.mediaplayer.loaders.PlaylistXMLLoader&quot; /&gt;
&lt;method name=&quot;registerLoader&quot;&gt;
&lt;object ref=&quot;loader&quot; /&gt;
&lt;/method&gt;

&lt;!-- Embed HTML template --&gt;
&lt;property name=&quot;embedHTMLTemplate&quot;&gt;
&lt;![CDATA[&lt;object width=&quot;%W%&quot; height=&quot;%H%&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://www.qq.com/swfs/embed.swf?id=%ID%&quot;&gt;&lt;/param&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;&gt;&lt;/param&gt;&lt;param name=&quot;allowscriptaccess&quot; value=&quot;always&quot;&gt;&lt;/param&gt;&lt;embed src=&quot;http://www.qq.com/swfs/embed.swf?id=%ID%&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;%W%&quot; height=&quot;%H%&quot;&gt;&lt;/embed&gt;&lt;/object&gt;]]&gt;
&lt;/property&gt;
&lt;!-- --&gt;
&lt;property name=&quot;pageURLTemplate&quot;&gt;
&lt;![CDATA[http://www.qq.com/video/%PAGE%]]&gt;
&lt;/property&gt;
&lt;!-- --&gt;
&lt;property name=&quot;dataURLTemplate&quot;&gt;
&lt;![CDATA[http://www.qq.com/xml/%DATA%]]&gt;
&lt;/property&gt;

&lt;property name=&quot;mediaURLTemplate&quot;&gt;
&lt;![CDATA[http://www.qq.com/videos_final/%URL%]]&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;/commands&gt;
&lt;/container&gt;

С первого взгляда все сложно, хотя такая конфигурация успела сохранить мне кучу времени при создании однотипных модульных видео плееров немного отличающихся по скину и функциональности.

Плюсы.

Про конфигурирование плеера я уже сказал. Это несомненно плюс. Этот подход позволяет максимально разделить используемые объекты друг от друга и легко переиспользовать их в других проектах. В осноном, избавляет от неободимости писать кучу конфигурационного кода. Конечно, приведенный здесь конфиг не является идеальным примером. СтОит почитать хелп Parsley, который имеет куда более продвинутые способы конфигурирования.

Минусы.

Есть какая-то граница, при переходе которой конфигурирование контейнера превращается в более time consuming процесс, чем если бы я просто сел и руками все написал. Конечно, IoC позволяет создать задел на будущее для возможных исправление и переработок, но как показывает практика, часто нужно просто быстренько наклепать код и избавиться от него. В небольших проектах смысла использовать особо нет, если не имеет место сборка маленьких reusable компонентиков.

Flex on, чтоб его, Rails

For those who don’t understand Russian and came here after googling  “ActionController::MethodNotAllowed (Only get, put, and delete requests are allowed.)” scroll down to the very bottom of the page for some English text.

Ruby on Rails развивается такими темпами и такими сложными заковыристыми скачками, что на 2.3.2 код из книги декабря 2008 в мартеапреле 2009 уже не работает. Я это уже испытал конечно на книгах 2006-2007 годов, но все же.

Значит, сама по себе связка Flex + Ruby on Rails очаровательна. Скорость, с которой делаются простые приложения, потрясает. Поэтому я сижу читаю сейчас
Flex on Rails: Building Rich Internet Applications with Adobe Flex 3 and Rails 2 (поделитесь PDFчиком у кого есть). И, как я уже сказал, пробую на себе одни грабли за другими.

Сейчас проблема вот в чем — во Flex HTTPService хоть и говорит, что свойство method = “GET|POST|PUT|DELETE”, но на практике же посылаются только GET и POST запросы (я уверен, 90% пользователей знакомых с понятием GET и POST о том, что есть еще как минимум PUT и DELETE и не подозревают). А рельсам уж очень хочется их использовать, например GET accounts/1 возвращает инфу об акаунте, PUT accounts/1 обновляет акаунт на сервере инфой с клиента, а DELETE accounts/1 удаляет пользователя. В книге об этом написано, надо к URL прибавлять ?_method=put. Но в 2.3.2 это уже не работает. На любые попытки такого обращения получаем.

ActionController::MethodNotAllowed (Only get, put, and delete requests are allowed.)

Гугление показало, что хорошо бы посылать _method=put переменной вместе с основным запросом. Запрос посылается в XML формате, а как известно, флексовый XML не может иметь два корня, тогда как XMLList посылаться отказывается. То есть вариант <_method>put</_method><data /> не проканывает. На оборачивание этого дела в другой тэг Rails ругается.

Продолжение гугления подсказало устанавливать header HTTP_X_HTTP_METHOD_OVERRIDE = PUT. Это не помогло тоже. Тут я расплакался и пошел жаловаться мамепошел перевел Гуглом некоторую китаюскую ссылку вроде бы по теме, где бравый китайский коммунист дошел все же до конца решения этой проблемы. Оказывается, что Rails каким-то макаром приписывал переменным еще один префикс HTTP_, а соответственно на HTTP_HTTP_X_HTTP_METHOD_OVERRIDE = PUT удивленно моргал глазами “а что? это не я!” и ничего не делал.

Так что в итоге работает так:

<mx:HTTPService id=”accountsUpdate”
url=”{CONTEXT_URL}/accounts/{accountsGrid.selectedItem.id}”
method=”POST” resultFormat=”e4x”  contentType=”application/xml” headers=”{{X_HTTP_METHOD_OVERRIDE: ‘PUT’}}” >
</mx:HTTPService>

Я очень надеюсь, что этот пост поможет очередным граблеступателям (только лишь русским правда), а я пойду искать очередные на свою голову.

Some English text

Basically, in the recent Rails version adding ?_method=put to URL doesn’t work any more (say hello to those book examples). So, you either have to have a hidden from field named _method (which in case of sending XML with Flex can’t be done) or pass HTTP_X_HTTP_METHOD_OVERRIDE = PUT in request header. But Rails for some reason adds “HTTP_” to variable name, so you need to send X_HTTP_METHOD_OVERRIDE = PUT. The example above shows how to do it.