habradigest habrahabr.ru * web * дизайн * разработка
0
пилотный выпуск
Internet Explorer → IE8: Изменения в CSS, подробности для разработчиков
Ностальгия → История одного программиста или путь от простого до точки
.NET → C# vs R#: использование var вместо явного указания типа
Разработка → Обзор моделей работы с потоками
Web-разработка → Замыкания в JavaScript
XaocCPS Слово редактора → Пилотный выпуск
Добрый день, читатель! Меня зовут Владимир ”XaocCPS” Юнев и, являюсь членом сообщества habrahabr.ru. В данном сообществе не редкостью являются интересные и оригинальные статьи на темы дизайна, разработки сайтов и программ. Сам я – web-программист и мои интересы тесно связаны с такими темами, иногда я сам пишу статьи или участвую в обсуждении статей написанных другими авторами. Идея выделять самые интересные статьи из общего потока и периодически создавать некую подборку давно пришла мне в голову. Результатом реализации этой идеи является этот журнал «habradigest» – по своей сути неотъемлемая часть сообщества habrahabr.ru. Этот номер первый и пилотный, во многом – это эксперимент, от результатов которого будет зависеть судьба проекта. Для первой публикации я отобрал статьи, которые были размещены на сайте habrahabr.ru в сентябре. Для перестраховки и первого раза из пяти статей три - моего производства. Еще две – отличные статьи о работе с потоками в разных языках и замыканиях в javascript. Надеюсь, ком первого блина не будет выглядеть совершенным уродцем и получит право на жизнь. Обсудить этот пилотный номер можно также на проекте habrahabr.ru, и если вам есть что сказать – присоединяйтесь. Вы можете, так же, написать отзыв на мой адрес vyunev@gmail.com.
2
XaocCPS Internet Explorer → IE8: Изменения в CSS, подробности для разработчиков http://habrahabr.ru/blogs/ie/39446/
В
восьмой версии Internet Explorer ожидается масса изменений, есть и такие, которые касаются CSS. Хороший разработчик должен стараться знать о возможностях различных браузеров, поэтому css-нюансы нового IE считаю интересными. На официальном
блоге разработчиков internet explorer появилась статья «Microsoft CSS Vendor Extensions», в которой достаточно подробно излагаются css-изменения. В первую очередь, необходимо рассказать о том, что Микрософт изменила порядок именования некоторых свойств CSS. Теперь все «нестандартные свойства» получают префикс «-ms-». Для того, чтобы полностью соответствовать CSS 2.1 в IE 8 такой префикс получили свойства подходящие под следующие условия: если свойство — это расширение Микрософт (не определено в спецификации или модуле CSS); если свойство — часть CSS-спецификации или модуля, которая не получила статус Candidate Recommendation от W3C; если свойство только частично реализовывает свойство, определенное в спецификации CSS. Вот список свойств, которые получили приставку «-ms-» (с указанием причины): Property -ms-accelerator -ms-background-position-x -ms-background-position-y -ms-behavior -ms-block-progression -ms-filter -ms-ime-mode -ms-layout-grid -ms-layout-grid-char -ms-layout-grid-line -ms-layout-grid-mode -ms-layout-grid-type -ms-line-break -ms-line-grid-mode -ms-interpolation-mode -ms-overflow-x -ms-overflow-y -ms-scrollbar-3dlight-color -ms-scrollbar-arrow-color
Type Extension CSS3 CSS3 Extension CSS3 Extension Extension CSS3 CSS3 CSS3 CSS3 CSS3 CSS3 CSS3 Extension CSS3 CSS3 Extension Extension
3
W3C Status Working Draft Working Draft Editor's Draft
Editor's Draft Editor's Draft Editor's Draft Editor's Draft Editor's Draft Working Draft Editor's Draft Working Draft Working Draft
-ms-scrollbar-base-color -ms-scrollbar-darkshadow-color -ms-scrollbar-face-color -ms-scrollbar-highlight-color -ms-scrollbar-shadow-color -ms-scrollbar-track-color -ms-text-align-last -ms-text-autospace -ms-text-justify -ms-text-kashida-space -ms-text-overflow -ms-text-underline-position -ms-word-break -ms-word-wrap -ms-writing-mode -ms-zoom
Extension Extension Extension Extension Extension Extension CSS3 CSS3 CSS3 CSS3 CSS3 Extension CSS3 CSS3 CSS3 Extension
Working Draft Working Draft Working Draft Working Draft Working Draft Working Draft Working Draft Editor's Draft
Разработчики понимают, что для многих сайтов придется переводить старые значения на новые, поэтому старые значения, хотя и будут считаться устаревшими, будут поддерживаться в восьмой версии ради совместимости. При разработке же нового кода для IE 8 следует использовать только новые именования.
Свойство filter Изменения коснулись такого свойства, как filter. Ранее, к сожалению, синтаксис filter не соответствовал CSS 2.1. Например, в указанном коде запятые считались недопустимыми. filter: progid: DXImageTransform.Microsoft.Alpha(Opacity=80, FinishOpacity=70, Style=2); В новой версии браузера синтаксис filter приведен к соответствию требованиям спецификации CSS: -ms-filter: “progid: DXImageTransform.Microsoft.Alpha(Opacity=80, FinishOpacity=70, Style=2) “; нетрудно заметить, что в новом синтаксисе значение свойства взято в кавычки.
4
XaocCPS Ностальгия → История одного программиста или путь от простого до точки http://habrahabr.ru/blogs/nostalgie/39098/ http://habrahabr.ru/blogs/nostalgie/39295/
П
редставленный под катом текст — это попытка в художественной форме рассказать этапы своей программистской жизни. Местами разбавленное случаями из жизни, размышлениями вслух, настальгированием по прежним временам, это эссе в тоже время имеет своей целью поднять диалог с хабраюзерами, которые
не прочь рассказать о своем пути разработчика и поделится своими мыслями по поводу истории развития средств и языков разработки.
Basic start или когда компьютеры были большими Когда в пятом классе я попадал в кабинет информатики, это походило на посещение сокровенного места. Светлое помещение с дюжиной агатов, каждый из которых в комплекте с монитором весил больше чем я, пятиклассник, которые, будучи включенными все вместе, вызывали в моем детском сознании какие-то фантастические ассоциации. Комната напоминала то ли фантастическую лабораторию, то ли пункт управления космическими полетами. Клавиши, блоки, светящиеся мониторы — это было здорово.
И, конечно, первым языком, которому меня учили, был Basic. Признаться, знаний и воспоминаний от этого обучения осталось столько, что хватает только вспомнить какие-то обязательные нумерации строк и тот факт, что Basic был зашит в систему, и его приглашение в виде «?», собственно, являлось первым, что видел пользователь. Еще из той поры вспоминается повешенный позднее рекламный плакат советского компьютера «Микроша», который гораздо позднее в одном из российских журналов назовут рахитичным.
Славное это было время, когда учитель пытался всунуть в нас какие-то программы, рисую-
5
щие графики, а мы рвались к играм, которых больше нигде не могли пощупать, потому что тогда из электронных игр был только советский аналог Game & Watch и даже про «денди» тогда еще не знали. А самым большим шиком считалось владение спектрумом (он же синклер), который под шум магнитофона грузил цветные игры. На те игры сейчас ни один здоровый человек не стал бы смотреть, но тогда это было здорово, даже очень.
EC-1841 или о вреде мониторов Позднее в моей жизни появился Pascal — язык, который, притягивал своей простотой и дружелюбием. Как и когда состоялся первый контакт, я уже не помню. Вполне возможно, что это было у друга, который разжился супер-компьютером 486DX с 4 мегабайтами памяти, но, скорее всего, контакт произошел на уроках УПК, на которых мы, используя советские компьютеры EC-1841, постигали азы паскаля. EC-1841 обладал аж двумя системными блоками! Но зато у него была мышь размером с кирипич, подключенная к блоку толстенным армированным кабелем, драйвер которой звался «колобок», на нем запускался Star Control и Death Track, а еще мегаигра «Коммерсант», первая российская игра, в которую я играл. Это чудо советского компьютеростроения содержало в себе аналог процессора i8086 с 4.77Mhz и поставлялось с монохромным дисплеем. Я вообще с улыбкой отношусь к порой истеричным спорам и крикам о вреде мониторов. Неведомо сколько просидел я за советскими мониторами, проглядел в эти чернобелые экраны с CGA-графикой. Да и позже мониторы были нисколько не лучше, те же 60 герц, только палитра менялась от CGA до SVGA. И вот прошло больше десятка лет, я давно уже провожу перед монитором по 8 часов в день, и до сих пор мое зрение ничуть не ослабло, все такое же 100%, как и было в далеком детстве. Итак, после невнятного бейсика, который не оставил в моей душе ничего кроме понятий о циклах, условиях и переменных, в моей жизни появился паскаль. Надо сказать, подсел я тогда на него крепко, процедуры функции, циклы с пред- и постусловиями — все это вдохновляло, заставляло эксперементировать. Многие сейчас этого не поймут, но тогда источником информации были только пара книг, да учитель. Не было ни интернета, ни тем более
6
wiki, не было форумов и торрентов, с которых можно стянуть любую книгу. В те непростые времена, когда страна разваливалась, поход в книжный магазин походил на охоту, были известны основные книжные точки, был намечен оптимальный маршрут, каждая точка тщательно обыскивалась на предмет новой литературы. Это сейчас в книжных полки завалены пособиями разной степени толщины. А в то время книжные магазины были наполовину комиссионными. Иной раз в руки попадались толковые книги, одной из них стала Borland Pascal 7.0, автора которой я сейчас не вспомню. Эта книга открыла для меня ООП и надолго стала главной.
Никлаус Вирт — бог Что и говорить, паскаль в России для многих стал отправной точкой. Да и сейчас, наверное, учат если не на нем, то но его наследнике Delphi. Язык, прямо скажем, хороший. Да, он не такой строгий, как C, и, наверное, не такой эффективный. Когда я постигал паскаль, я, конечно, уже знал о существовании языка С, и мне досмерти хотелось научится писать на нем. Но, как я уже сказал, с литературой было плохо, желание не сразу нашло воплощение. В это время я заимел компьютер, который купил у друга, все тот же 486DX-33 c HDD на 340 мегабайт. Вскоре в моей жизни появились BBS, эдакие архаичные узлы, которые связывали людей не хуже того, как связывает людей сейчас ICQ. В то время уже достаточно широко распространились модемы, каждый мог найти и выкупить с рук какой-никакой, но модем. Мой первый, например, был безымянный модем на 2400 бод. Вот скажите мне, ктонибудь сейчас будет работать на скорости 2400 бод? А тогда такой модем был окном в сеть. У друга был аппарат получше, на 14400 бод, и все поголовно мечтали о внешнем U.S.Robotics Courier на 28800, который стоил тогда каких-то совершено бешенных денег. BBS познакомили меня со многими людьми, я узнал про FidoNet, у меня появилась наконец электронная документация, в общем, средств для развития стало не в пример больше. Я открыл для себя железную начинку компьютера, буфер видеопамяти, и долгое время паскаль стал для меня средством экспериментов по работе с железом. Параллельно, постигая премудрости Object Pascal, я узнавал принципы ООП. Трудно поверить, но чтобы
7
понять смысл виртуальных функций, я потратил несоклько дней, мне одному с большим трудом, один на один с книгой, удалось понять принцип полиморфизма. И это открытие перевернуло сознание. Целыми днями я что-то крутил-вертел в паскале, писал простейшие вещи, создавал какие-то библиотеки. То было интересное время, время, когда с каждым успехом меня переполняла гордость — «я понял это сам!». Напомню, что в то время толькотолько появился Windows 3.1, и программирование велось только под DOS. Что касается языка С и его противопоставления паскалю, то занять позицию с положительной оценкой последнего мне на всю жизнь помогла статья создателя языка — Никлауса Вирта, в которой он высказывал тезисы в защиту своего языка и дальнейшего его развития в виде языка Modula-2. Та статья, как сейчас помню, открыла для меня целый пласт людей — мега-разработчиков, которые витают где-то там и делают поразительные вещи вроде новых языков. Во многом из-за этих воспоминаний даже сейчас, когда я уже давно не пишу ни на паскале, ни в Delphi, я все равно с симпатией отношусь к этому языку.
Ассемблер, си, си с классами, си-- или лучшие времена С появлением модема и сети в форме Fido мои устремления в программировании получили дополнительный импульс. Узнавалось многое, новые знания появлялись очень часто и помногу. Связано это, наверное еще и с бурным ростом как прикладного программного обеспечения, так и системного. Вышел Windows 95, вирусы, которые ранее были просто головной болью теперь таили реальную угрозу (CIH), а все мое внимание завоевала система и соответственно системное программирование. Возможности паскаля были исчерпаны, хотелось залезть в систему еще глубже, чем просто порты и память. Примерно в это же время мной была приобретена книга за авторством Березин Б. И., Березин С. Б «С и С++», в интернете даже сейчас полно ссылок на этих авторов, должно быть выпускают книги до сих пор. Книга эта кстати, до сих пор стоит у меня дома на полке и когда я ее замечаю сразу же наполняюсь разными воспоминаниями.
8
Сначала, я, конечно, начал изучать С. Имея какой-никакой, а опыт я жадно впивался в язык. Компания Borland и тут помогла выпустив прекрасный продукт Borland C++ 3.1, который выглядел так же как и Borland Pascal, но содержал в себе более мощный язык. Эмоций и впечатлений было много, информационный поток был достаточно сильным, недостатка в документации я не испытывал и то были самые лучшие времена. В моем компьютере завелись Керниган с Ричи и Страуструп, которые встали в моем личном пантеоне богов в один ряд с Виртом. Вообще языки си и си++ (который, не многие знают, называется еще «си с классами»), конечно, покорили меня своей строгостью, тем что они не прощали то, что сейчас зовут быдло-кодингом. Забыл вызвать деструктор? Жди беды. Накосячил с указателями — жди беды. Подсказок ни от компилятора ни от линковщика почти не было, то были спартанские условия, наверное самые лучшие для закалки характера. В то время, программируя на си, невозможно было не знать об ассемблере. tasm/tlink, masm, nasm — таинственные программы, которые стали привлекать мое внимание тогда, когда я скачал первый мануал по процессору i8086. Ассемблер вообще казался и кажется мне до сих пор лучшим языком программирования. И дело тут не в том, что это по сути нативный язык процессора, следовательно компьютера. Нет, язык ассемблера еще суровее чем си и си++. Привычки, которые прививал во мне ассемблер остались до сих пор. Все эти экономии пары тактов или десятка байт, оптимизации циклов, заучивание списка комманд, насколько я помню, одних операторов перехода в ассемблере больше десятка. Ассемблер — как головоломка для ума, это инструмент настоящих поэтов от программирования. Увиденные в то время первый примеры демо-сцены поражали воображение. В 4 килобайта, а порой и в 256 байт мастера-программисты ассемблера вмещали целые визуальные динамические сцены, порой с музыкальным оформлением. Я вообще думаю, что те кто пишет на асме кроссворды не разгадывают. Божественная сила ассемблера в то время под сомнение мной не ставилась. Знакомые старше меня работающие программистами на ассемблере вызывали восхищение. Первыми ласточками моего пера на ассемблере стали резидентные программы под DOS. То были всякие мелочи и приколы, которые призваны были в основном продемонстрировать друзьям свои познания и возможности в программировании. Создание резидентных программ требовало тогда немалого умения и обширных знаний. Нюансов было много, следовало знать не только как работает система, но и как устроено железо, что такое таблица прерываний, какие могут быть подводные камни. Сейчас многозадачность воспринимается
9
как нечто нормальное, а в то время, большинство компьютеров еще не работали под windows95 и практически везде стоял DOS 6.22. То время запомнилось мне как самое полезное, как самое продуктивное. Я гонялся по городу за каким-то особыми компиляторами, даже сейчас я помню с каким энтузиазмом бежал с винтом к людям у которых обнаружился Watcom C++. Еще одним интересным продуктом который мне запомнился о том времени был C--. Этот авторский продукт являл собой облегченный С-подобный язык, который больше походил на ассемблер. Этакий гибрид C и асма. Проект до наших времен не дожил, но в тот момент интерес к нему у меня был сильным.
Первая работа или как Delphi заборол C++ На первую работу я устроился в 17 лет. Тогда я поступил в институт и параллельно стал работать в конторе, которую держали сотрудники вуза. Сейчас смешно, но тогда от первой работы я даже денег не ждал. А платили, надо сказать, очень мало, в стране стоял полный бардак и развал. Первая работа пришлась мне по-вкусу: тут и работа с телеметрией, опрос портов компьютера, знакомый с++. Об одном только не хватало знаний, о Turbo Vision, мега-библиотеки от Borland на которой делалось очень много программ. Эта библиотека предназначалась для формирования пользовательского интерфейса под DOS, она была полностью объектно-ориентированной и заставляла писать просто неимоверно много кода для формирования привычного сейчас всем интерфейса с кнопочками, чекбоксами и прочим. Всем программистам знакомы ощущения копания в чужом коде. Тут море эмоций: где замечаешь ошибки, там ты материшься на создателя кода, где видишь новые для себя решения, начинаешь его уважать. Часто все это бывает в одном флаконе. Как водится на работе никакой документации мне никто не оставил, да ее и не было. Все пришлось разбирать самому, благо дебаггер я в то время освоил основательно. Время шло, технологии развивались бешенными темпами, все больше софта писалось под Windows. А программирование под windows было совершенно новым делом. Тут и другая философия и архитектура, старое мышление приходилось вырывать просто под корень. Из-за работы и учебы я не сильно развивал навыки программирования под windows, меня отпугивали сообщения, потоки, WinAPI, который следовало знать. Десятки констант и макросов Visual C++ от микрософта надолго отпугнули меня от программирования под windows. Да, не легко было бросить такой родной дос, в котором все так ясно и понятно.
10
Но время шло, windows окружал со всех сторон, памяти под досом уже не хватало, становилось ясно что пора развиваться. В это время очень вовремя появилась, как я считаю инновационая, Delphi. Delphi 3 обладала многими достоинствами: на ней можно было писать программы под windows, она использовала хорошо известный язык object pascal, она была RAD-средой и полностью оправдывала этот термин. Программы на делфи писались за минуты, не было проблем с winapi, который делфи полностью оборачивал в свою библиотеку VCL. Не было проблем с GUI, казалось делфи — лучшее что может быть, особенно после Visual C++, в котором от слова «visual» ничего не было. С приходом windows однозначно стало ясно, что между мной и компьютером появился еще один слой, дотянуться до системы теперь стало сложнее, да и не так это было необходимо. Копание в системе сменилось копанием с формами, кнопочками, менюшками. И перевод рабочей системы с Borland C++ на Borland Delphi казался очевидным. Но работа так просто не сдавалась, постоянно приходилось накручивать на старый проект новые фичи и времени на разработку новой версии не было. Тем временем Delphi росла в версиях: 4, 5, 5.5…
Дельфийская эпоха, com, dcom, com+ и СУБД Вообще, именно благодаря делфи я познакомился с работой с базами данных. Тогда вместе с делфи шла такая замечательная штука как Borland Database Engine (BDE), которая позволяла работать со многими базами данных. Первыми моими хранилищами данных, конечно были dbf-файлы и файлы access. На то время — это было более чем достаточно. Работа в основном представляла собой проект который не предполагал сетевого хранения базы данных и подключения множества клиентов. Но настоящим открытием стал Interbase, настоящая СУБД с сервером, с транзакциями, с такими замечательными вещами как триггеры. Мало того, interbase заставлял думать более распределенно, знакомство с ним как был говорило: «пара выходить в сеть, пора распределять ресурсы».
11
К тому времени в моду вошла трехуровневая парадигма программирования: сервер баз данных, сервер приложения и тонкий клиент. Постигать все это было очень интересно. Параллельно мое внимание привлекли технологии микрософта COM и DCOM. Делфи располагала к программированию com-серверов, делфи в то время вообще была очень дружелюбна, она как бы предугадывала ваше желание и подсовывала то мастер, то какой-то готовый шаблон. От этого сильно страдало понимание азов технологии, зато сильно экономило время. Признаюсь COM и DCOM остались для меня незаконченным для усвоения материалом, я так и не разобрался кардинально в этих техниках, хотя и писал для себя рабочие программы с их использованием. Вскоре микрософт выдала на гору новую технику COM+, которая еще больше запутала меня и, откровенно говоря, оттолкнула. Вместе со всем прочим я, на некоторое, время «заболел» отладчиками и reverse engineering в виде копания в чужом коде с использованием дизассемблера IDA Pro и суперотладчика SoftIce. Эти инструменты были настолько мощными, что полностью возвращали власть над системой, которая несколько уменьшилась в windows. Softice, к примеру, загружался еще до windows и таким образом просто не давал себя обнаружить и перехватывал все что только можно. Не знаю как дела обстоят сейчас, но тогда это позволяло прервать работу windows в любой момент и начать отлаживать текущий процесс. Признаюсь, первыми жертвами экспериментов с этим дебагером стали игры с простейшей защитой, взлом которых заключался в простой замене условного перехода типа jnz на безусловный jmp. Очень жаль, что толковая документация от Криса Касперски появилась только после того, как я наигрался с этими инструментами. Возможно наша с reverse engineering любовь могла продлиться дольше. Время шло и наш проект наконец-то был переведен на рельсы win32 платформы. К тому времени делфи достигла шестой версии. Это был 2001 или 2002 год. Borland выпустила так же Kylix для программирования под linux, не знаю жив ли он сейчас. Вообще, кажется именно шестая версия делфи стала для меня канонической, да и не только для меня, я знаю
12
много людей, которые до сих пор пишут по мере надобности программы именно на шестой версии. Вскоре после перевода проекта я сменил работу. Новое место работы было финансовым учреждением, сравнимым с банковским. И многие программисты, работавшие в банках, согласятся со мной в том, что о работе в таких учреждениях можно писать отдельные книги. Этот этап в жизни начисто отбил у меня охоту работать в респектабельных огромных конторах. Новая работа помогла мне закрепить знания SQL, именно тогда я понял ценность триггеров, именно тогда узнал про блокировки таблиц, про права и про откаты транзакций. Кстати, многие ли разработчики SQL знают почему его называют «сиквелом»? А между тем это интересно, потому что изначально SQL назывался SEQUEL (Structured English Query Language). C interbase связано еще одно приятное воспоминание: IBExpert. IBExpert — это сторонняя разработка, которая похоже жива до сих пор, для полного администрирования СУБД Interbase и его братьев Firebird и Yaffil. Более продуманной, полной и навороченной тулзы для работы с СУБД я не видел ни до ни после этого. IBExpert содержал все что только можно придумать, но основным его достоинством для меня был дебаггер SQL-кода с возможностью пошаговой отладки, вещь которую я больше нигде не встречал.
Вход через XML или как я попал в паутину Все хорошее когда-нибудь кончается и наше с финансовым учреждением сотрудничество закончилось. С этим закончилась и моя делфи-эпоха. Следующим местом работы стала крупная контора разработчиков, где порядка 50 программистов работали над одним проектом. Вся прелесть новой работы заключалась в том, что проект был intranet-ориентированный, клиентом являлся браузер и разрабатывать приходилось в основном html-страницы, которые генерировались средствами xml/xslt. Это, на минуточку, 2002 год. Сейчас это обычное дело, но тогда лично для меня явилось откровением. В очередной раз я с энтузиазмом начал вникать в новые для себя технологии, которые еще дальше, очень далеко, уводили меня от системы и компьютерных потрохов.
13
Между прочим, начало эпохи web я как-то пропустил. В то время вокруг меня была такая обстановка, что тратить время на изучение web было просто бессмысленным. «Ну, кому скажите, нужен этот web, если у людей не то что интернета, даже компьютеров нет» — думал тогда я. Нет, конечно, какие-то вещи я узнавал. Были приобретены книги по HTML и javascript, но всерьез я их не воспринимал — так, необычное развлечение. И вот только моя новая работа показала мне что могут дать web-технологии в прикладном плане. Проект в котором я участвовал представлял собой глобальную государственную систему с множеством подсистем, несколькими уровнями и самое главное с одним на все клиентом в виде internet explorer. Вот тогда-то до меня и стали доходить перспективы как веба так и всех его технологий. Очередное озарение заставило плотно изучать html/xml/xslt/xpath. CSS и javascript в то время меня интересовали меньше, первый из-за того, что формы проекта в основном были простейшими в плане дизайна и не требовали особой разметки, да и основная нагрузка ложилась на activex элементы вроде специальных таблиц. Javascript хоть и использовался но его использование так же сводилось в основном к созданию элемента activeX, да вызове его методов. Еще одним инструментом который стал для меня важным в жизни — MS SQL Server. В то время актуальной была 2000-версия. Мои sql познания пополнились знанием tsql, процедурами и функциями, написание скриптов, курсорами и представлениями. Кстати, с базами данных связана в ту пору интересная история. Наш мегазаказчик по инициативе одного человека, похожей на диверсию, потребовал перевода проекта с MS SQL на Informix. Вот скажите мне, кто в здравом уме будет такое делать на огромном всегосударственном проекте? Однако, работа есть работа и наши системщики модернизировав сервер приложений, через который шли запросы, выдали на гора некий усредненный вариант спецификации языка sql, котрого требовалось придерживаться. Различий с informix было очень много. Тут и требование писать inner join вместо join и требование писать AS, там где mssql разрешал его опускать и много еще по мелочи вещей, сейчас все и не упомнишь. Ситуация с переводом была идиотская, но зато запомнилась надолго. Кстати, на прошедшей в этом году конференции TechNet, на которой были представлены продукты MS SQL Server 2008, VS.Net 2008 и Windows Server 2008 мне удалось поймать одного из команды разработчиков SQL Server и задать вопрос, который давно меня беспокоил: «посмотрите на три надписи о трех продуктах компании, что в них необычного
14
по отношению к SQL Server?». Парень (а это был молодой парень) ни секунды не думая ответил: «логотип». И добавил: «наша команда решила, что нам не нужен логотип, наш сервер в этом не нуждается, его знают и без логотипа». В этой конторе я проработал около года или чуть больше. И считаю что это место работы было лучшим в моей жизни. Специализация конторы на софт, основной состав программисты, небольшой размер, дружный коллектив, серьезный проект, достаточно на тот период продвинутые технологии, серьезный штат специалистов, в котором работало много очень грамотных людей. То была самая высокая концентрация программерской мысли на один квадратный метр. И это было здорово. Ни в финансовом учреждении, ни в технологических конторах, ни в госучреждениях, ни у частника — я больше нигде не чувствовал такой классной атмосферы как тогда. Ушел я оттуда только из-за того, что переехал в другой город.
Новый си или долгожданная точка О выходе .NET я знал заранее, в том смысле, что после того, как узнал об этой новой инициативе Микрософт я пристально следил за его судьбой. К сожалению до меня не дошли бета версии продукта и свое знакомство с платформой я начал прямо с релиза Visual Studio.net. Ожиданий была масса: тут и решение проблемы «dll hell», и новый язык C# с автоматическим сбором мусора и новая архитектура сборок, которая предполагала кардинальное улучшение по сравнению с com-объектами и объединенная в один инструмент студия, и asp.net, и ado.net. В общем, нововведения ожидались революционные. Студия не обманула ожиданий, все для меня казалось настолько крутым, что у меня в очередной раз произошел приступ энтузиазма и я начал поиск и изучение всех возможных мануалов, книг и другой документации. Главным из таких источников, конечно, являлась (и является до сих пор) документация MSDN. Так как в то время я был занят разработкой в webсреде, то первое, чем я занялся в новой студии был asp.net. По правде говоря, от того яркого впечатления от продукта сейчас осталось на две трети меньше, а то и больше. Сейчас я явно вижу все минусы этой платформы, порой жирные, и у меня к asp.net множество личных претензий. Тем не менее, asp.net первой версии меня ошеломил своим дружелюбием. Все что раньше нужно было делать руками здесь делалось само собой. Раздельный код, по сравнению со скриптами asp казался просто манной небес-
15
ной. Framework поражал своей обширностью. ViewState, который тогда я воспринял на ура, казался мне чудесным откровением. В общем, эйфория от новой платформы была полная, наверное, знакомое многим ощущение. Сказалась новизна, желание разобраться первым, быть на пике программерской мысли. Вообще, сейчас мне кажется, что если бы не тот выход .net я бы оказался в стане java-программистов, потому что давно поглядывал в сторону Java и даже ставил у себя какие-то средства разработки и ковырял исходники с примерами. Но сложилось так как сложилось и меня переманил .net. Первым проектом, который я написал на .net был intranet-база фильмов которые лежали на расшаренных ресурсах в нашей компании. Отдельный софт обходил все шары, набивал xml-файл, который затем импортировался в бд моего проекта. Проект был совершенно простой, как и все первые проекты жутко неоптимизированный, зато полностью на .net. Этот проект дал мне многое: закрепил прочтенные из документации сведения по языку и платформе, дал навыки работы в среде разработки, проявил подводные камни при программировании на C# и собственно asp.net. Время шло, .net менялся, взрослел, появился .net framework 2.0, а с ним и C# 2.0, значительно улучшенный, который принес обобщения, то чего так не хватало. Да, это были не шаблоны C++, но и у generics были свои плюсы. Еще добавились такие классические сейчас вещи как master pages, data controls, SqlDataSource. К сожалению, тогдашнее новое место работы не способствовало моему росту программиста и все что я делал в тот период было собственной инициативой. Но все же мне удалось написать два проекта для учреждения: официальный веб-сайт и специфический функциональный сайт. Создание первого познакомило меня с фреймворком DotNetNuke. DotNetNuke — это такой свободно распространяемый фреймворк, создание которого, вроде бы, спонсировала Микрософт. DNN предназначен для быстрого создания сайтов, в основном среднего и энтерпрайз уровня. По моем меркам это очень навороченная система, с кучей возможностей, активно развивающаяся, и к тому же написанная на .net. Работа с DNN с одной стороны доставила удовольствие тем, что все нужные компоненты сайта были уже готовы и от создателя требовалось только расставить их в нужно порядке, сделать интерфейсу какой-никакой дизайн и подключив нужные плагины запустить проект в сеть. С другой же стороны, отсутствие какой-либо необходимости в программировании уменьшало ценность работы, накопленный опыт по сути был мизерным и практическую пользу от всех своих телодвижений я оценивал как очень низкую. В этом проекте основную пользу я вынес из про-
16
тиворечия: чем легче, тем менее полезно. В будущем, я старался остерегаться легких путей, пытаясь решать проблемы от корня до конца самостоятельно.
Хабрахабр наших дней или дороги хватит на всех Остальная часть рассказа представляет собой настоящее или недалекое прошлое. События в жизни переплелись с жизненным процессом моей рабочей платформы .net и подавшись во фриланс я встретил очередную важную для меня версию .net 3.5 в работе над новым проектом. Контора на которую я фрилансил вскоре переманила меня на полную ставку и я, после длительного перерыва, занялся тем, что мне действительно нравилось. Где-то в этот момент я в первый раз попал на Хабрахабр. Я уже довольно уверенно применял ajax.net, создавал web-сервисы, вовсю использовал LINQ. Но новый ресурс открыл для меня бездну нового. Во первых, надо сказать о моих текущих впечатлениях о Хабре. Изначально я считал и считаю до сих пор этот ресурс яркой лампой в ночи, концентрированной кислотой и бесконечной энциклопедией. Хабр для меня — это идеальное воплощение идеи web 2.0 о создании и самоорганизации сообщества единомышленников в наше время. Правда, как и любое сообщество, оно не лишено недостатков, но все разногласия только придает ресурсу живости и вызывают дополнительные импульсы для движения внутри. Сначала я только изучал Хабр как анонимный читатель. Меня бесконечно радовали (и радуют до сих пор!) оригинальные, авторские статьи, которые обычно несут массу полезного. Знаете, за статьи гораздо ниже уровнем иные люди зарабатывают в печатных изданиях приличные деньги. А тут все даром и, главное, практически всегда от души. Достаточно сказать, что именно такие статьи именно на Хабре сильно подвинули мои знания в javascript и css. Такие статьи подвигли меня на изучение jQuery — инструмента, без которого я ныне просто не смыслю свою работу. Эти статьи познакомили меня с языком erlang и его философией, и это знакомство заставило по-другому взглянуть на программирование. Но однажды мой нынешний френд fotokaif создал блог .NET, которого так не хватало и я решил — пора. Регистрация, первые посты — прошло всего то 5 месяцев, но за этот короткий период я постарался в меру своих сил поделится своими знаниями, интересными новостями. Мои посты на Хабре, как отражение моих предпочтений, в основном представлены
17
в блоге .net. И не так давно я представил проект Хабраредактор, который представляет мое видение редактора для написания статей на Хабре. Хабраредактор — это компиляция того, что я получил от Хабра за время моего знакомства с ним. Тут и unobtrusive Javascript и мои познания в css и jquery и блочная верстка, на которую лично я перешел только после знакомства с Хабром. Конечно, я уверен, что Хабраредактор — не совершенен, но для меня он, в первую очередь, то самое что я смог получить от Хабра. Еще одно достоинство Хабрахабра — его многополярность. PHP соседствует с .NET и никто из них не мешает обсуждению Java-вопросов. Одновременно на ленте могут быть топики о js, css, вопросах блочной верстки или очередные холивары о скорости браузеров. Посуществу, такое положение дел огромный плюс ресурса и я бы хотел обратится ко всем: «камрады, поймите, дороги хватит на всех». Как и множество людей существует и множество технологий, которые создает множество разработчиков. Все эти множества пересекаются так, что порой не бывает двух одинаковых программистов или дизайнеров, которые были бы одинаково увлечены одинаковыми инструментами. У нас общая дорога, но он широкая и ее хватит на всех.
Что осталось за бортом За бортом эссе остались: защищенный режим DOS, который я не изучил в начале по причине малого количества документации, а когда появилась flat-модель по причине занятости другими вещами; Builder C++, который я ковырял и даже что-то писал на нем; разработка под Windows Mobile, все мои достижения которой заключены в написании англо-русского словаря для Windows Mobile smartphone edition 2003; Java, J2EE и все остальное, что касается java, по причине того, что меня вовремя переманила .net; cgi, php, python, ruby, perl, linux/apache/mysql, mac/mac os/safari и многое другое по причине того, что мне никогда не приходилось с ними сталкиваться.
18
No speed limit или вместо заключения Таков в кратком изложении мой путь. И, я уверен, он далеко еще не закончен. Будет новое время, будут новые технологии, которые так же смогу взбудоражить воображение и заставить с интересом вникать в новые термины, техники, приемы. Желаю всем читателям, которые смогли дочитать до этой строки развития с беспредельной скоростью, поглощения нового, постоянного роста, постоянного интереса, постоянной жажды знаний.
Фото старых компьютеров взяты с ресурса «Музей компьютеров» pchistory.ru.
19
XaocCPS .NET → C# vs R#: использование var вместо явного указания типа http://habrahabr.ru/blogs/net/39231/
В
своей работе с замечательным дополнением ReSharper в Visual Studio я постоянно сталкивался с предложением вместо явного объявления типа переменных использовать объявления типа в неявной форме с использованием var. Сначала меня это
несколько удивило, но я особо не обратил внимание. Но по прошествии некотрого времени такие предложения стали уже напрягать и я решил разобраться в чем же суть такой оптимизации. Ответ был найден в блоге у создателей R#:
Итак, Илья приводит следующие доводы в пользу повсеместного использования «var»: использование var потребуется вам для определения переменной с анонимным типом. Тут все просто — вы не сможете определить переменную анонимного типа без использования var; использование var принуждает вас более грамотно называть сами переменные. Когда вы читаете определение переменной с явным типом, то получаете больше информации и что-нибудь типа «IUnitTestElement current» имеет смысл. Тем не менее, когда локальная переменная используется дальше, вы прочитаете «current», что потребует от вас больше времени понять что она означает. Использование «var currentElement» позволяет более быстро понимать переменную в любом месте кода; использование var принуждает к более качественному API. Во-первых, вы получите оптимальные типы, когда позволяете компилятору получать самому тип возвращаемого значения метода или свойства. И еще вы вынуждены будете
20
более правильно называть свои методы, чтобы они явно указывали на то, что возвращают; использование var принуждает к инициализации переменных при их объявлении. В общем случае, инициализация переменных при определении является хорошим тоном, а в нашем случае, компилятор обязательно требует такую инициализация при определении переменной через var; использование var приводит к уменьшению «шума» в коде. Существует множество случаев, когда объявленные неявно переменные уменьшают количество текста, который приходится читать разработчику и который он мог бы пропустить. Если мы не используем var, то определение переменной через выражение new или cast требует указание типа дважды. Когда мы имеем дела с обобщениями (generics), то такое положение дел приведет к появлению большого количества излишнего, чрезмерного кода (redundant code). Еще одним подобным примером может стать переменная итерации в foreach для типа наподобие «Dictionary<TKey, TValue>»; использование var позволяет уменьшить использование директивы using. С var у вас нет явных ссылок на типы, и так как компилятор определит тип за вас, то вам не нужно импортировать пространства имен, когда вам требуется какаянибудь временная переменная. Вот такое вот объяснение. Хотел бы привести еще один комментарий к этой статье. Alexander пишет, что Микрософт не рекомендует использовать var нигде кроме как в случае анонимных типов. На что Илья отвечает просто: «Yeah, Microsoft often tries to make things „safer“. I don't agree with them here :)». Думаю, перевод тут излишен.
21
Lite
Разработка →
перевод
Обзор моделей работы с потоками http://habrahabr.ru/blogs/development/39543/
Оригинальный источник: http://justin.harmonize.fm/index.php/2008/09/threading -model-overview/
Обзор моделей работы с потоками
М
ногие люди не понимают того, как многопоточность реализована в различных языках программирования. В наши времена многоядерных процессоров такое знание будет весьма полезно.
Вот вам небольшой обзор.
Начало (С и native threads) Первая модель, которую мы рассмотрим — это стандартные потоки ОС (threads). Их поддерживает каждая современная ОС, несмотря на разницу в API. В принципе, поток — это процесс выполнения инструкций, который работает на выделенном процессоре, выполнение которого контролирует планировщик (scheduler) ОС, и который может быть заблокирован. Потоки создаются внутри процесса и пользуются общими ресурсами. Это означает, что, например, память и декскрипторы файлов, являются общими для всех потоков процесса. Подобный подход и принято называть native threads. Linux позволяет использовать данные потоки с помощью библиотеки pthread. BSDs тоже поддерживают pthreads. Потоки Windows работают немного иначе, но базовый принцип тот же.
Java and Green Threads Когда появилась Java, она принесла с собой другой тип многопоточности, который называется green threads. Green threads — это, по сути, имитация потоков. Виртуальная машина Java берёт на себя заботу о переключении между разными green threads, а сама машина работает как один поток ОС. Это даёт несколько преимуществ. Потоки ОС относительно дороги
22
в большинстве POSIX-систем. Кроме того, переключение между native threads гораздо медленнее, чем между green threads. Это всё означает, что в некоторых ситуациях green threads гораздо выгоднее, чем native threads. Система может поддерживать гораздо большее количество green threads, чем потоков OС. Например, гораздо практичнее запускать новый green thread для нового HTTPсоединения к веб-серверу, вместо создания нового native thread. Однако есть и недостатки. Самый большой заключается в том, что вы не можете исполнять два потока одновременно. Поскольку существует только один native thread, только он и вызывается планировщиком ОС. Даже если у вас несколько процессоров и несколько green threads, только один процессор может вызывать green thread. И всё потому, что с точки зрения планировщика заданий ОС всё это выглядит одним потоком. Начиная с версии 1.2 Java поддерживает native threads, и с тех пор они используются по умолчанию.
Python Python — это один из моих любимейших скриптовых языков и он был одним из первых предложивших работу с потоками. Python включает в себя модуль, позволяющий манипулировать native threads, поэтому он может пользоваться всеми благами настоящей многопоточности. Но есть и одна проблема. Python использует глобальную блокировку интерпретатора (Global Interpreter Lock, GIL). Эта блокировка необходима для того, чтобы потоки не могли испортить глобальное состояние интерпретатора. Поэтому две инструкции Python не могут выполняться одновременно. GIL снимается примерно каждые 100 инструкций и в этот момент другой поток может перехватить блокировку и продолжить своё выполнение. Сперва это может показаться серьёзным недостатком, однако на практике проблема не столь велика. Любой заблокированный поток как правило освобождает GIL. Расширения С также освобождают её когда не взаимодействуют с Python/C API, поэтому интенсивные вычисления можно перенести в C и избежать блокировки выполняющихся потоков Python. Единственная ситуация, когда GIL действительно представляет проблему — это ситуация когда поток Python пытается выполняться на многоядерной машине.
23
Stackless Python — это версия Python, которая добавляет “tasklets” (фактически green threads). По их мотивам был создан модуль greenlet, который совместим со де-факто стандартом: cPython.
Ruby Модель потоков Ruby постоянно меняется. Изначально Ruby поддерживал лишь собственную версию green threads. Это хорошо работает во многих сценариях, но не даёт пользоваться возможностями многопроцессорности. JRuby перевёл потоки Ruby в стандартные потоки Java, которые, как мы выяснили выше, являются native threads. И это создало проблемы. Потокам Ruby нет необходимости взаимно синхронизоваться. Каждому потоку гарантируется, что никакой другой поток не получит доступа к используемому общему ресурсу. Подобное поведение было сломано в JRuby, так как native threads переключаются принудительно (preemptive) и поэтому любой поток может обратиться к общему ресурсу в произвольное время. Из-за подобной несостыковки и желания получить native threads разработчиками C Ruby было решено, что Ruby будет переходить на них в версии 2.0. В состав Ruby 1.9 был включён новый интерпретатор, который добавил поддержку fibers, которое, насколько я знаю, являются более эффективной версией green threads. Короче, модель потоков Ruby — это плохо документированная каша.
Perl Perl предлагает интересную модель, которую Mozilla позаимстовала для SpiderMonkey, если я не ошибаюсь. Вместо использования глобальной блокировки интерпретатора как в Python, Perl сделал глобальное состояние локальным и фактически запускает новый интерпретатор для каждого нового потока. Это позволяет использовать настоящие native threads. Не обошлось и без пары загвоздок. Во-первых, вы должны явно указывать переменные доступными для других потоков. Вот что происходит, когда всё становится локальным для потока. Приходится синхронизировать значения для межпоточного взаимодействия. Во-вторых, создание нового потока стало очень дорогой операцией. Интерпретатор — большая штука и многократное копирование его съедает много ресурсов.
24
Erlang, JavaScript, C# and so on Существует множество других моделей, которым время от времени находят применение. Например Erlang, использует архитектуру «ничего-общего» (shared nothing), которая стимулирует использование лёгких пользовательских процессов вместо потоков. Подобная архитектура просто великолепна для параллельного программирования, так как она устраняет всю головную боль насчёт синхронизации, а процессы настолько легки, что вы можете создать любое их количество. JavaScript обычно не воспринимается как язык, который поддерживает работу с потоками, но она необходима и там. Модель работы с потоками в JavaScript очень похожа на ту, что используется в Perl.
C# использует native threads. От себя: досаду на некоторую поверхностность статьи (которую я и сам осознаю) адресуйте автору. Я всего-навсего перевёл в меру своих скромных возможностей. ;) Буду рад уточнениям и дополнениям в комментариях.
От себя 2: по мотивам комментариев таки подправил пару фраз. Прости, автор! :)
25
Mourner Web-разработка → Замыкания в JavaScript http://habrahabr.ru/blogs/webdev/38642/
Е
сли вы используете JavaScript, но при этом так до конца и не разобрались, что же это за чудная штука такая — замыкания, и зачем она нужна — эта статья для вас.
Как известно, в JavaScript областью видимости локальных переменных (объявляемых
словом var) является тело функции, внутри которой они определены. Если вы объявляете функцию внутри другой функции, первая получает доступ к переменным и аргументам последней: function outerFn(myArg) { var myVar; function innerFn() { //имеет доступ к myVar и myArg } }
При этом, такие переменные продолжают существовать и остаются доступными внутренней функцией даже после того, как внешняя функция, в которой они определены, была исполнена. Рассмотрим пример — функцию, возвращающую кол-во собственных вызовов: function createCounter() { var numberOfCalls = 0; return function() { return ++numberOfCalls; } } var fn = createCounter(); fn(); //1 fn(); //2 fn(); //3
В данном примере функция, возвращаемая createCounter, использует переменную numberOfCalls, которая сохраняет нужное значение между ее вызовами (вместо того, чтобы сразу прекратить своё существование с возвратом createCounter).
26
Именно за эти свойства такие «вложенные» функции в JavaScript называют замыканиями (термином, пришедшим из функциональных языков программирования) — они «замыкают» на себя переменные и аргументы функции, внутри которой определены.
Применение замыканий Упростим немножко пример выше — уберём необходимость отдельно вызывать функцию createCounter, сделав ее аномимной и вызвав сразу же после ее объявления:
var fn = (function() { var numberOfCalls = 0; return function() { return ++ numberOfCalls; } })();
Такая конструкция позволила нам привязать к функции данные, сохраняющиеся между ее вызовами — это одно из применений замыканий. Иными словами, с помощью них мы можем создавать функции, имеющие своё изменяемое состояние. Другое хорошее применение замыканий — создание функций, в свою очередь тоже создающих функции — то, что некоторые назвали бы приёмом т.н. метапрограммирования. Например:
var createHelloFunction = function(name) { return function() { alert('Hello, ' + name); } } var sayHelloHabrahabr = createHelloFunction('Habrahabr'); sayHelloHabrahabr(); //alerts «Hello, Habrahabr»
Благодаря замыканию возвращаемая функция «запоминает» параметры, переданные функции создающей, что нам и нужно для подобного рода вещей. Похожая ситуация возникает, когда мы внутреннюю функцию не возвращаем, а вешаем на какое-либо событие — поскольку событие возникает уже после того, как исполнилась
27
функция, замыкание опять же помогает не потерять переданные при создании обработчика данные. Рассмотрим чуть более сложный пример — метод, привязывающий функцию к определённому контексту (т.е. объекту, на который в ней будет указывать слово this). Function.prototype.bind = function(context) { var fn = this; return function() { return fn.apply(context, arguments); }; } var HelloPage = { name: 'Habrahabr', init: function() { alert('Hello, ' + this.name); } } //window.onload = HelloPage.init; //алертнул бы undefined, т.к. this указывало бы на window window.onload = HelloPage.init.bind(HelloPage); //вот теперь всё работает
В этом примере с помощью замыканий функция, вощвращаемая bind'ом, запоминает в себе начальную функцию и присваиваемый ей контекст. Следующее, принципиально иное применение замыканий — защита данных (инкапсуляция). Рассмотрим следующую конструкцию:
(function() { … })();
Очевидно, внутри замыкания мы имеем доступ ко всем внешним данным, но при этом оно имеет и собственные. Благодаря этому мы можем окружать части кода подобной конструкцией с целью закрыть попавшие внутрь локальные переменные от доступа снаружи. (Один из примеров ее использования вы можете увидеть в исходном коде библиотеки jQuery, которая окружает замыканием весь свой код, чтобы не выводить за его пределы нужные только ей переменные). Есть, правда, одна связанная с таким применением ловушка — внутри замыкания теряется значение слова this за его пределами. Решается она следующим образом:
28
(function() { //вышестоящее this сохранится }).call(this);
Рассмотрим еще один приём из той же серии. Повсеместно популяризовали его разработчики фреймворка Yahoo UI, назвав его «Module Pattern» и написав о нём целую статью в официальном блоге. Пускай у нас есть объект (синглтон), содержащий какие-либо методы и свойства:
var MyModule = { name: 'Habrahabr', sayPreved: function(name) { alert('PREVED ' + name.toUpperCase()) }, sayPrevedToHabrahabr: function() { this.sayPreved(this.name); } } MyModule.sayPrevedToHabrahabr();
С помощью замыкания мы можем сделать методы и свойства, которые вне объекта не используютя, приватными (т.е. доступными только ему):
var MyModule = (function() { var name = 'Habrahabr'; function sayPreved() { alert('PREVED ' + name.toUpperCase()); } return { sayPrevedToHabrahabr: function() { sayPreved(name); } } })(); MyModule.sayPrevedToHabrahabr(); //alerts «PREVED Habrahabr»
Напоследок хочу описать распространённую ошибку, которая многих вгоняет в ступор в случае незнания того, как работают замыкания.
29
Пускай у нас есть массив ссылок, и наша задача — сделать так, чтобы при клике на каждую выводился алертом ее порядковый номер. Первое решение, что приходит в голову, выглядит так:
for (var i = 0; i < links.length; i++) { links[i].onclick = function() { alert(i); } }
На деле же оказывается, что при клике на любую ссылку выводится одно и то же число — значение links.length. Почему так происходит? В связи с замыканием объявленная вспомогательная переменная i продолжает существовать, при чём и в тот момент, когда мы кликаем по ссылке. Поскольку к тому времени цикл уже прошёл, i остаётся равным кол-ву ссылок — это значение мы и видим при кликах. Решается эта проблема следующим образом:
for (var i = 0; i < links.length; i++) { (function(i) { links[i].onclick = function() { alert(i); } })(i); }
Здесь с помощью еще одного замыкания мы «затеняем» переменную i, создавая ее копию в его локальной области видимости на каждом шаге цикла. Благодаря этому всё теперь работает как задумывалось. Вот и всё. Эта статья, конечно, не претендует на звание исчерпывающей, но комунибудь, надеюсь, всё-таки поможет разобраться. Спасибо за внимание!
30
в номере использованы материалы с ресурса Хабрахабр, созданные авторами: Mourner, Lite, XaocCPS автор проекта habradigest Владимир «XaocCPS» Юнев habradigest, Сентябрь 2008
31