программирование РСС
Что почитать на выходных — 10
14 апреля, 23:06
Понравилось недавно:
- Hammerforum.com. Если бы плотники на форумах общались как фотографы. «That’s because you have absolutely no skills. A good hammer user can drive nails with a 20 pound sledghammer with no problem.»
- Startups, this is how design works. Видео-текстовое эссе о том, зачем стартапу дизайнер. Клёво и интересно сделано.
- A Passenger’s Lot Then... And Now. Сравнение условий на Титанике в 1912 году и на современном корабле. Я и не знал, что Титаник, оказывается, такой был крохотный по современным меркам.
- The Beauty of Silence. Это я в английском блоге написал про тупость искусственного шума в электромобилях. Ничё, что я сам на себя ссылаюсь? Если вы меня там не читаете, самое время подписаться и, главное, рассказать всем своим англоговорящим друзьям.
- PHP: a fractal of bad design. Чувак-питонист рассказывает о том, о чём просто не могут молчать Питонисты: о том, как плох и ужасен ПХП. Всё это, конечно, не имеет значения, но работа, которую провёл автор по классификации всех корявостей ПХП, вызывает уважение. Я вот тоже высказываюсь по теме.
- Love Hotels and Unicode. О том, откуда в Уникоде взялись символы U+1f3e9 Love Hotel и U+1f4a9 Pile of Poo.
Помогите сделать автобекап в Эгее
31 марта, 0:58
Хочу сделать в Эгее автоматический бекап. Мало ли, что. Если вы программист и хотите помочь, то будет очень клёво.
Мне нужно просто делать дамп базы данных в файл. Как это должно работать:
- В функцию передаётся идентификатор соединения с БД, массив со списком таблиц для бекапа, имя файла.
- Функция селектит всё, что нужно, из базы, собирает из этого корректный SQL-дамп (как сделал бы «Пхпмайадмин»), в идеале — заворачивает его в архив. Возвращает true, если всё прошло успешно, false в противном случае.
- Если функция не успела выполниться (кончилось время на выполнение ПХП-скрипта), недописанного файла с дампом оставаться не должно. Всё, что делает функция, должно делаться во временной папке, а в конечную помещаться уже когда всё готово.
- Естественно, функция не может запускать стандартную функцию дампа от самого Май-эс-ку-эля через командную строку, потому что мне никто это не даст делать на среднестатистическом хостинге.
- Функция должна понимать, что версия Май-эс-ку-эля может оказаться какой угодно.
Может такая функция уже существует, и вы просто скажете мне, где её взять. А я уже сам сделаю, чтобы всё это регулярно запускалось и не захламляло сервер бесконечными бекапами. Спасибо.
Что почитать в выходные — 4
11 февраля 2012, 3:44
Понравилось на этой неделе:
- Developing for old browsers is (almost) a thing of the past. 37сигналов рассказывают, как они отпиливают левые браузеры. Горячо поддерживаю.
- To My Old Master. Письмо бывшего раба своему бывшему владельцу (США, 1865 год). Обалдеть.
- Adjust Your Mirrors to Avoid Blind Spots. Совет по настройке боковых зеркал машины. Контринтуитивно, но по утверждению авторов статьи — более безопасно. Езжу так уже три дня, пока немного разрывает мозг.
- What the Vaio Z says about Sony's little design problem. Про разницу в подходах к дизайну у Эпла и Сони.
- Как добавить ПХП в список шеллов в Автоматоре. Женя Степанищев объясняет, как прикрутить ПХП-сервис иначе, чем я показывал — возможно, кому-то так больше понравится.
- Still Fucking Hate Email. Эм-Джи Сиглер предлагает изменить отношение к почте, смотреть на неё как на ленту твиттера, которая проносится мимо тебя, а не как на инбокс, который нужно непременно разобрать. Радикально.
Как написать сервис на ПХП
9 февраля 2012, 23:05
На Маке есть такая странная фича — сервисы. Это такие то ли программы, то ли не программы, которые встраиваются во все приложения и могут как-то преобразовывать контент в них. Например, они могут делать что-то с выделенным текстом. Если прикрутить такой текстовый сервис, то он будет доступен по правой кнопке наряду со стандартными операциями типа копирования, трансформации или произнесения вслух.
Разные приложения могут добавлять свои сервисы, например после установки Твиттера любой выделенный текст можно твитнуть. Но текст — это не обязательно. Сервис может уметь что-то делать с файлами или другими объектам. Девон-технолоджис много всяких сервисов дают. Сервисами никто не пользуется, потому что фиг поймёшь, как с ними совладать и какую пользу они могут принести. Не пользовался особо и я.
Но недавно назрела необходимость сделать себе макросы, которые бы заменяли простые куски текста на большие простыни ХТМЛ-кода. Чтобы писать pic image.jpg, а оно превратилось в целую кучу вёрстки для правильного размещения изображения, да ещё и размер файла само вписывало. Причём не в редакторе кода, где таких фич завались, а в любом месте. Долго объяснять, зачем, но надо было. Программерский мозг не терпит неавтоматизированной работы.
Оказалось, что всё очень просто. Запускаем Автоматор (это ещё одна недооценённая фича Мака) — он спросит, что мы хотим создать. Выбираем сервис и делаем в нём одно-единственное действие — выполнить скрипт:
Настраиваем, чтобы выделенный текст шёл скрипту в stdin и заменялся результатом выполнения (всё в правой части скриншота). Осталось написать сам скрипт на ПХП, а это может даже ребёнок (no pun intended). Саша Карпинский научил, что в начале скрипта нужно заклинание, чтобы он заработал, то есть всё вместе получится как-то так:
#!/usr/bin/php
<?
$input = trim (file_get_contents ('php://stdin'));
if ($input == 'SOPA') die ('PIPA');
echo $input;
?>
Ну, например. А дальше идём в «Систем преференсес», в настройку клавиатуры, и вешаем наш волшебный сервис на любую кнопку. Да, ПХП на Маке встроенный, то есть ничё про это думать не надо.
О чистоте кода
17 января 2012, 1:20
Нам пишет Алексей Чикин:
Хочу задать вопрос именно тебе, как человеку, который сделал Е2. Что важнее, сделать готовый продукт такой, чтобы был прост в обращении, красив, удобен, но при этом сделать его так, как умеешь, без использования всех этих современных паттернов проектирования, замудростей с объектно-ориентированным программированием и выпиливании внутреннего кода до совершенства, или же надо сделать, чтобы всё было кошерно, чтобы код удовлетворял всем современным требованиям?
Просто у меня дилемма. Я сделал нечто, что мне нравится, но я сделал это так, как я умею. И мои умения далеки от гуру-программирования. Стоит ли это нечто представить публике или всё таки убить какое-то время на изучение и рефакторинг кода?
То есть вопрос в следующем. Важно что там и как внутри устроено или результат важнее?
Смотрите, что является вашим продуктом? Код или полезное действие, который он обеспечивает? Я не даю исходники Эгеи, потому что продуктом является сам движок, а не его код. Бывает, конечно, наоборот: если вы делаете какую-то опенсорсную библиотеку, то сам код и является продуктом. В этом случае, несомненно, важно, как он устроен внутри. Но в большинстве случаев пользователи не взаимодействуют с кодом вообще никак и никогда, и поэтому его внутреннее устройство не имеет значения.
Но всё не так просто.
Проблема в том, что такое отношение к коду практически неизбежно влияет на качество самого продукта. Самое главное, что плохой код сильно мешает продукт развивать. Цена внесения изменения сильно выше, из-за того что нужно разобраться, как именно его внести, чтобы не сломать хрупкую конструкцию из хаков, патчей, нигде не сформулированных соглашений. Цена ошибки при таком изменении тоже, очевидно, возрастает — глюки и тормоза образуются лавинообразно.
Эгея вышла только этим летом, потому что я посвятил предыдущие три года приведению в относительный порядок её кода. Это немыслимо длительный срок для обновления программы, даже для некоммерческого проекта выходного дня. Сейчас мне намного проще добавлять разные плюшки благодаря проделанной тогда работе. Если бы я лучше писал код 8 лет назад, сегодня Е2 как продукт был бы в сто раз лучше, чем есть.
Или вот Биатлонтайм. Там совершеннейшая, беспросветная жесть под капотом. Потому что всё приходится программировать очень быстро. Вдруг оказывается, что судьи добавили кому-то 20 секунд времени штрафа согласно правилу такому-то, а код сайта не знает о том, что так вообще может быть. Приходится делать через жопу, прописывая исключение прямо в коде, потому что результаты публиковать надо прямо сейчас, и это важнее красоты кода. А вот заставлять себя потом привести всё это в порядок удаётся не всегда, и это уже мне мешает добавлять новые штуки на сайте.
Так что, конечно, вне всякого сомнения, результат важнее качества кода, но нельзя из этого делать вывод, что на качество кода можно плевать, потому что оно очень быстро начинает влиять на результат.
Отдельно хочу предостеречь вас от приравнивания понятий «качество кода» и «использование современных паттернов проектирования». Между ними нет ничего общего. Использование современных паттернов, ООП, модных фреймворков, валидной семантической вёрстки, распределённых систем контроля версий и всего другого сами по себе никому не нужны. Используйте их только тогда, когда сами точно знаете, какую именно пользу они приносят вашему продукту.
Урлопарсер и урлогенератор в Эгее
26 апреля 2011, 0:26
Раньше как было: все урлы были прописаны в файле .htaccess в виде инструкций мод_реврайта. Даже не буду показывать, на это было больно смотреть. Помимо, собственно, зависимости от мод_реврайта это создавало ещё кучу неудобств. Например, добавление новой страницы означало изменение хтакцесса. Поскольку у меня было автообновление, то движку приходилось править собственный хтакцесс, из-за чего к нему нужен был доступ на запись.
Теперь урлы разбирает сам движок; если реврайта нет, то в урле появляется ?go=, а остальная часть не меняется. Поэтому движку, во-первых, не нужен мод_реврайт, а во-вторых не нужно ничего менять в хтакцессе при добавлении новых страниц. Тут выяснилась приятная деталь: оказывается, мод_реврайт был единственным, что привязывало движок к Апачу; Эгея прекрасно работает и на nginx, и на lighttpd (на русском эти названия непередаваемы).
Разбор урла заключается в том, что любой урл преобразуется в название функции-обработчика, называемой candy (режим или служба в моей старой терминологии; сейчас ещё появились аджакс-обработчики как отдельный институт) и массив её параметров. Например, урл этой заметки «/2011/04/25/1/» преобразуется вот во что:
$candy = 'e2m_note'
$parameters = array (
'year' = '2011',
'month' = '04',
'day' = '25',
'day-number' = '1',
)
Дальше специальный фильтр проверяет, залогинен ли пользователь, а если нет, то можно ли ему выполнять эту candy (смотреть на заметку можно всем, а вот редактировать — только мне, вот смотрите). Если можно, то e2m_note () вызывается для генерации контента страницы с массивом $parameters в качестве параметра. Сама она не интересуется урлом и просто собирает заметку по этим параметрам.
Урлогенератора не было вообще: ссылки писались прямым текстом или собирались из кусков как строки. Если урл чего-то изменялся, то потом можно было целый год обнаруживать глюки, связанные с тем, что в какой-то хитрой ситуации ссылка вела не туда. Сейчас есть функция e2_compose_url (), в которую передаётся candy и её параметры. Поэтому если функции генерации заметки нужно передать в шаблонизатор урлы тегов, то для каждого тега она делает так:
e2_compose_url ('e2m_tag', array ('tag-urlname' => $tagrec['URLName']))
На самом деле это означает «сгенерировать урл, переход по которому вызывал бы e2m_tag (array ('tag-urlname' => $tagrec['URLName']))».
Итого, профит: независимость от мод_реврайта, Апача; гибкость в настройке урлов, независимость урлов от кода, который их обрабатывает; более высокая пуленепробиваемость кода. Когда-нибудь в следующей серии расскажу про шаблонизатор.
Эгея без реестра
12 марта 2011, 12:57
1. Больше нет дебильного реестра!
2. Разбор и генерацию урлов делает движок.
3. Новая подсистема тем-шаблонов берёт вообще весь вывод в браузер на себя.
4. Переписана подсистема кеширования и теперь она работает нормально.
Сначала про реестр. Раньше в E2 был файл registry.psa, в котором хранилось вообще всё, что движку нужно было хранить на сервере, в виде сериализованного ПХП-массива (PSA — это PHP Serialized Array). Ну, кроме того, для чего предназначена БД. Каркас движка был так устроен, что обработка любого запроса начиналась с чтения реестра и заканчивалась его записью. Я относился к нему как к постоянному хранилищу любых данных. Я мог просто написать где угодно $registry['setttings']['somesetting'] = some_value и знать, что это значение сохранится. В результате в реестре хранились всякие параметры, данные установщика, какая-то статистика, хеш пароля, какая-то отладочная фигня, кеш (!) и чёрт знает что ещё.
Иногда случалось страшное: файл реестра умирал. Видимо, в каких-то ситуациях получалось так, что движок не успевал его дописать и прекращал работу (хотя ignore_user_abort был полный). После этого целостность файла нарушалась и прочитать его назад уже было нереально. В какой-то из версий у меня даже появилась специальная чинилка реестра, которая пыталась хоть как-то восстановить что-то из нецелостного файла, но всё равно это спасало далеко не всегда. В результате движок не мог продолжать работу: он даже не знал, как соединиться с базой.
Возврат движка к рабочему состоянию был не очень сложным — для меня, который знал, как там что устроено, — но для пользователей это был коллапс. Просто вдруг всё переставало работать и было непонятно, что делать. Да, инсталлятор тогда не умел устанавливаться путём подключения к существующей базе, а не создания новой (в Эгее — умеет), поэтому даже неясно было, как переустановить движок, чтобы сохранить все заметки.
Сейчас такой лажи просто нет, потому что всё хранится в своём месте и записывается только когда нужно. Файл с пользовательскими параметрами пишется вообще только один раз при установке движка и далее только если кто-то решит их поменять. Поэтому вероятность того, что файл умрёт, стала на несколько порядков меньше. Но даже если это почему-то произойдёт, движку не придёт в голову переставать работать. Всё, что успело сохраниться в кеше, вообще продолжит показывать как ни в чём не бывало. А настройку подключения к базе можно будет повторить прямо через веб-интерфейс.
В результате всё стало радикально надёжнее и немножко быстрее. О других изменениях — в следующей серии.
Как вернуть ранки по колонке?
19 декабря 2010, 14:47
SELECT * FROM BAProtocols
WHERE StartID = 123
ORDER BY TimeAtFinishПолучится какая-то такая таблица результатов гонки (запрос сильно упрощён, конечно, потому что все остальное — не суть).
Могу отсортировать табличку иначе:
SELECT * FROM BAProtocols
WHERE StartID = 123
ORDER BY BibТогда получится старт-лист, типа такого.
Вчера на «Биатлонтайме» появилась возможность сортировать индивидуальные гонки по колонке Ski time, это типа чистое время. Поскольку в базе я храню только итоговое время, то чистое приходится вычислять как итоговое минус штрафы за промахи минус судейские корректировки:
SELECT
<много всего>,
(
BAProtocols.TimeAtFinish
- IF (BAProtocols.S1Misses > 0, BAProtocols.S1Misses * 60000, 0)
- IF (BAProtocols.S2Misses > 0, BAProtocols.S2Misses * 60000, 0)
- IF (BAProtocols.S3Misses > 0, BAProtocols.S3Misses * 60000, 0)
- IF (BAProtocols.S4Misses > 0, BAProtocols.S4Misses * 60000, 0)
- BAProtocols.Adjustment
) _skitime
FROM BAProtocols
WHERE StartID = 123
ORDER BY _skitimeПредвосхищая вопрос «Нахрена иф?»: потому что я храню −1 в стрельбе, если человек вообще не стрелял (бывает, люди не доезжают до финиша, это нужно отражать в протоколах), а 0 означает «чисто отстрелялся».
Получается такое. Кстати, любопытно посмотреть, какое колоссальное значение в индивидуальной гонке имеет качество стрельбы: например, пятая по чистому времени Мириам Гёсснер, в итоговом протоколе даже не попала в очки и оказалась 53-й. Но сейчас не об этом речь.
Дело в том, что при сортировке по чистому времени можно сказать, что каждый спорсмен «занял какое-то место», то есть имеет какой-то rank (не знаю, как по-русски) именно по этому критерию, например, Магдалена Нойнер — 1-я по чистому времени, а Ольга Зайцева — 10-я. Можно ли как-то заставить базу данных вернуть мне именно эти ранки по интересующим меня колонкам, не сортируя таблицу по ним?
Собственно, сейчас сам нужный эффект вы видите в самой первой колонке, когда сортировка не по ней. Например, когда результаты отсортированы по чистому времени, ранки слева продолжают означать место в итоговом протоколе. Но я это делаю вручную, что муторно, хочется, чтобы база сама считала.
То есть, короче, мне нужно по нескольким колонкам вернуть не только их значения, но и натуральные числа, которые означали бы, какой по счёту была бы эта колонка, если бы я отсортировал по ней. Если в колонке одинаковые значения у нескольких строчек, то их ранки должны быть равными, а ранк следующих должен быть таким, как если бы они про повторы ничего не знали (то есть, 1, 2, 3, 3, 3, 6, 6, 8, 9, 10).
Технические ограничения, если это важно: MySQL, MyISAM. Помогайте, пожалуйста.
Тачдев
7 июля 2010, 1:27

Все, кто не тупой, понимают, что сейчас самое время писать программы под Айфон и Айпад и грести бабло лопатой. А Тачдев.ру — мегаресурс для русскоговорящих разработчиков под Ай-ОС. Вот что пишет рекламодатель:
Там очень приятно сделаны статьи, с переносами, висячей типографикой… Я время от времениДа чё говорить, там есть статья на русском языке о том, как написать своё первое приложение под Айфон (разобраться в этом без статьи, несмотря на всё обожание Эплами их средств разработчика, нереально). А для серьёзных ребят там есть подробный рассказ про Гранд-централ-диспетч (телефоны и планшеты ведь тоже скоро будут многоядерными).
улучшаю. Там достаточно высококачественный материал. Попадается разное, но в среднем — очень хороший. Там есть новости, для разработчиков, есть документы, в том числе уникальные, просто заметки.
Там можно задать вопрос, получить ответы — в направлении «Стек-оверфлоу». Сильно проще, конечно, но в отличие от «Стек-оверфлоу» — на русском, оказывается, это проблема для многих.
Там теперь есть афигенный движок трансляций с аджаксом, комментариями и айфон-клиентом.
Реклама на сайте выходит по средам и попадает на экраны 4-5 тысяч человек. Пишите: ilyabirman@ilyabirman.ru.
Иллюстратор и Джаваскрипт
28 февраля 2010, 1:21
Я обожаю придумывать дизайн графиков, диаграмм и таблиц, но все автоматические инструменты строят уродство, а не графики.
Иногда нужно табличные данные вставить и покрасить каждое значение оттенком, зависящим от этого значения. Иногда нужно во всей таблице взять и во всех значения после запятой оставить ровно два знака, набрав их шрифтом чуть меньше. Иногда нужно классы чисел разделить полупробелами. Иногда придуманный удачный формат для каких-то данных хочется опробовать для других данных (например, таблицеграфик каких-то спортивных результатов перестроить для другого матча или заплыва).
Всё это довольно муторная работа, которую хочется автоматизировать.
Так вот, кто-нибудь знает, как это сделать лучше всего? Кажется, адобовские программы как-то поддерживают Джаваскрипт. Как этим воспользоваться? Где задокументированна их объектная модель? Есть ли там консольный режим? Типа, пишешь команду — она сразу же выполняется по энтеру.
Хочется строить график в Иллюстраторе так:
if (line = document.layerNamed ('rus medals')) line.clear ()
else line = document.layers.add ('rus medals')
line.select ()
document.pen (0, 0)
for (i = 0; i <= 16; ++ i) {
document.pen (i * 10, -medalsByCountryAndDate ('RUS', i + 12))
}Ну или что-то вроде того. То есть пишем, что хотим от машины, а не делаем это руками. Я чувствую, как вам хочется придраться к этому коду или придумать свой, но это никому не интересная хрень. Смысл в другом: захотели построить график Новергии — бац, построили. Решили, что 10 пикселей плохое расстояние — исправили на 12, перестроили. Захотели поставить восклицательный знак на графике каждый раз, когда его поднимает золотая медаль — дописали строчку. Захотели воткнуть грустный норвежский флаг везде, где проиграли золото Бьёрндалену — дописали ещё пару строчек.
Кто так умеет, научите меня, а?
Нужна техпомощь
27 февраля 2010, 11:19
Мне нужен человек, который по техзаданию, сформулированному в свободной форме, напишет за выходные десяток-другой (как я думаю) килобайтов ПХП-кода, даже не предполагая, что ему за это что-то дадут, а просто из любви к искусству. А если потом окажется, что всё работает не совсем так, как предполагалось, то за следующие выходные устранит недочёты. Имя героя навсегда войдёт на страницу, рассказывающую о проекте на моём сайте (которая появится в день запуска проекта).
Если вы вызоветесь помочь, это никак вас не обяжет действительно помочь: после того, как вы услышите задачу, вы легко сможете передумать. Единственное, о чём я прошу — это конфиденциальность. То есть, короче, не болтать о проекте до его запуска.
Мне неинтересно знать о том, что вам это неинтересно; интересно знать только если интересно.
Добавлено через несколько часов: Большое спасибо всем откликнувшимся! С моей стороны было бы нечестно загружать задачей сразу всех, так что я буду связываться с вами по очереди, пока с кем-нибудь не договоримся.
О шестнадцатеричных цифрах больших, чем 9
21 декабря 2009, 22:44
Почему указатели трудны и что с этим делать
26 мая 2009, 11:06
Человек должен быть парсером, а это не то, что человеку хорошо удаётся. В случае с указателями, кроме того, что используются плохо читаемые символы, ещё и нет их однозначного «перевода» на человеческий. Например, звёздочка рядом с именем переменной в выражении означает обратное тому, что она означает в описании переменной. Си:
int n[10]; // здесь n хранит адрес, но нет ни звёздочки, ни амперсанда
n[5] = 77; // незаметно поиграли в указатели
*(n + 5) = 77; // здесь звёдочка означает «значение по адресу» (уже заметно)
char *s; // здесь звёздочка уже означает «переменная хранит не значение, а адрес»
unsigned int m = 2131;
*((char *) &m + 1) = 'A'; // теперь, если у меня правильно взорвался мозг, m == 2113
Если бы вместо звёздочки и амперсанда использовались конструкции addressof () и valueat (), для объявления типов был бы модификатор address, а для кастинга использовался бы оператор as указатели бы понимало в 10 раз больше человек. Назовём такой язык Ди (хоть такой уже и есть).
Квадратные скобки в выражениях в Ди пусть означают «значение по адресу с указанным сдвигом», тогда есть для любого x будет справедливо:
x == x[0] == valueat (addressof (x) + 0).
Наличие квадратных скобок в объявлении переменной пусть само по себе не превращает переменную в указатель, то есть, если мы захотим указатель, нам придётся дописать слово address. Тогда запишем первые три строчки нашего кода на Ди:
int address n[10];
valueat (n)[5] = 77; // пока получается некрасиво
valueat (n + 5) = 77; // а тут — нормально
Так обращение к элементам массива, как видно, получается слишком громоздким. Но кто нас заставляет вообще играть в указатели там, где это не нужно? Квадратные скобки в объявлении переменной у нас просто резервируют памяти на несколько таких переменных, но в указатель её не превращают. Так не будем этого делать и мы, выкинем слово address:
int n[10]; // так само n будет хранить значение нулевого инта (n == n[0], напомню)
n[5] = 77; // значение со сдвигом — как раз то, что нам нужно
valueat (addressof (n) + 5) = 77; // длинная запись того же самого
Теперь простая вещь выглядит просто, а игры с указателями выглядят как игры с указателями, но вдобавок не теряют понятности. Синонимичность последних двух строк тоже очевидна. Это прекрасно. А вот другие наши сишные строчки в переводе на Ди:
char address s;
unsigned int m = 2131;
valueat ((addressof (m) as char address) + 1) = 'A'
Теперь, если мы что-нибудь перепутаем, это сразу будет видно:
valueat ((m as char address) + 1) = 'A' // пытаемся представить значение в качестве адреса
valueat (m) = 'A' // пытаемся взять значение по адресу m, в то время как m не является адресом
Такой язык не требует ни больших вычислительных ресурсов, чем Си, ни каких-либо ещё достижений современности, зато читать его легче. Чтобы его придумать, нужно было просто отнестись к задаче чуть внимательнее, чем к ней отнёсся тот, кто нажимал Шифт+цифры в поисках ещё незадействованных символов.
Не исключено, что я где-нибудь наошибался, потому что я вхожу в число тех людей, у кого с указателями дружба складывается весьма посредственно. Тогда подскажите, пожалуйста.
Математика в дизайне
21 мая 2009, 9:35
Задача: сделать такую же штуку в E2 (проверить пароль, не перезагружая страницу, не проблема; мы займёмся только анимацией).
Многие дизайнеры и даже программисты настолько плохо дружат с физикой и математикой, что когда нужно добавить элементу массы (т. е. инерции) или реализовать правдоподобное подёргивание окна, они не в состоянии ничего придумать. Это печально. Мне понадобилась минута, чтобы придумать формулу, ещё 5 — чтобы сделать рабочий скрипт, а потом ещё 5, чтобы подобрать удачные коэффициенты.
Для начала пойдём в Гугль и спросим у него, где можно в онлайне построить график функции. Интернет — великая вещь: первая же ссылка приведёт нас на подходящий сайт. Отличное место, чтобы придумать формулу зависимости горизонтальной координаты окна от времени.
Колебания естественно ассоциируются с синусом, но нам нужно, чтобы они постепенно затухали. Первое, что пришло мне в голову — домножить синусоиду на гиперболу:

Конечно, головой мы качаем совсем не так, зато так получится приятная пружинистость.
Предел sin x / x в нуле равен единице, что, конечно, лучше, чем бесконечность но всё-таки хочется, чтобы в точке 0 значение было нулевым, иначе в начале окно вдруг окажется на сто (например) пикселей правее, чем было до начала анимации. Нам нужно, чтобы гипербола в нуле получила конечное значение, тогда домножение на sin 0, равный нулю, даст ноль. Сдвинем гиперболу чуть левее, чтобы её точка разрыва уехала в неинтересующую нас отрицательную область:
Поскольку гипербола при увеличении x стремится к нулю, но никогда не достигает его, наши колебания, несмотря на затухание, будут продолжаться бесконечно. Можно, конечно, остановить их, когда они станут меньше пикселя, но намного проще чуть-чуть опустить гиперболу, чтобы она всё же достигла нуля через некоторое время:
Чтобы было понятнее, взглянем на саму гиперболу, без синуса (

Сдвиги на 1 левее и на 0,01 ниже взяты от балды; после того, как скрипт уже написан, можно заниматься повышением реалистичности путём изменения этих чисел и примешивания всяческих коэффициентов.
Итоговая формула, с учётом перерисовки каждую 0,01 с, у меня получилась вот такая:
Возведение в степень 1,25 понадобилось, чтобы слегка выгнуть гиперболу, которая затухает слишком стремительно (можно было просто сдвинуть её ещё левее, но так мне больше понравилось).
Дизайнер, который не знает математики, в два раза хуже дизайнера, который её знает. Естественно, на место математики тут можно поставить любой другой предмет — литературу, историю, русский язык, английский язык, биологию, географию, программирование, физику, химию. Кстати, большую половину из перечисленных предметов я знаю крайне плохо.
Будет классно, если читатели поделятся своими примерами того, как знание того или иного предмета помогло им сделать лучший дизайн.
Нотация для названий функций
12 мая 2009, 16:01
[someObject doStuffWithThing:x andThing:y usingParameter:z];
Получаются нормальные предложения. В большинстве языков так делать нельзя, однако формулировать названия функций так, чтобы параметры продолжали предложение, никто не запрещает. Если параметр всего один, или параметры образуют очевидную последовательность, то это добавит удобочитаемости. Я принял нотацию, согласно которой название функции завершается подчёркиванием, если оно составлено по такому принципу. В этом случае открывающая скобка не отбивается пробелом (вообще, я всегда отбиваю; меня страшно бесит, что в ЦССе так делать нельзя).
Пример перевода:
e2_note_by_id ($id) →
e2_note_with_id_($id).
А так могли бы выглядеть некоторые функции с несколькими параметрами:
do_stuff_at_($x, $y),
do_stuff_with_date_($year, $month, $day).
Если параметры не образуют очевидной последовательности или нормально переформулировать смысл функции не удаётся, то функция пишется «по старинке», без подчёркивания в конце:
e2_store_cache ($path, $data, $secure).
А какие вы используете хитрости для повышения удобочитаемости кода?
Красный курсор для русского языка
11 мая 2009, 13:01
Я пытался реализовать такую штуку под Виндоусом, но, насколько я понял, там текстовый курсор на уровне системы вообще не имеет цвета, то есть, чтобы это реализовать, пришлось бы, натурально, рисовать поверх настоящего курсора своё «окно» размером с палку курсора, и мигать этим окном, подстраиваясь под выбранную в системе частоту мигания курсором. Проще застрелиться, чем даже пытаться написать такое заведомо кривое и глючное приложение.
С тех пор, как я переехал на Мак, я не без удовольствия ковыряюсь в местных средствах разработки. Постепенно я научился: отслеживать момент смены раскладки клавиатуры; изменять цвет курсора в текстовом поле моей программы, определять текущее «ключевое» (т. е. отвечающее за обработку клавиатурных событий) окно приложения, определять текущую раскладку клавиатуры.
Оставалось только научиться встраиваться в чужие приложения и находить там текущий активный контрол. Я начитался документации и понял, что моей программе нужно стать инпутменеджером, то есть приложением, посредством которого производится текстовый ввод (это АПИ, видимо, сделано для организации всяких альтернативных способов ввода). Поскольку инпутменеджер — это, по сути, совершенно любой твой код, который выполняется от имени любого другого приложения (и, соответственно, с его правами), то это как бы опасная штука. Вроде как в Тигре даже был какой-то вирус, использовавший этот механизм, поэтому в Леопарде к инпутменеджерам стали предъявляться сверхстрогие требования.
С помощью интернета и Славы Карпенко я разобрался с тем, как сделать и установить инпутменеджером собственный бандл (connecting the dots!) и сделал пруф-оф-концептовый инпутменеджер, реализующий идею Артёма. Он далёк от совершенства:
- Работает только в Какао-приложениях (Мейл, Адрес-бук; Адиум, Скайп; системное меню Спотлайта...), но не работает в Карбон-приложениях (Айтюнс, Файндер, весь Адоби). С одной стороны, в Снежном барсе все родные приложения должны будут перейти на Какао. С другой, нельзя исключать, что поддержку инпутменеджеров прибьют.
- Работает только в стандартных текстовых полях. Например, в поле комментариев в браузере работать не станет. В поле написания письма в мейле тоже не заработает, так как там тоже используется какой-то контрол на базе WebView (потому что там можно писать ХТМЛ-письма и вставлять туда атачи).
- Использует таймер, чтобы не пропустить момент, когда человек переходит из одного текстового поля в другое (ловить activeConversationChanged:toNewConversation: у меня пока не получается).
- Не исключено, что он где-нибудь «течёт» (берёт память и не освобождает потом) — я не очень силён в таком хардкорном программировании, поэтому мог упустить что-то. Учитывая, что он выполняется в чужих приложениях, а не сам по себе, масштаб бедствия (если оно есть) существенно увеличивается. Ну, а если он течёт в том коде, который выполняется по таймеру, то всё вообще очень плохо.
- Он тупо красит курсор в красный цвет, если среди поддерживаемых текущей раскладкой языков не встречается английский, то есть никакой настройки раскладок/цветов нет.
- Скачать архив.
- Создать в /Library/InputManagers (именно в корне!) папку Lipstick
- Кинуть туда оба файла (инфо и бандл) из архива.
- Сказать терминалу (ввести пароль при необходимости):
sudo chmod -R 555 /Library/InputManagers
В каждом Какао-приложении он заработает после его перезапуска (перезапускать систему, по крайней мере, Леопард, не нужно). Спотлайт в правом верхнем углу можно перезапустить так:
killall Spotlight
Картинка:

Если какое-нибудь приложение начнёт тормозить или падать в неположенном месте, вполне может быть, что это из-за Липстика. Чтобы его выключить, просто уберите Lipstick из папки /Library/InputManagers и перезапустите соответствующее приложение.
Всё, что вы делаете, вы делаете на свой страх и риск, за возможный взрыв вашего компьютера я не несу ответственности. Но — с интересом послушаю ваш рассказ об этом.
Какао-наблюдения
25 апреля 2009, 14:37
Вот понадобилось мне на днях изменить шрифт в текстовом поле. Код угадал, не глядя в документацию (автозаполнение помогло, конечно); заработало с первого раза:
[textView setFont:[NSFont fontWithName:@"Arial" size:20]];
Что касается setFont:, то я просто предположил, что такой метод может быть, начал писать set..., и выбрал из списка setFont:, у которого единственный параметр — экземпляр класса NSFont. Как сделать нужный NSFont — угадывается по аналогии: очень часто названия конструкторов начинаются с типа создаваемого объекта, например у NSString есть конструктор stringWithContentsOfFile:encoding:error:, который возвращает содержимое файла; у NSArray есть конструктор arrayWithObjects:, который возвращает массив с переданным набором объектов; а у NSColor есть конструктор colorWithCalibratedRed:green:blue:alpha:, который, как нетрудно догадаться, возвращает цвет с заданными R, G, B и непрозрачностью с учётом каких-то цветовых профилей.
С другой стороны, эпловская терминология местами разрывает мозг. Иногда невозможно по три часа найти что-нибудь в документации просто потому, что ты ищешь, например, keyboard layout, а надо искать text input source.
Биты, Болк и Уникод
15 апреля 2009, 14:06
Если вы читаете по-английски, я рекомедую всё же Сполского, у него понятнее и последовательнее. Главное, что Джоел обращает внимание на разницу между таблицей символов (то есть тем, какому номеру соответствует какой символ) и кодировкой (то есть тем, как именно эти номера упаковываются в байты). Это позволяет сразу понять, что не бывает кодировки «Уникод», что это как «Эйч-ди» — просто маркетинговый термин, значение которого нужно выяснять в каждом случае заново.
Кстати, слова байт и бит в родительном (не счётном) падеже множественного числа будут: байтов, битов. Ещё Болк ошибается, говоря, что в одном байте можно уложить числа до 256 (на самом деле до 255); в двух — до 65536 (на самом деле до 65535); что информации меньше бита не бывает (на самом деле бывает).
Добавлено в 15:41: Ну вот, щас Болк все косяки исправит и моя заметка потеряет смысл.
Стенфорд на Айтюнсе
14 апреля 2009, 2:03
С 1 апреля начат новый курс CS193P — разработка приложений для Айфона. Курс читают разработчики из Эпла. Уже прошло три лекции длительностью чуть больше часа каждая, их можно скачать. Я посмотрел только первую, и, как оказалось, то, что показывается в ней, я уже умею и так. Но дальше, думаю, будет веселее. Курс находится по запросу «CS193P».
Бесплатное образование — это невероятно круто. Независимо от круга ваших интересов вы наверняка подыщете себе что-нибудь по вкусу. Системные требование — знание английского и более-менее свободный трафик.
В комментариях предлагаю делиться своими находками.
Эмси будущего
30 марта 2009, 23:13
Во-первых, я сделал шрифт мельче. Когда пунктом меню выступал просто текст в виде NSString, он рисовался стандартным шрифтом для пунктов меню. Я думал, что изменить его я смогу только, если буду использовать кустомный вью (сейчас-то я уже думаю, что, наверное, можно было просто NSAttributedString использовать в качестве тайтла). Короче, теперь у меня используется более мелкий шрифт — из стандартных вещей такой применяется, например, в индикаторе уровня заряда батарейки на ноутбуках.

Во-вторых, у меня теперь видно, какая часть трека уже проигралась. Я хочу сделать так, чтобы в эту штуку можно было тыкать, чтобы сразу прыгать в нужное место песни. Ещё есть гипотеза, что круче будет показывать, наоборот, сколько осталось до конца. Эта хрень использует Кор-анимейшен и плавненько переезжает в нужное место, если в Айтюнсе перемотать что-нибудь куда-нибудь резко.
Во время паузы вся эта штука красиво зачёрнобеливается.
Кстати, синий градиент — это просто синий прямоугольник высотой в несколько пикселей, которому с помощью Кор-имиджа был сделан вертикальный моушен-блюр ещё на несколько пикселей.
К сожалению, чтобы показывать прогресс мне приходится снова по таймеру каждую секунду спрашивать Айтюнс о ситуации, что заметно грузит процессор. Само по себе время идёт со вполне предсказуемой скоростью, поэтому мне было бы достаточно знать только о моментах, когда пользователь перематывает что-то, а всё остальное время просто пририсовывать раз в секунду по секунде. Но, к сожалению, Айтюнс не уведомляет систему о перемотке, только о паузе, воспроизведении или смене песни. Если у вас есть идеи относительно того, как отлавливать момент перемотки, я был бы очень рад совету — можно было бы реализовать всё намного чище.
Если кому интересно — пишите, пришлю последний билд посмотреть. Он глючит, иногда вдруг перестаёт рисовать синюю фигню, приходится перезапускать.
