Если нельзя, но очень хочется, то нужно обязательно и ничего в мире не стоит того, чтобы делать из этого проблему!


Интересна Java? Кликай по ссылке и регистрируйся!
Если тебе полезно что-то из того, чем я делюсь в своем блоге - можешь поделиться своими деньгами со мной.
с пожеланием
столько времени читатели провели на блоге - 
сейчас онлайн - 

четверг, 29 марта 2012 г.

Camtasia Studio 7 - мощный инструмент для работы с видео записанным с экрана

Нашел ман хороший. Спасибо Автору.

среда, 28 марта 2012 г.

Мотивировать себя на миниподвиг сегодня

Сегодня утром, я услышав будильник в 6:25 проснулся. Я знал что не выспался, хотя еще не почувствовал этого - вчера вечером я поставил будильник в 2 часа ночи и спал чуть больше 4 часа. Мне нужно подготовиться к митингу, который будет в 9 утра - обещал. А вчера просто увлекся и не посмотрел на часы.

Говорят, главное, услышав будильник, встать и не ложиться обратно. Ага. Фиг там. Я прятал будильник и на шкаф, чтобы встать на табуретку и достать его. Позже, я просто его игнорил. Я ставил будильник в другую комнату, чтобы моя жена его точно не проигнорила, а я получил по шапке за то, что она проснулась в 5:30 утра есл ия вдруг проигнорю. Я мировые показывал рекорды! Но раз, спросонья, зацепился об табуретку. Другой раз наступил на воздушный шарик и он лопнул. Осторожно в доме игрался ребенок! :) Понял, что так недалеко до травм.

Лучше всего сработала такая штука: я, ложась спать, глянул на часы выставляю будильник на 9,5 часов +, а потом ложусь спать. Я знаю, что обычно просыпаюсь спустя 8,5 часов сна (если с открытой форточкой то 7,5). Будильник я ставлю для того, чтобы не дай бог не спать больше чем положено (тогда я хожу разбитый до обеда). Будильником не разу не воспользовался, всегда сам вставал бодрячком. Но если лег в три, то подъем соответственно ~11. А если надо в 7 - правило не работает. Ложись раньше! А если увлекся, как вчера?

Придумался другой инструмент как микс нескольких. Уже давно практикую такую штуку: вставать стоит тогда, когда в голове появится идея, мотивирующая тебя так сильно, чтобы буквально поднять тебя и усадить за ее реализацию. Это работает, но не так часто, как хотелось бы. Дело в том, что сознание по утрам играет со мной в игры, если его удалось обмануть пару раз, то на третий оно придумывает железный аргумент "а почему бы и нет". И вот в ходе очередной такой игры в уламывание, я сегодня нашел еще хак.

Мотивировать себя что либо сделать намного легче, если спросить лежа утром на диване "а как мне сделать Сегодня +1?". Не думать при этом, что "завтра тоже надо будет +1, а за месяц надо будет +30 и вообще я это делаю потому что бла бла бла", нет. Только сегодня и только +1. А завтра? А завтра будет завтра.

Сразу как-то так легко стало. Я представил четко сегодняшний день, что я делаю такие-то и такие-то таски. Остальные таски я физически не успею - не супермен же. Ну и ладно! Останутся в моем бесконечном туду до следующего раза. Я приблизительно прикинул какой-то драфтовый план, понимая, что уже в следующую минуту он перестанет отражать действительность. Но все же я понял, что план вполне реальный. И легонько встал.

Следуя другому совету я сразу же взялся за дело - решил написать этот пост. Не фейсбуки, почты или еще что-то там (может это из за того, что они пустые с вчерашнего вечера, не знаю), а именно пост. И вот я уже проснулся и готов брать следующий таск из ТУДУ.

А спать захочу ближе к обеду. И я не буду себя в этом ограничивать. Захочу - значит так надо. Организм не обманешь - свои 8 часов в сутки он заберет, сегодня же, завтра или через неделю предложив сходить на больничный :) Кстати, если будет больничный, я с удовольствием схожу на него, потому как сам тому стал причиной и должен отслужить перед телом эту работу - работу по выздоравливанию. Но в том году я был здоров всю осень зиму и весну, хоть часто выходил на улицу раздетым и сидел в офисе рядом с кашляющими соседями.... Такс, чото мысль пошла в сторону :) Как увязать? А наверное так. Если болен или чувствуешь, что заболеваешь, не мотивируй себя ни на какие подвиги - даже сегодня, даже на крохотные. Просто отдыхай. Так выздоравливание быстрее закончится. Болезнь - это работа по выздоравливанию, и ее стоит проделать так, как ты делаешь свое любимое хобби. Болезнь - это процесс который имеет начало и конец. Очередная таска в туду.

Но лучше будь здоров!

И я вместе с тобой! :)

вторник, 27 марта 2012 г.

Пинарик может быть позитивный

В моей модели подсчет +1 ведет к еще большем числу +1 в будущем. То, что меня волнует/увлекает/парит/вдохновляет - то, о чем я думаю, то и увеличивается. Подсчитывая свои фейлы, я становлюсь экспертом в этой области и фейлов становится все больше.

Одно дело, если я на фейлах учусь и извлекаю урок, но другое дело если я просто их коллекционирую. Жизнь преподносит уроки постоянно, и если не пройти текущий уровень она попадает в цикл и дает тебе одно и то же но сильнее и сильнее. Пока не пройдешь. Потом будет другой урок.

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

Уровень пройден, если я нашел какое-то новое правило, следуюя которому я на своем пути больше не вижу ничего подобного, что меня парило. А если и увижу - я знаю как с ним поступать. Но на пути этом будет какой-то другой урок. И я могу на нем зависнуть.

Решением обычно есть смена привычки. Это больно и неприятно если делать это сразу (но не более 30 дней, потом отпускает). Но если привычек надо поменять сразу 30, что ближе к реальности, то так нахрапом не получится - стресс будет и больничный. А потому, либо последовательно прививать и растянуть это на 30 месяцев, либо параллельно, но не требуя от себя сверх результатов - просто подсчитывая позитивные сдвиги каждый раз, как этот сдвиг получился. Не каждый день, потому как тогда волей не волей буду фиксировать то что я чего-то не сделал, а каждый раз как что-то сделал.

Пользовался пинариками - они обладают этим недостатком. Я в какой-то момент забрасываю и начинаю себя грызть за то, что так классно начал а потом забросил. Негативизм тут не помощник. Пинарик должен быть позитивный - заглядывать в него стоит только тогда, когда есть продвижение в направлении новой привычки - и если это раз в месяц, значит раз в месяц.

Если вообще, то я уже написал +600 постов в блоге
А в этом году я пробежал +5 раз
Так же в этом году я сам убрал свою комнату именно так, как мне хочется +1 раз
Еще в этом году я выступил публично +5 раз
В этом году я встал в 5 утра +20 раз
и так далее по всем направлениям, которые я развиваю...

Теперь надо придумать тулу, в которую приятно было бы заглядывать. Думаю это должен быть календарик. Обычный бумажный календарик.

понедельник, 26 марта 2012 г.

Евгений Хмара - пианист Ukraine got talent 4



Ой, хочу так уметь. Это сколько работы было проделано. Женя ты молодец!

Как удаленно включить свой компьютер?

Только что научился включать свой комп не нажимая кнопку питания компьютера, причем удаленно. Радуюсь как ребенок. Оказывается еще давным давно было придумана технология Wake On Lan. Все, что надо знать, так это мак адресс включаемого компа и айпишку рабочего узла, который видит карточку с фичей Wake On Lan на борту.

Начнем с самого самого начала. Допустим я не знаю ни IP компьютеров в сети, ни уж тем более MAC адресов карточек. Что делать?

Нумбер1. Садимся за комп, который позже планируем включать удаленно. На нем запускаем команду cmd и там запускаем ipconfig

C:\Users\xxx>ipconfig /all

Windows IP Configuration

Wireless LAN adapter Wireless Network Connection:

   Connection-specific DNS Suffix  . : ......
   Description . . . . . . . . . . . : ....
   Physical Address. . . . . . . . . : 12-34-56-78-9A-BC
   DHCP Enabled. . . . . . . . . . . : ....
   Autoconfiguration Enabled . . . . : ....
   Link-local IPv6 Address . . . . . : ....::....:....:....:....%..(Preferred)
   IPv4 Address. . . . . . . . . . . : 192.168.1.200(Preferred)
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Lease Obtained. . . . . . . . . . : ......, .... .., .... ..:..:.. PM
   Lease Expires . . . . . . . . . . : ......, .... .., .... ..:..:.. PM
   Default Gateway . . . . . . . . . : 192.168.1.1
   DHCP Server . . . . . . . . . . . : 192.168.1.1
   DHCPv6 IAID . . . . . . . . . . . : ...........
   DHCPv6 Client DUID. . . . . . . . : ..-..-..-..-..-..-..-..-..-..-..-..-..-..
   DNS Servers . . . . . . . . . . . : 123.45.678.9
                                       234.56.789.10
   NetBIOS over Tcpip. . . . . . . . : ........

C:\Users\xxx>

Так среди прочего можно в поле IPv4 Address найти айпишку компа а в поле Physical Address - МАС адресс. Кстати я лишние циферки заменил на символ '.' чтобы не смущать любознательных и выделить полезное.

Если же у нас нет возможности подойти к компьютеру, то мы можем за любым другим компьютером сети сделать следующее (опять же в cmd)

C:\Users\xxx>arp -a 

Interface: 192.168.1.200 --- 0xc
  Internet Address      Physical Address      Type
  192.168.1.1           01-23-45-67-78-9A     dynamic
  192.168.1.200         12-34-56-78-9A-BC     dynamic
  192.168.1.201         23-45-67-89-AB-CD     dynamic
  ___.___.___.___       ..-..-..-..-..-..     static
  ___._.__.__           ..-..-..-..-..-..     static
  ___._.__.__           ..-..-..-..-..-..     static
  ___._.__._            ..-..-..-..-..-..     static
  ___.___.___.___       ..-..-..-..-..-..     static
  ___.___.___.___       ..-..-..-..-..-..     static

Тут отображаются компьютеры (IP и MAC), с которыми пообщался текущий компьютер. Если тут нет компьютера, который мы хотим включить, тогда его всего лишь надо попинговать (если знаем IP)

C:\Users\xxx>ping 192.168.1.200

Pinging 192.168.1.200 with 32 bytes of data:
Reply from 192.168.1.200: bytes=64 time<1ms TTL=128
Reply from 192.168.1.200: bytes=64 time<1ms TTL=128
Reply from 192.168.1.200: bytes=64 time<1ms TTL=128
Reply from 192.168.1.200: bytes=64 time<1ms TTL=128

Ping statistics for 192.168.1.200:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms

А после снова запустить arp -a и подглядеть результат Но если мы не знаем айтишку включаемого компьютера - тоже не беда. Нам всего лишь надо просканить весь диапазон айпишек локальной сетки. Когда мы вводили ipconfig команду то могли пронаблюдать что в поле Subnet Mask написано 255.255.255.0 - это значит, что айпишники локальной сетки при том, что айпишник текущей машины 192.168.1.200 - будут в диапазоне 192.168.1.0-255. Осталось пропинговать все 255 компов и снова запустить arp -a

Как пропинговать диапазон? Просто запускаем команду (для адресов 192.168.1.1-255)

C:\Users\xxx>for /l %i in (1,1,254) do ping -n 1 192.168.1.%i >>ping.txt

А потом смотрим файл ping.txt. Обычно ответ будет таким

Pinging 192.168.1.___ with 32 bytes of data:
Reply from 192.168.1.___: Destination host unreachable.

Ping statistics for 192.168.1.___:
    Packets: Sent = 1, Received = 1, Lost = 0 (0% loss),

Но когда комп включен, то таким

Pinging 192.168.1.___ with 32 bytes of data:
Reply from 192.168.1.___: bytes=32 time<1ms TTL=128

Ping statistics for 192.168.1.___:
    Packets: Sent = 1, Received = 1, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms

Вот это нам и надо.

Чтобы не париться по всему файлу, можно качнуть отсюда пак с юниксовыми утилитами, портированными под windows. Распаковать и запустить команду

C:\Users\xxx>grep bytes.*time.*TTL ping.txt

Reply from 192.168.1.___: bytes=64 time=20ms TTL=64
Reply from 192.168.1.___: bytes=64 time<1ms TTL=128
Reply from 192.168.1.___: bytes=64 time=112ms TTL=128
Reply from 192.168.1.___: bytes=64 time=3ms TTL=128

Теперь, если ты еще не забыл зачем мы тут собрались, запускаем arp -a - смотрим на мак-адрес компьютера. Записываем на бумажке, а когда он случайно выключится, то качаем вот эту програмульку и запускаем ее так:

C:\Users\xxx>wol 12:34:56:78:9A:BC

И все :) Если вы находитесь в расхных подсетях, тогда запускать стоит так

C:\Users\xxx>wol 123.234.231.123-12:34:56:78:9A:BC

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

Этот пост состоялся благодаря гуглу и страничкам
1) http://white55.narod.ru/wol.html
1) http://computernity.ru/index.php?s=&act=ST&f=11&t=5941
1) http://www.sablog.ru/kak-uznat-mac-adres-setevoj-karty-udalenno/
1) http://unxutils.sourceforge.net/

воскресенье, 25 марта 2012 г.

Завтра нет

Сегодня поймал себя на слове в разговоре "...не знаю что такое завтра, поясни..." Причем на полном серьезе. А потом понял, как глупо это выглядит в глазах собеседника.

А разговор был о том, идти завтра в кино или нет. А я не знаю, что завтра будет. Вроде как воскресенье - выходной день, кинотеатры работают, все можно спланировать. Но обычно получается не так как планируется. Сегодня - давай, идем в кино, а завтра не уверен. Тогда чего загадывать? Чтобы потом расстраиваться? Или через кровь и пот добиваться своего, когда поезд уже ушел. Этот поезд есть сегодня, а завтра будет другой поезд, в другой город.

В детстве не помню чтобы часто думал о завтра. Я начал осознавать хорошо понятие "завтра" в школе, когда было расписание на неделю, когда завтра предмет xxx, а я не хочу сейчас сидеть и готовиться к нему - хочу гулять и развиваться в другой области.

Но думаю впервые я услышал слово "завтра" намного раньше. Это я понял сегодня, когда покатавшись на карусели с ребенком пошли дальше, а ребенок стал плакать - вот я его завтраками и кормил. Завтра еще раз сходим. "Завтра" в смысле потом, через неделю, не сейчас. А кто знает, будет ли эта карусель завтра? Обманул получается. Так и меня в детстве обманывали мои родители, а я научился ждать этого самого завтра.

А еще  день рождения и всякие-разные праздники, которых ждешь, потому как дни эти необычные - мама на стол накрывает, папа дома, а не на работе, подарок...

Позже стал планировать неделями, месяцами. "Вот соберу за три месяца немного денег и куплю себе новую игру" - думал. Потом пришла привычка.

В взрослого мысли как-то все в будущем или наоборот в прошлом. Наверное это и есть взросление, когда фокус внимания перемещается с сегодня на где-то там.

Вот бы разучиться думать о "завтра". Раз и навсегда.

суббота, 24 марта 2012 г.

Регулярные выражения - это просто

Начал читать книгу "Регулярные выражения" автора Дж. Фридла. Просто о сложном. Много водички, а потому я буду вести в своем блоге конспект лекций.

Мне понравилось что сказал автор про ругулярные выражения:

Если бы я не знал о существовании регулярных выражений, мне бы и в голову не пришло, что существует другой выход. ... Научившись пользоваться регулярными выражениями, вы будете удивляться, как раньше обходились без них.

И я скажу он прав, впервые я брал его книгу в руки год назад, за это время я многократно ускорился в рутинных делах всего лишь зная базовые приемы регулярок. Сейчас же, хочу зайти еще дальше.

Мне очень понравился этот тул и сегодня я его рекомендую для экспериментов. А изучать регекспы мы будем на практике. И вот тот текст, который сегодня будем использовать для примеров. Текст песни "Звезда по имени Солнце" Виктора Цоя. Надеюсь никто не против, я очень люблю эту песню.

Белый снег, серый лед
На растрескавшейся земле
Одеялом лоскутным на ней
Город в дорожной петле


А над городом плывут облака
Закрывая небесный свет
А над городом желтый дым
Городу две тысячи лет
Прожитых под светом звезды по имени Солнце


И две тысячи лет война -
Война без особых причин
Война - дело молодых
Лекарство против морщин


Красная-красная кровь
Через час уже просто земля
Через два на ней цветы и трава
Через три она снова жива
И согрета лучами звезды по имени Солнце


И мы знаем, что так было всегда
Что судьбою был больше любим, -
Кто живет по законам другим
И кому умирать молодым


Он не помнит слова "Да" и слова "Нет"
Он не помнит ни чинов ни имен
И способен дотянуться до звезд
Не считая что это сон
И упасть опаленный звездой по имени Солнце


Предлагаю ее скопипастить в поле для текста, где написано 'Welcome to RegExr 0.3b, an intuitive...' Лишние пробелы между куплетами можно удалить, чтобы весь текст был перед глазами.

В поле регулярки введем однин символ и увидим как тулза красиво найдет все места, в которых символ встречается.


Если вводить простой текст (а символы его составляющие называются литералами), то тулза ничего необычного из себя не представляет - тупо поиск текст. Но магия начинается с так называемыми метасимволами - спецсимволами которые задают режимы поиска.

Вот ты хочешь поискать все файлы на винчестере с расширением 'txt' - как будешь об этом просить операционку? Ну как-то так - '*.txt'. Вот тут '.txt' - литерал, а '*' - метасимвол. Но это вовсе не регулярные выражения - там немного другие правила. Но принцип тот же.

Попробуй поиграйся с поиском простого текста. Учти, что символы [ ] \ / ^ $ . | ? * + ( ) { } являются метасимволами и пока их не стоит использовать.

Наигрался? Пойдем дальше. Начнем с метасимвола ˹^˼ (крышка, циркумфлекс). Он указывает на начало строки. Попробуй введи в качестве запроса литерал ˹Не˼ - найдешь тем самым два места (как ты уже наверное понял тут все чувствительно к регистру и ˹abc˼ != ˹Abc˼).

Кстати, ты заметил, что весь текст я беру в 'одинарные кавычки', а регулярки ˹угловые кавычки˼. Эту идею так же использует автор книги. Чтобы не запутаться. И еще, на будущее, символ пробела в регулярке будет отображаться точкой ˹˼.


Теперь, чтобы второй вариант (в начале строки), надо указать в качестве запроса ˹^Не˼.

Попробуй. Попробовал? Супер!

Автор книги призывает воспринимать этот нехитрый запрос не так:
˹^Не˼ совпадает, если строка начинается с 'Не'
но так:
˹^Не˼ совпадает, если мы находимся в начале строки, после которого сразу же следует символ 'Н', после которого сразу же следует символ 'е'.
То есть все регекспы стоит воспринимать буквально - так легче будет читать их в будущем.

Идем дальше, если есть метасимвол отвечающий за начало строки, то есть метасимвол отвечающий за конец строки. Вот он ˹$˼

Попробуй из запроса ˹ка˼ совпадающего в двух строках сделай так, чтобы остался только первый.


Вероятно у тебя получилось так ˹ка$˼ - и это правильный ответ. Опять же понимать такой запрос надо буквально:
˹ка$˼ совпадает, если после символа 'к' сразу же идет символ 'а', после которого сразу же следует конец строки.
Хотя по факту получается:
˹ка$˼ совпадает, во всех строках, которые заканчиваются на 'ка'.
это не одно и то же - позже у тебя будет возможность в этом убедиться. 

Несколько вопросов на домашку:
  1. C чем совпадет ˹^$˼?  А как стоит прочитать эту регулярку?
  2. C чем совпадет ˹^˼? А как стоит прочитать эту регулярку?
  3. C чем совпадет ˹$˼? А как стоит прочитать эту регулярку?
  4. C чем совпадет ˹^Городу две тысячи лет$˼? А как стоит прочитать эту регулярку? 
  5. А что будет если написать ˹$^˼? 
 Еще один весьма интересный метасимвол -  ˹.˼ - обыкновенная точка. Она указывает на один произвольный символ. Например попробуй на нашем исходном тексте запроси ˹а.а˼


Количество точек может быть любым. Попробуй ˹а..а˼


Располагаться они могут в любом месте (до литералов, после или между).  Попробуй ˹а...а........˼


Но тут если добавить еще одну точку в конец выражения ˹а...а........˼ - оно будет совпадать только с одним из вариантов. Подумай почему так получается?

Я использовал в качестве литерала только один или два символа, но литералом может быть и целое предложение. Например такой запрос ˹......... .......зве....по имени Солнце˼


Будь внимателен с точками, потому как точка - это абсолютно любой символ и если ты захочешь выбрать из текста все даты '27.11.2004' '27-11-2004' и '27/11/2004' то запросом ˹27.11.04˼ ты выберешь так же и чей-то номер телефона '2771132004' это частая ошибка новичков.

Хорошо, а если я хочу использовать метасимвол как литерал? Все просто - перед ним тогда стоит поставить метасимвол ˹\˼ - обратная косая черта экранирует следующий за ней символ. То есть ˹27\.11\.04˼ совпадет только с '27.11.2004'

На сегодня все. В следующий раз разберемся с символьными классами, но чтобы ты не скучал вот тебе пару примеров из следующего поста:
˹бы[лх]˼
˹о[лср]о˼
˹е[а-я]•˼  (напомню, что ˹˼ - это пробел)
˹е[абв]˼ 
˹[-ая]˼
˹[а-вэ-я]л˼
˹[к-ну]т˼
˹[а..]с˼
˹[а-я][к-н]˼

Приятных экспериментов!

пятница, 23 марта 2012 г.

Как быстро засетапить программу для какого-то ивента

Ребята, не знаю, быть может есть онлайн тулы для удобного создания прогhаммы ивента, но искать не стал - быстрее для меня написать пару формул в excel. Когда на меня упала задачка распланировать однодневный ивент я реализовал эту формочку, которую и хочу задэмить следующим видео.



Юзайте наздоровье. Надеюсь сэкономит кому-то время...

четверг, 22 марта 2012 г.

"Помолимся за родителей"

Стихи: Губин К.
Композитор: Сосо Павлиашвили





Я отпил у взрослой жизни рано,
А вот сейчас бы убежать, в детство убежать
Как хочу к тебе прижаться, мама,
И папе тайны рассказать, тайны рассказать


Вы простите мне разлуки грешность,
Время закрутило, занесло...
Пусть хранят ваш дом: любовь и нежность,
Они из детства моего, детства моего


Помолимся за родителей, за всех живых и небожителей,
И в час, когда станет холодать, их души свечами согреем
Помолимся за родителей, ангелам нашим, хранителям
Помолимся, и когда-нибудь помолятся дети за нас


Я давно мудрее стал и старше,
Но лишь сейчас одно постиг, истину постиг,
Дай Вам Бог, уйти минутой раньше
Своих детей давно седых, нас давно седых


Вы простите мне разлуки грешность,
Время закрутило, занесло...
Пусть хранят ваш дом: любовь и нежность,
Они из детства моего, из детства моего


Помолимся за родителей, за всех живых и небожителей,
И в час, когда станет холодать, их души свечами согреем
Помолимся за родителей, ангелам нашим, хранителям
Помолимся, и когда-нибудь помолятся дети за нас

среда, 21 марта 2012 г.

Страх публичных выступлений или "Ну я пока еще не готов"

Если на предложение "А хочешь выступить с докладом?" мысли появляются "Неее, мне пока рано перед публикой выступать..." то этот мессадж тебе адресован...


"Самое время" никогда так и не наступит, пока не сделаешь первый раз. Кривой, косой - все равно. Первый раз надо сделать. И нет для него лучшего времени, чем Сегодня. А там уже решишь, было ли время подходящим или нет. Что было так, а что не очень. Что улучшить на будущее.

Если страшно самому - найди себе напарника которому тоже страшно, и выступите вместе. Так вы разделите один страх на 2. Уже легче.

Попробуй рассмешить. Если слушатель улыбается или смеется - это уже win. И не важно что ты рассказываешь.

Посмотри http://www.slideshare.net/thecroaker/death-by-powerpoint-rus и ты узнаешь, каксделать презентацию для своего долада.

В начале пути будет сложно концентрировать на себе внимание большой аудитории, а потому "прячься" за яркими и смешными слайдами, которые часто меняются на проекторе. Твой слушатель это оценит. Чуть позже (после ряда докладов) переключай внимание все больше на себя.

Нчни выступление с небольшой аудитории в которой сидят близкие тебе люди - можно начинать с твоей команды. Позже выходить на площадку клубов, котих при твоей компании должно быть моного. Дальше можно попробовать вебинар. Потом уже и выступление на конференции.

Почитай http://wiki.4intra.net/Conference-for-speakers и ты узнаешь, как стоит готовиться докладчику к своему докладу пред живой аудиторией.

Посмотри пример классного живого доклада Димы Миндры http://xpdays.com.ua/archive/xp-days-ukraine-2011/materials/architecture/ и сделай выводы лично для себя. Я пересматриваю это виде всякий раз, когда готовлюсь - вдохновляет! Дима, спасибо за пример!

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

Не так оно страшно как рисуется, хотя достаточно волнительно - не спорю... А втянешься - как наркотик!

А на замечание "Неее, мне пока рано перед публикой выступать..." я реагировать буду как на "а я еще ни разу не делал этого и не знаю с чего начать" и буду рекомендовать как минимум это чтиво. Рекомендуй его и ты.

Нам всем есть, чем поделитсья с аудиторией! Айтишник 2.0 просто обязан прокачивать свои public speaking навыки.

Все получится!

вторник, 20 марта 2012 г.

Как писать каждый день по посту?

Для начал начну с благодарности дорогому и постоянному читателю hazzik, который вносит конструктивные предложения. Спасибо Саша.

Идея появилась в кулуарах этого поста и я рад что она появилась. Теперь я сделал это своей привычкой и выгляжу еще более рационально, при этом оставаясь иррациональным внутри.

В чем дело? Дело в том, что я пишу посты запоями - по несколько штук под настроение а потом неделями ничего не пишу. Для читателя это оказалось немного неудобно, а потому Саша предложил вариант отложенной публикации, благо блогспот поддерживает это.

Как делать отложенную публикацию в блог? Просто. Перед тем как опубликовать пост кликаем на "настройки сообщения" а потом "Запланировано в", где указываем дату и время публикации.

Я к примеру ставлю всегда 11:00 каждый следующий свободный день. После пост можно публиковать. Он не отобразится на главной страничке.

Кстати с помощью этого поля можно в blogspot сделать так, чтобы какой-то пост был всегда на первой страничке, даже если ты написал некоторые другие. Для этого надо опубликовать пост как обычно, а потом открыть его на редактирование и выставить дату, до которой мы хотим, чтобы пост был первым. После снова публикуем. Хак или нет, но работает :)

Но вернемся к нашим тараканам. После подобного опубликования пост появится в разделе "запланировано"


Там же можно подглядеть до какого дня у тебя посты есть. К примеру, сейчас я целых три дня (уже 4 могу не переживать) - блог будет жить своей жизнью.

А посты пишу я ровно столько сколько мне интересно это делать. Иногда пол часа иногда час, иногда четыре. Но всегда я останавливаюсь тогда, когда мне это надоедает а не тогда когда мысль исчерпается. Мысль неисчерпаемый ресурс.

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

Механизм отложенной публикации натолкнул меня на одну идею. Можно сделать так, чтобы блог жил задолго после его автора :) Можно ли так обмануть смерть? Филосовский вопрос...

А еще можно самому себе поздравления писать :) В общем рационально планировать публикацию... Интересно как еще этот инструмент можно использовать?

понедельник, 19 марта 2012 г.

Java for fun: Что такое Dependency injection, Inversion of Control и почему это возникло. Часть #6

В прошлый раз обещал, что выложу самописный IoC контейнер. Видимо пора бы это сделать уже - столько времени прошло.

Итак исходники качаем тут.

Начнем как всегда с тестов.

package container;
import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;

import container.ioc.*;
import container.flashlight.*;
import container.battery.*;

public class TestBaterry {

    private Container container;
    
    @Before 
    public void initContainer() {
        // обрати внимание перед каждым тестом проинитится контейнер, которому мы сообщили, что 
        // заместь батарейки используй ChinaBattery а вместо фонарика SomeFlashlight
        container = new ContainerImpl(
                 Binder.use(ChinaBattery.class).as(Battery.class), 
                 Binder.use(SomeFlashlight.class).as(Flashlight.class));
    }
    
    @Test
    public void testDischargeNewBattery() {    
        // таким нехитрым способом по интерфейсу мы получаем реализацию
        // контейнер пройдется по всем полям новосозданного объекта и 
        // если там найдет знакомые типы то вставит в них реализации, 
        // в соответствии с настройками, которые мы указали
        Flashlight flashlight = container.get(Flashlight.class);        
        assertFalse(flashlight.isShines());
        
        flashlight.swithOn();        
        assertTrue(flashlight.isShines());
        
        for (int count = 0; count < 1000; count ++) {
            flashlight.swithOff();                            
            flashlight.swithOn();                    
        }
        
        flashlight.swithOn();
        assertFalse(flashlight.isShines());        
    }
    
    @Test
    public void testBadBattery() {                                
        Battery battery = new Battery(){
            @Override
            public boolean getVoltage() {            
                return false;
            }
        };
        
        // а вот так мы вдруг можем передумать и переопределить настройки контейнера
        container.update(Binder.use(battery).as(Battery.class));
        
        Flashlight flashlight = container.get(Flashlight.class);        
        assertFalse(flashlight.isShines());
        
        flashlight.swithOn();        
        assertFalse(flashlight.isShines());            
    }
        
    @Test
    public void testNoGetPowerIfDoubleSwithOn() {                        
        Flashlight flashlight = container.get(Flashlight.class);    
        assertFalse(flashlight.isShines());
        
        for (int count = 0; count < 1000; count ++) {                    
            flashlight.swithOn();                    
        }
        
        assertTrue(flashlight.isShines());            
    }    
    
    @Test 
    public void testNoBatteryNoLight() {        
        container.remove(Battery.class);
        
        Flashlight flashlight = container.get(Flashlight.class);
        
        assertFalse(flashlight.isShines());
        
        flashlight.swithOn();
        
        assertFalse(flashlight.isShines());    
    }

    @Test
    public void integrationTestGetPowerFormNewChinaBattery() {                
        Flashlight flashlight = container.get(Flashlight.class);    
        
        assertFalse(flashlight.isShines());
        
        flashlight.swithOn();
        
        assertTrue(flashlight.isShines());    
    }  
}

Именно так я и написал код, когда я начал разработку этого примера. Никаких классов контейнера естественно не было, и тест не компилился. Но я создал пустые классы с помощью IDE (Ctrl1-1 в Eclipse или Alt-Enter в Idea).

Вот он код пустой
package container.ioc;

public class Binder {    

    public static Binder use(Class<?> classToCreate) {
        return null;
    }
    
    public static Binder use(Object object) {
        return null;
    }

    public Binder as(Class<?> interfaceClass) {
        return null;
    }    
}

package container.ioc;

public interface Container {

    <T> T get(Class<T> interfaceClass);

    void remove(Class<?> clazz);

    void update(Binder binder);
}

package container.ioc;

public class ContainerImpl implements Container {

    public ContainerImpl(Binder...binders) {    
    }
    
    @Override
    public <T> T get(Class<T> interfaceClass) {
        return null;
    }

    @Override
    public void remove(Class<?> clazz) {        
    }

    @Override
    public void update(Binder binder) {
    }
}

Так теперь код хоть компилится! :) После того как была создана интерфейсная часть, я взялся за реализацию с помощью TDD.

Вот те тестовые сценарии, которые я один за другим написал и реализовал. Кода очень много :) потому что должен был проверить всевозможные случаи инъекции с разными полями, с одинаковыми полями, с наследниками, с реализациями, с примитивами и так далее. Если хочешь скипнуть - жми сюда.

Текста на самом деле много. А еще я все классы сделал Inner классами хотя в реальной версии они должны быть отдельными public классами иначе ничего не получится. Работа с иннераклассами у меня у туду.

package container2.ioc.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import org.junit.Test;

import container.ioc.Binder;
import container.ioc.ContainerImpl;

public class IoCTest {
            
    interface Marker1 {
    }
    
    class Marker1Impl implements Marker1{
    }
    
    interface Marker2 {
    }
    
    class Marker2Impl implements Marker2 {
    }
    
    interface Marker3 {
    }
    
    class Marker3Impl implements Marker3 {
    }
    
    interface MainMarker {
    }
    
    private <T> T getImpl(Class<T> mainClass) {
        return (T)new ContainerImpl(
                Binder.use(Marker1Impl.class).as(Marker1.class), 
                Binder.use(Marker2Impl.class).as(Marker2.class),
                Binder.use(Marker3Impl.class).as(Marker3.class),                
                Binder.use(mainClass).as(MainMarker.class)).get(MainMarker.class);
    }
    
    class ClassWithDefaultConstructor implements MainMarker {
        Marker1 marker1; 
        
        public ClassWithDefaultConstructor() {
            
        }
    }
    
    @Test
    public void testWithDefaultConstructor() {
        assertEquals(Marker1Impl.class, 
                getImpl(ClassWithDefaultConstructor.class).marker1.getClass());            
    }
    
    class ClassWithoutAnyConstructors implements MainMarker {
        Marker1 marker1; 
        
        private ClassWithoutAnyConstructors() {        
        }
    }
    
    @Test
    public void testWithoutAnyConstructors() {
        assertEquals(Marker1Impl.class, 
                getImpl(ClassWithoutAnyConstructors.class).marker1.getClass());            
    }
    
    class ClassWithPrivateField implements MainMarker {
        private Marker1 marker1; 
        
        public ClassWithPrivateField() {}

        public Object getMarker1() {
            return marker1;
        }
    }
    
    @Test
    public void testWithPrivateField() {
        assertEquals(Marker1Impl.class, 
                getImpl(ClassWithPrivateField.class).getMarker1().getClass());            
    }
    
    class ClassWithInjectorConstructor implements MainMarker {
        private Marker1 marker1;
        
        public ClassWithInjectorConstructor(Marker1 marker1) {
            this.marker1 = marker1;
        }

        public Marker1 getMarker1() {
            return marker1;
        }            
    }
                
    @Test
    public void testWithInjectorConstructor() {
        assertEquals(Marker1Impl.class, 
                getImpl(ClassWithInjectorConstructor.class).getMarker1().getClass());            
    }
    
    class СlassWithInjectorConstructorAndTwoFields implements MainMarker {
        
        private Marker1 marker1;
        private Marker2 marker2;
        
        public СlassWithInjectorConstructorAndTwoFields(Marker2 marker2, Marker1 marker1) {
            this.marker1 = marker1;
            this.marker2 = marker2;
        }

        public Marker1 getMarker1() {
            return marker1;
        }
        
        public Marker2 getMarker2() {
            return marker2;
        }    
    
    }
    
    @Test
    public void testWithInjectorConstructorAndTwoFieldsCheckField1() {
        assertEquals(Marker1Impl.class, 
                getImpl(СlassWithInjectorConstructorAndTwoFields.class).getMarker1().getClass());            
    }
    
    @Test
    public void testWithInjectorConstructorAndTwoFieldsCheckField2() {
        assertEquals(Marker2Impl.class, 
                getImpl(СlassWithInjectorConstructorAndTwoFields.class).getMarker2().getClass());            
    }    
    
    class СlassWithPrivateInjectorConstructorAndTwoFields implements MainMarker {
        private Marker1 marker1;
        private Marker2 marker2;
        
        private СlassWithPrivateInjectorConstructorAndTwoFields(Marker2 marker2, Marker1 marker1) {
            this.marker1 = marker1;
            this.marker2 = marker2;
        }

        public Marker1 getMarker1() {
            return marker1;
        }
        
        public Marker2 getMarker2() {
            return marker2;
        }        
    }
    
    @Test
    public void testWithPrivateInjectorConstructorAndTwoFieldsCheckField1() {
        assertEquals(Marker1Impl.class, 
                getImpl(СlassWithPrivateInjectorConstructorAndTwoFields.class).getMarker1().getClass());            
    }
    
    @Test
    public void testWithPrivateInjectorConstructorAndTwoFieldsCheckField2() {
        assertEquals(Marker2Impl.class, 
                getImpl(СlassWithPrivateInjectorConstructorAndTwoFields.class).getMarker2().getClass());            
    }
    
    class ClassWithTwoConstructors implements MainMarker {
        private Marker1 marker1; 
        
        public ClassWithTwoConstructors() {
            
        }
        
        private ClassWithTwoConstructors(Marker1 marker) {
            this.marker1 = marker;
        }
        
        public Object getMarker1() {
            return marker1;
        }
    }
    
    @Test
    public void testWithTwoConstructors() {
        assertEquals(Marker1Impl.class, 
                getImpl(ClassWithTwoConstructors.class).getMarker1().getClass());            
    }
    
    class ClassWithTwoConstructorsWitnSameParametersCount implements MainMarker {
        private Marker1 marker1; 
            
        private ClassWithTwoConstructorsWitnSameParametersCount(Marker1 marker) {
            this.marker1 = marker;
        }
        
        private ClassWithTwoConstructorsWitnSameParametersCount(String string) {
        }
        
        public Object getMarker1() {
            return marker1;
        }
    }
    
    @Test
    public void testWithTwoConstructorsWithSameParametersCount() {
        assertEquals(Marker1Impl.class, 
                getImpl(ClassWithTwoConstructorsWitnSameParametersCount.class).getMarker1().getClass());            
    }
    
    class ClassWithTwoFieldsAndOnlyOneInjectedViaConstructor implements MainMarker {
        private Marker1 marker1; 
        private Marker2 marker2;
        private boolean costructorCall = false;
            
        private ClassWithTwoFieldsAndOnlyOneInjectedViaConstructor(Marker1 marker) {
            this.costructorCall = true;
            this.marker1 = marker;
        }
            
        public Object getMarker1() {
            return marker1;
        }
        
        public Object getMarker2() {
            return marker2;
        }

        public boolean isCostructorCall() {
            return costructorCall;
        }
    }
    
    @Test
    public void testWithTwoFieldsAndOnlyOneInjectedViaConstructor1() {
        assertEquals(Marker1Impl.class, 
                getImpl(ClassWithTwoFieldsAndOnlyOneInjectedViaConstructor.class).getMarker1().getClass());            
    }
    
    @Test
    public void testWithTwoFieldsAndOnlyOneInjectedViaConstructor2() {
        assertEquals(Marker2Impl.class, 
                getImpl(ClassWithTwoFieldsAndOnlyOneInjectedViaConstructor.class).getMarker2().getClass());            
    }
    
    @Test
    public void testWithTwoFieldsAndOnlyOneInjectedViaConstructor_constructorUsed() {
        assertTrue(getImpl(ClassWithTwoFieldsAndOnlyOneInjectedViaConstructor.class).isCostructorCall());            
    }
    
    class ClassWithTwoFieldsAndOtherOneInjectedViaConstructor implements MainMarker {
        private Marker1 marker1; 
        private Marker2 marker2;
        private boolean costructorCall = false;
            
        private ClassWithTwoFieldsAndOtherOneInjectedViaConstructor(Marker2 marker) {
            this.costructorCall = true;
            this.marker2 = marker;
        }
            
        public Object getMarker1() {
            return marker1;
        }
        
        public Object getMarker2() {
            return marker2;
        }

        public boolean isCostructorCall() {
            return costructorCall;
        }
    }
    
    @Test
    public void testWithTwoFieldsAndOtherOneInjectedViaConstructor_CheckField1() {
        assertEquals(Marker1Impl.class, 
                getImpl(ClassWithTwoFieldsAndOtherOneInjectedViaConstructor.class).getMarker1().getClass());            
    }
    
    @Test
    public void testWithTwoFieldsAndOtherOneInjectedViaConstructor_CheckField2() {
        assertEquals(Marker2Impl.class, 
                getImpl(ClassWithTwoFieldsAndOtherOneInjectedViaConstructor.class).getMarker2().getClass());            
    }
    
    @Test
    public void testWithTwoFieldsAndOtherOneInjectedViaConstructor_constructorUsed() {
        assertTrue(getImpl(ClassWithTwoFieldsAndOtherOneInjectedViaConstructor.class).isCostructorCall());            
    }
    
    class ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor implements MainMarker {
        private Marker1 marker1; 
        private Marker2 marker2;
        private boolean costructorCall = false;
        private Marker3 marker3;
            
        private ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor(Marker3 marker) {
            this.costructorCall = true;
            this.marker3 = marker;
        }
            
        public Object getMarker1() {
            return marker1;
        }
        
        public Object getMarker2() {
            return marker2;
        }

        public boolean isCostructorCall() {
            return costructorCall;
        }

        public Object getMarker3() {
            return marker3;
        }
    }
    
    @Test
    public void testWithThreeFieldsAndOtherOneInjectedViaConstructor_CheckField1() {
        assertEquals(Marker1Impl.class, 
                getImpl(ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor.class).getMarker1().getClass());            
    }
    
    @Test
    public void testWithThreeFieldsAndOtherOneInjectedViaConstructor_CheckField2() {
        assertEquals(Marker2Impl.class, 
                getImpl(ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor.class).getMarker2().getClass());            
    }
    
    
    @Test
    public void testWithThreeFieldsAndOtherOneInjectedViaConstructor_CheckField3() {
        assertEquals(Marker3Impl.class, 
                getImpl(ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor.class).getMarker3().getClass());            
    }
    
    @Test
    public void testWithThreeFieldsAndOtherOneInjectedViaConstructor_constructorUsed() {
        assertTrue(getImpl(ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor.class).isCostructorCall());            
    }
    
    class Marker1Fasade implements Marker1 {

        private Marker1 marker;

        public Marker1Fasade(Marker1 marker) {
            this.marker = marker; 
        }
        
        public Object getMarker() {
            return marker;
        }
        
    }
    
    class ClassWithTwoFieldsAndOnlyOneInjectedViaConstructorWithFasade implements MainMarker {
        private Marker1 marker1; 
        private Marker2 marker2;
            
        private ClassWithTwoFieldsAndOnlyOneInjectedViaConstructorWithFasade(Marker1 marker) {
            this.marker1 = new Marker1Fasade(marker);
        }
            
        public Object getMarker1() {
            return marker1;
        }
        
        public Object getMarker2() {
            return marker2;
        }
    }
    
    @Test
    public void testWithTwoFieldsAndOnlyOneInjectedViaConstructorWithFasade_checkThatNoReflectionInjectionIfConstructorInjected() {
        ClassWithTwoFieldsAndOnlyOneInjectedViaConstructorWithFasade impl = 
            getImpl(ClassWithTwoFieldsAndOnlyOneInjectedViaConstructorWithFasade.class);
        
        assertEquals(Marker1Fasade.class, impl.getMarker1().getClass());            
        assertEquals(Marker1Impl.class, ((Marker1Fasade)impl.getMarker1()).getMarker().getClass());
    }
    
    class ClassWithThreeFieldsAndTwoInjectedViaConstructor implements MainMarker {
        private Marker3 marker3;
        private boolean costructorCall = false;
        private Marker1 marker1; 
        private Marker2 marker2;
            
        private ClassWithThreeFieldsAndTwoInjectedViaConstructor(Marker3 marker3, Marker2 marker2) {
            this.costructorCall = true;
            this.marker3 = marker3;
            this.marker2 = marker2;
        }
            
        public Object getMarker1() {
            return marker1;
        }
        
        public Object getMarker2() {
            return marker2;
        }

        public boolean isCostructorCall() {
            return costructorCall;
        }

        public Object getMarker3() {
            return marker3;
        }
    }
    
    @Test
    public void testWithThreeFieldsAndTwoInjectedViaConstructor_CheckField1() {
        assertEquals(Marker1Impl.class, 
                getImpl(ClassWithThreeFieldsAndTwoInjectedViaConstructor.class).getMarker1().getClass());            
    }
    
    @Test
    public void testWithThreeFieldsAndTwoInjectedViaConstructor_CheckField2() {
        assertEquals(Marker2Impl.class, 
                getImpl(ClassWithThreeFieldsAndTwoInjectedViaConstructor.class).getMarker2().getClass());            
    }
    
    @Test
    public void testWithThreeFieldsAndTwoInjectedViaConstructor_CheckField3() {
        assertEquals(Marker3Impl.class, 
                getImpl(ClassWithThreeFieldsAndTwoInjectedViaConstructor.class).getMarker3().getClass());            
    }
    
    @Test
    public void testWithThreeFieldsAndTwoInjectedViaConstructor_constructorUsed() {
        assertTrue(getImpl(ClassWithThreeFieldsAndTwoInjectedViaConstructor.class).isCostructorCall());            
    }    
    
    class Marker3Fasade implements Marker3 {

        private Marker3 marker;

        public Marker3Fasade(Marker3 marker) {
            this.marker = marker; 
        }
        
        public Object getMarker() {
            return marker;
        }    
    }
    
    class ClassWithThreeFieldsAndTwoConstructors implements MainMarker {
        private Marker3 marker3;
        private boolean isCommonCostructorCall = false;
        private Marker1 marker1; 
        private Marker2 marker2;
        
        private ClassWithThreeFieldsAndTwoConstructors(Marker1 marker1) {
            this.marker1 = marker1;
        }
        
        private ClassWithThreeFieldsAndTwoConstructors(Marker3 marker3, Marker2 marker2) {
            this.isCommonCostructorCall = true;
            this.marker3 = new Marker3Fasade(marker3);
            this.marker2 = marker2;
        }
            
        public Object getMarker1() {
            return marker1;
        }
        
        public Object getMarker2() {
            return marker2;
        }

        public boolean isCommonCostructorCall() {
            return isCommonCostructorCall;
        }
        
        public Object getMarker3() {
            return marker3;
        }
    }
    
    @Test
    public void testWithThreeFieldsAndTwoConstructors_CheckField1() {
        assertEquals(Marker1Impl.class, 
                getImpl(ClassWithThreeFieldsAndTwoConstructors.class).getMarker1().getClass());            
    }
    
    @Test
    public void testWithThreeFieldsAndTwoConstructors_CheckField2() {
        assertEquals(Marker2Impl.class, 
                getImpl(ClassWithThreeFieldsAndTwoConstructors.class).getMarker2().getClass());            
    }
    
    @Test
    public void testWithThreeFieldsAndTwoConstructors_CheckField3() {        
        ClassWithThreeFieldsAndTwoConstructors impl = 
            getImpl(ClassWithThreeFieldsAndTwoConstructors.class);
        
        assertEquals(Marker3Fasade.class, impl.getMarker3().getClass());            
        assertEquals(Marker3Impl.class, ((Marker3Fasade)impl.getMarker3()).getMarker().getClass());        
    }
    
    @Test
    public void testWithThreeFieldsAndTwoConstructors_constructorUsed() {
        assertTrue(getImpl(ClassWithThreeFieldsAndTwoConstructors.class).isCommonCostructorCall());            
    }    
    
    class ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed implements MainMarker {
        private Marker3 marker3;
        private boolean isCommonCostructorCall = false;
        private Marker1 marker1; 
        private Marker2 marker2;
        
        private ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed(Marker1 marker1) {
            this.marker1 = marker1;
        }
        
        private ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed(Marker3 marker3, Marker2 marker2) {
            this.isCommonCostructorCall = true;
            this.marker3 = new Marker3Fasade(marker3);
            this.marker2 = marker2;
        }
        
        private ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed(Marker3 marker3, Marker2 marker2, String bla) {
            this.marker3 = marker3;
            this.marker2 = marker2;
        }
            
        public Object getMarker1() {
            return marker1;
        }
        
        public Object getMarker2() {
            return marker2;
        }

        public boolean isCommonCostructorCall() {
            return isCommonCostructorCall;
        }
        
        public Object getMarker3() {
            return marker3;
        }
    }
    
    @Test
    public void testWithThreeFieldsAndThreeConstructorsOneIsNotUsed_CheckField1() {
        assertEquals(Marker1Impl.class, 
                getImpl(ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed.class).getMarker1().getClass());            
    }
    
    @Test
    public void testWithThreeFieldsAndThreeConstructorsOneIsNotUsed_CheckField2() {
        assertEquals(Marker2Impl.class, 
                getImpl(ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed.class).getMarker2().getClass());            
    }
    
    @Test
    public void testWithThreeFieldsAndThreeConstructorsOneIsNotUsed_CheckField3() {        
        ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed impl = 
            getImpl(ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed.class);
        
        assertEquals(Marker3Fasade.class, impl.getMarker3().getClass());            
        assertEquals(Marker3Impl.class, ((Marker3Fasade)impl.getMarker3()).getMarker().getClass());        
    }
    
    @Test
    public void testWithThreeFieldsAndThreeConstructorsOneIsNotUsed_constructorUsed() {
        assertTrue(getImpl(ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed.class).isCommonCostructorCall());            
    }
}

Постепенно появлялась реализация. После несокльких подходов к коду я родил вот эти реализации

package container.ioc;

public class Binder {

    Class<?> interfaceClass;
    Class<?> classToCreate;
    Object object;

    public Binder(Class<?> classToCreate) {
        this.classToCreate = classToCreate;
    }

    public Binder(Object object) {
        this.object = object;
    }

    public static Binder use(Class<?> classToCreate) {
        return new Binder(classToCreate);
    }
    
    public static Binder use(Object object) {
        return new Binder(object);
    }

    public Binder as(Class<?> interfaceClass) {
        this.interfaceClass = interfaceClass;
        return this;
    }
    
}

package container.ioc;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

public class ContainerImpl implements Container {

    private Collection<Binder> binders;

    public ContainerImpl(Binder...binders) {
        this.binders = new LinkedList<Binder>(Arrays.asList(binders));
    }
    
    @Override
    public <T> T get(Class<T> interfaceClass) {
        Binder binder = find(interfaceClass);
        
        if (binder.object != null) {
            return (T) binder.object;            
        }
        
        Class<?> classToCreate = binder.classToCreate;
        
        List<Constructor<?>> constructors = getCostructors(classToCreate);
        
        List<Object> dependencies = getObjectsFor(onlyInterfaceTypes(getTypes(getFields(classToCreate))));                                                    
                
        return (T) foundCommonConstructor(constructors, dependencies).newInstanceFor(dependencies);                                     
    }

    private InstanceMaker foundCommonConstructor(
            List<Constructor<?>> constructors, List<Object> dependencies) 
    {
        sortByParameterCount(constructors);
        
        for (Constructor<?> constructor : constructors) {
            if (sufficiently(constructor.getParameterTypes(), dependencies)) {
                return new InstanceMaker(constructor); 
            }
        }        
        
        throw new RuntimeException("Constructor not found");
    }

    private void sortByParameterCount(List<Constructor<?>> constructors) {
        Collections.sort(constructors, new Comparator<Constructor<?>>() {
            @Override
            public int compare(Constructor<?> constructor1, Constructor<?> constructor2) {
                return constructor2.getParameterTypes().length - 
                    constructor1.getParameterTypes().length;
            }                
        });
    }    

    private boolean sufficiently(Class<?>[] parameterTypes, List<Object> dependencies) {        
        for (Class<?> clazz : parameterTypes) {
            if (notIn(clazz, dependencies)) {
                return false;
            }
        }
        return true;        
    }

    private boolean notIn(Class<?> clazz, List<Object> dependencies) {
        for (Object dependency : dependencies) {
            if (clazz.isAssignableFrom(dependency.getClass())) {
                return false;
            }
        }
        return true;
    }

    private List<Class<?>> getTypes(Collection<Field> fields) {
        List<Class<?>> result = new LinkedList<Class<?>>();
        for (Field field : fields) {            
            result.add(field.getType());
        }
        return result;
    }
    
    private List<Class<?>> onlyInterfaceTypes(List<Class<?>> classes) {
        List<Class<?>> result = new LinkedList<Class<?>>();
        for (Class<?> clazz : classes) {
            
            try {
                find(clazz);
                result.add(clazz);
            } catch (RuntimeException e) {
                continue;
            }
        }
        return result;                
    }
    
    private Binder find(Class<?> interfaceClass) {
        for (Binder binder : binders) {
            if (binder.interfaceClass.equals(interfaceClass)) {
                return binder;
            }
        }
        throw new RuntimeException("Dependency class not found for interface" + interfaceClass.getName());
    }

    private List<Object> getObjectsFor(List<Class<?>> parameterTypes) {
        List<Object> result = new LinkedList<Object>();
        for (Class<?> clazz : parameterTypes) {
            result.add(get(clazz));
        }
        return result;        
    }
    
    private List<Constructor<?>> getCostructors(Class<?> classToCreate) {
        return Arrays.asList(classToCreate.getDeclaredConstructors());        
    }
    
    private Collection<Field> getFields(Class<?> classToCreate) {
        return Arrays.asList(classToCreate.getDeclaredFields());
    }

    @Override
    public void remove(Class<?> clazz) {
        for (Binder binder : binders) {
            if (binder.interfaceClass.equals(clazz)) {
                binders.remove(binder);
                return;
            }
        }        
    }

    @Override
    public void update(Binder binder) {
        remove(binder.interfaceClass);
        binders.add(binder);
    }
}

package container.ioc;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;
import java.util.List;

public class InstanceMaker {
    private Constructor<?> constructor;
    
    public InstanceMaker(Constructor<?> constructor) {
        this.constructor = constructor;
    }
    
    public Object newInstanceFor(List<Object> dependencies) {
        List<Object> foundDependencies = getDependenciesFor(constructor, dependencies);
        Object object = getObject(constructor, foundDependencies);            
        dependencies.removeAll(foundDependencies);
            
        return injectToField(object, dependencies);
    }
    
    private List<Object> getDependenciesFor(Constructor<?> constructor, List<Object> dependencies) {
        List<Object> result = new LinkedList<Object> ();
        for (Class<?> parameterType : constructor.getParameterTypes()) {
            for (Object dependency : dependencies) {
                if (parameterType.isAssignableFrom(dependency.getClass())) { 
                    result.add(dependency);
                    break; 
                }
            }
        }
        return result;
    }
        
    private Object getObject(Constructor<?> constructor, List<Object> dependencies) {
        try {
            constructor.setAccessible(true);
            Object newInstance = constructor.newInstance(dependencies.toArray(new Object[0]));
            constructor.setAccessible(false);
            
            return newInstance;
        } catch (IllegalArgumentException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
    
    private <T> T injectToField(T object, List<Object> dependencies) {
        for (Object dependency : dependencies) {
            Injector.injectAll(object, dependency);
        }
        return object;
    }
}

Классы достаточно сложновастые, но рефлексия все же. Библиотеку Fest Reflection я не хотел подключать из за лишней jar-dependency. В следующий раз попробую сделаю с ней - за одно попиарю этот классный инструмент для работы с рефлексией.

В будущих планах есть сделать то же но со Spring IoC. Ждите, продолжение следует..

воскресенье, 18 марта 2012 г.

Как сохранить сообщения Skype

Недавно возникла задача - найти в хистори скайпа кое что. Дело в том что это "кое что" я не помню в какой общей группе писал, но точно помню что писал и приблизительно какими словами.

Погуглив нашел интересную програмульку SkypeLogView она работает :)

Скайп надо выключить и долго-долго подождать после нажатия кнопки рефреш - если хистори большая, то и времени copy-past займет пару минут.

Есть возможность сохранить в xml, html или тексты всякие comma separated - дело в том, что программа не блещет поиском и для поиска я использовал другую тулу.

А еще в ходе экспериентов оказалось что в винде есть папочка
C:\Users\xxx\AppData\Roaming\Skype\My Skype Received Files в которой есть все когда-либо отправленные файлы через скайп. Ухты! А я искал как-то один, не мог найти.... Что еще там есть?

Зацепило, что вот так вот просто можно потянуть с компа хистори скайпа :) Прибег к мерам - почистил нафиг весь хистори!

суббота, 17 марта 2012 г.

Черно-белые письма

Не надоели черно-белые письма? Мне надоели. Выход как всегда один - что-то не нравится, меняйся пока не попустит. Я и раньше промышлял спамом в компании в которой трудился раньше, но она была небольшая и меня терпели :) Раз в неделю, а позже каждый вечер отправлял на всю компанию в 40 человек письмо-спам. Были недоброжелатели. Ну их можно понять - спам все же. В большой компании это недопустимо, одним таким письмом я могу украсть дни времени. А тем более спам - никто не пропустит, а если он как-то пройдет, то наверное попросят больше так не делать. Но что делать? Почта все равно черно-белая и скучно-серьезная.

Выход нашелся. Нашелся он немного странным образом. Я хотел отвязаться от почты максимально, когда понял что чем больше я пишу - тем больше мне отписывают и тем больше тратится нашего времени. Чтобы изменение легло на меня безболезненно, надо было придумать что-то хитрое - просто так привычка не отвыкается, привычку надо заменять другой привычкой. Я думал, чтобы меньше продуцировать писем в день надо больше времени уделять процедуре отправки одного письма. После этого мн прийдется либо сократить число писем, либо целый день только письмами и заниматься.

Реализация придумалась молниеносно, причем трех зайцев сразу. Процедура такая - если хочу ответить на письмо, то обязательно должен приписать в конце письма какой-то самый смешной (от которого я бы "под стол") прикол из башорга. Причем хорошо бы, чтобы анекдот был в том же контексте, что и текст письма. А чтобы это отделялось от письма, я каждый раз украшаю текст разными цветами и фоном.

Делаю это два месяца. Уже стал получать фидбеки. Все позитивные. Оказывается люди любят, когда их развлекают, даже в деловой строго-черно-белой почте.

Вот как это выглядит на практике.

И так в абсолютно каждом моем письме.

Третий заяц которого поймал - это поднятие себе настроения. Я уже вынужден перечитывать башорг в поисках интересных постов. Кстати, время затраченное на перечитывание смешариков уже окупилось - я нахожу башорг большим источником вдохновения для креативных идей и уже пару тройку придумал и реализовал. Это уже 4й заяц... :) А еще повод написать этот пост - 5й :) Вот это уже по мне - делай что-то что fun, а потом перечисляй почему это стоило сделать.

Вот тебе и вот.

пятница, 16 марта 2012 г.

Не люблю говорить "нет"

Сегодня сказал два раза "нет". Как не люблю это слово. :) Почему не люблю? А все просто, если я сказал кому-то "нет" то это значит (в моей системе ценностей):
- не помог человеку
- не потраблшутил
- не придумал стратегию win-win в которой от помощи выиграю и я тоже
- не придумал как сделать это максимально быстро, так чтобы не отвлекаться от текущих дел
- если честно, то просто не было времени на это все, а значит как-то очень плотно все.


Бывает "нет" направлено на прошлое. Шаблон такой: "Ты это сделал? - Нет" Это значит, что чего-то не учел или пропустил, хотя мог задуматься. Тоже фидбек.

Фуу, какой антипост получился. Ану-ка домножим его на -1 и на всякий случай возьмем по модулю )

Почему я люблю говорить "да"?
- это значит, что мне так же попути
- это значит, что если мне не по пути, то я нашел почему мне по пути а значит см. пункт 1.
- это значит кому-то сегодня помог и день уж точно не зря - потраблшутил
- это значит, что будущее будет более интересным, потому как уже есть кто-то, кто обратится за помощью еще раз, а потом еще (обычно так завязывается дружба)
- это значит я сделал что-то для будущего, проинвестировал
- а так как мне удалось это сделать, значит я еще и нашел быстрое решение, потому как график плотненький обычно

Опять этот плотненький график! Тэкс, надо в отпуск наверное :)

четверг, 15 марта 2012 г.

Как с помощью xpath вытянуть внутренности html странички?

Есть музычка, которую я постоянно слушаю, когда работаю. Об этом писал раньше. Так вот на сайте периодически (хоть и редко) меняются урлы серверов. Когда-то в далеком-далеком прошлом я все-пре-все сохранил в один плейлист, который долго мне служил. Но время прошло и теперь там ни один сервер не грузится. Помню в прошлом на создание этого плейлиста методом открывания каждой из пары десятков ссылок ушло пол часа. На этот раз я решил тех же пол часа инвестировать в эксперименты с xpath.

Итак нам понадобится плагин FirePath для Firefox. После установки этого плагина (и перезагрузке firefox) в окне debug появится вкладка FirePath. Отправимся на исходную страничку и наберем в FirePath следующий код:
//.[contains(text(),'AAC')]/..//a[contains(@href,'.pls')]/@href
После нажатия Enter получим что-то похоже на


Дальше скопируем все ссылки. Сделать это напрямую из FirePath не получилось, а потому пришлось немного извратом заняться. Что сделал? Сохранил исходную страничку на рабочий стол. Открыл в редакторе и вконец ее добавил следующий код.
<script src="http://dfn.dl.sourceforge.net/project/js-xpath/js-xpath/1.0.0/xpath.js" type="text/javascript">
</script>
<script type="text/javascript">
    window.onload = function() {
        // я немного переписал xpath выражение, потому как с помощью xpath.js оригинальный вариант не работал. 
        var xpath = "//.[contains(text(),'Download')]/.[contains(@href,'.pls')]/@href";
        var elements = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);

        function toString(iterator){
            var result = '';
            var item;
            while (item = iterator.iterateNext()){
                result += (item.nodeType == 1 ? item.innerHTML.replace(/</g, "<").replace(/>/g, ">") : item.nodeValue);
            }
            return result;            
        }      
        
        document.clear(); // W3C DOM очень не рекомендует использовать этот метод, но я рискнул
        document.write(toString(elements));        
    }
</script>
Текст примера и xpath.js взят с http://js-xpath.sourceforge.net/xpath-example.html

После стоило всего лишь перегрузить страничку и получить список линков, которые я вставил в текстовый файл и назвал его m3u, а когда тот открыл в winamp то был приятно удивлен - он все сам попарсил и загрузил каждый pls.


Очень приятно осознавать, что невозможного не бывает. Эксперименты заняли немного больше чем планировал - 50 минут. Но я кое в чем прокачался. Поди знай, где этот опыт пригодится.

Я попробовал обобщить код загрузки до такого.
<html>
<body>
<iframe id="iframe" src="http://blablabla.com"></iframe>
<script src="http://dfn.dl.sourceforge.net/project/js-xpath/js-xpath/1.0.0/xpath.js" type="text/javascript">
</script>
<script type="text/javascript">
    window.onload = function() {
        // я немного переписал xpath выражение, потому как с помощью xpath.js оригинальный вариант не работал. 
        var xpath = "//.[contains(text(),'Download')]/.[contains(@href,'.pls')]/@href";
          
        var iframe = document.getElementById("iframe").contentWindow.document;
        var elements = document.evaluate(xpath, iframe, null, XPathResult.ANY_TYPE, null);

        function toString(iterator){
            var result = '';
            var item;
            while (item = iterator.iterateNext()){
                result += (item.nodeType == 1 ? item.innerHTML.replace(/</g, "<").replace(/>/g, ">") : item.nodeValue) + "</br>";
            }
            return result;            
        }      
        
        document.clear(); // W3C DOM очень не рекомендует использовать этот метод, но я рискнул
        document.write(toString(elements));        
    }
</script>
</body>
</html>
Но в месте где я пытался получить document iframe я получал ошибку связанную судя по всему с авторскими правами. Это я понял когда наткнулся на комментарий "хватит пытаться взломать авторский контент" при гуглинге "iframe get document error". По этой причине я не стал выкладывать никаких оригинальных исходников, а всю информацию про сайт, с которого я доставал линки, скрыл. Код (html) в примерах - выдуман мной. А пост написан в целях демонстрации нового инструментария.

Есть у тебя есть информация, как обойти "iframe get document error" поделись в комментах. Я же не стал больше инвестировать в это времени, быть может когда-то в будущем... Поставленная задача была решена, но все же любопытно...

среда, 14 марта 2012 г.

Как узнать кто создал файл?

Не всегда это работает, но от этого инструмент не перестает быть действенным. В общем задача такая - есть файл на серваке, надо узнать кто его туда положил. Смотрим в свойствах. Там чаще всего написан имя юзера (виндовый юзер), который файл создал.


Некоторые форматы файлов так же содержат эту информацию внутри. Например так сделали в этой статье. "Будьмо уважні", как говорит мой друг.

вторник, 13 марта 2012 г.

Изучаю новую песенку

Подготовил этот пост для тех кто хотел бы, но по какой-то причине не решается начать играть на пианино.

Вот услышал случайно и понравилось очень. Автор Miika152.



Вот как с помощью синтезии играется



И так я один денек часика полтора поразбирал что куда тыцять надо. Потом прошло пару недель и вот я в очередной раз попробовал, только по памяти. По результатам можно судить о прогрессе. За одно и посмотреть как происходит заучивание. Как друг раз сказал - задротство. :) Где-то так. Так устроен наш мозг - только повторяя что-то многократно и получая обратную связь спустя некоторое время будет что-то получаться.



Кстати, я заметил, что намного интереснее играть с завязанными глазами. Как-то все иначе мир воспринимается...

Если заинтересовало, быть может тебя заинтересуют и другие мои посты про то как я учусь играть на пианино:
"Как научиться играть на пианино"
"Музыка - точная наука"

понедельник, 12 марта 2012 г.

Java for fun: Что делать, если нет тестов

У меня на тренинге трейни пишут ООП модельки (университет там, жек). Работают в основном с консолью. System.out.println почти в каждом методе. Но тестов пока нет, потому как рано еще давать. Но мне, чтобы демонстративно порефакторить их код надо тесты. Как быть? Выход нашелся....

Студенты как-то интуитивно чувствуют, что нужен некий метод main в котором модель надо позапускать. Иногда даже называют этот класс тестовым. Вот пример такого класса
package ua.kpi.javatrainee6.model;
import ua.kpi.javatrainee6.model.departments.*;
import ua.kpi.javatrainee6.model.people.*;

public class UniversityModel {
 
    private static void testUniverFunctionality(University univer) {
        univer.viewAllSections();
        univer.viewAllSubjects();
        univer.viewAllMembers();
        univer.viewCurriculum();
    }
 
    private static void testGroupFunctionality(Group group) {
        group.viewAllSections();
        group.viewAllMembers();
        group.viewAllSubjects();
        System.out.printf("Average score of group = %1.2f\r\n", group.calculateAverage());
        group.viewCurriculum();
    }
 
    private static void testAdminFunctionality(UniversityAdministrator admin) {
        admin.viewResponsibilities();
        admin.performAssessment();
        System.out.println("Results of assessment");
        for (Group group : admin.getGroups()) {
            System.out.println("For group " + group.getTitle());
            for (Student student : group.getStudents()) {
                student.viewAllMarks();
            }
        }
        System.out.println("Salary: " + admin.calculateSalary());
    }
 
    private static void testProfessorFunctionality(Professor professor) {
        professor.viewResponsibilities();
        professor.viewAllGroups();
        professor.viewCurriculum();
        System.out.println("Salary: " + professor.calculateSalary());
        professor.evaluateAsBadOrGood();
    }
 
    private static void testStudentFunctionality(Student student) {
        student.viewResponsibilities();
        student.viewAllMarks();
        System.out.printf("Average score of student = %1.2f\r\n",     student.calculateAverage());
        student.viewCurriculum();
        System.out.println("Salary: " + student.calculateSalary());
        student.evaluateAsBadOrGood();
    }
 
    public static void main(String[] args) throws IOException {
        University univer = new University("KPI", new Date());
        testUniverFunctionality(univer);

        UniversityAdministrator admin = univer.getAdmin();
        testAdminFunctionality(admin);

        Professor anyProfessor = admin.getProfessors().get(0);
        testProfessorFunctionality(anyProfessor);

        Group anyGroup = admin.getGroups().get(0);
        testGroupFunctionality(anyGroup);

        Student anyStudent = anyGroup.getStudent(0);
        testStudentFunctionality(anyStudent);
    }
}
В университете каждый univer.viewBlaBla() метод печатает что-то на консоль. Инкапсулировать System.out в одном месте задача сложновастая - могу что-то поломать.

Что было сделано. Переоперделил консольку и записал результаты в файл.
...
    public static void main(String[] args) throws IOException {
        ByteOutputStream out = switchSystemOut();

        // тут без изменений

        wtiteTo(out, "1.txt");
    }

    private static void wtiteTo(ByteOutputStream out, String name) throws IOException {
        File path = new File(name);
        path.createNewFile();
        FileOutputStream file = new FileOutputStream(path);
        out.writeTo(file);
        file.close();
    }
}

Считается что там все сейчас работает, а потому делается такой вот фикс
...
    public static void main(String[] args) throws IOException {
        ByteOutputStream out = switchSystemOut();

        // тут без изменений

        String expected = readFrom("1.txt");
        if (!expected.equals(out.toString())) {
            throw new Error("Я чёта поламал!!");
        }
    }

    private static ByteOutputStream switchSystemOut() {
        ByteOutputStream out = new ByteOutputStream();
        System.setOut(new PrintStream(out));
        return out;
    }

    private static String readFrom(String name) throws IOException {
        File path = new File(name);
        InputStream reader = new BufferedInputStream(new FileInputStream(path));
        byte[] data = new byte[reader.available()];
        reader.read(data);
        reader.close();
        return new String(data);
    }
}
Читаем из файла и сравниваем с той же консолькой. Если не equals - значит что-то поломал.

Чем не инструмент? Мне помог.

Да, кстати. Чтобы он помог надо рефакторингом пользоваться осторожно. Делаем небольшое изменение так, чтобы компиляция как можно быстрее прошла. Запускаем "main-тест" и если все ок - коммитимся! Потом проделываем ту же операцию. Если не дай бог тест не проходит - делаем откат и перерыв. Потом снова подходим к коду и пробуем, но уже аккуратнее, сделать то же. Не дебажим!

воскресенье, 11 марта 2012 г.

Философия?

Я люблю быстрые и простые решения - все гениальное просто. Но порой надо копнуть вглубь чтобы найти настоящую причину, а это часто выглядит как философствование. По крайней мере в этом меня часто уличали коллеги на прошлой работе.

Мы не понимали друг друга поскольку в слово "философствование" вкладывали разный смысл. Я не ходил на пары по философии в вузе, потому как там не пахло никакой философией - все изучение чьих-то работ и биографии. Потому и не ходил. Нудотень.

Вот википедия что говорит (в частности).
Философия является мировоззренческой дисциплиной (наукой), поскольку её задачей является обозрение мира в целом, поиск ответов на наиболее общие вопросы.

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

Просто гонять мысли туда сюда мне лично не интересно, только с пользой для дела. Но так же заметил, что если просто гонять мысли туда сюда бесцельно, то в результате получишь нечто, что где-то пригодится. Мышление на перспективу, чтоли.

Задавая вопрос "почему" углубляюсь, а после поняв что-то выныриваю и пытаюсь с вопросом "как" найти полученному знанию применение.

суббота, 10 марта 2012 г.

Отчет automated testing dojo. Киев, 10 марта 2012

Не так давно, 25 февраля этого года, мы с Серегой придумали одну идею. Это была суббота. Мы долго брейнстормили, что же предложить Глебу Рыбалко и Вике Мусияченко в качестве тренинга для автоматизаторов. Офис был пуст - там было свежо. Одно, второе, третье - брейнсторминг во всю. Мы хорошенько накалили митинг рум. Обессилев от генераии мы присели на пуфики и расслабились, и в этот момент случился чик! Уже через 10 минут мы писали видяшку для Глеба с Викой.

Вот она.



Мы были очень довольны этой идеей. Очень-очень. Приступили к разработке тут же. Теперь наши субботы были наполнены одним проектом. Рисовали всякие рисуночки, диаграмки. Спорили как и за что начислять баги...

И совсем скоро, 10 марта, мы провели первую встречу, на которую пригласили наших друзей и Гуру автоматизации Андрея Дзыню и Мишу Поляруша. Цель - собрать фидбек. И фидбек нас приятно удивил. Очень приятно.

Вот, как ребята игрались

Ну а что было дальше читайте в следующих отчетах.