программирование

Запуск сайтов из Spotlight

Занялся потихоньку автоматизацией своих задач на «Маке».

Очень хотелось сделать так, чтобы по короткому имени, набираемому в Spotlight, открывались какие-то сайты. В интранете «Яндекса» очень много сайтов, которые я открываю десятки раз за день, вот я и подумал — было бы круто, если бы я набирал в Spotlight «wiki», нажимал Enter и мне открывался бы сайт wiki.yandex-team.ru.

Я знаю, что тут много ребят сидят на Windows, поэтому поясню. Spotlight (его аналог) есть на Windows 7 — это поисковая строка, куда можно вбить начальные буквы какого-то названия, а операционная система предложит к запуску список программ и/или файлов, имена или метаинформация которых наилучшим образом подходит под заданную пользователем строку.

В Mac OS X удобно, что это поисковое поле появляется по кнопке Ctrl+Пробел.

Написать плагин к Spotlight мне пока слабо́, а вот набросать на AppleScript программу, открывающую одноимённый сайт — пожалуйста.

Идея такова: кладу на диск одну и ту же программу в нескольких копиях, с разными именами — «wiki», «jira», «calendar» и так далее. Теперь их можно запускать откуда угодно, включая Spotlight. После запуска программа смотрит какое у неё имя, прибавляет к нему префикс/суффикс и проверяет существует ли получившийся домен, если да, то открывает его в браузере.

Программу надо сохранить при помощи редактора AppleScript (AppleScript Editor) с нужным именем (↑⌘S), формат файла должен быть «программа» («application»).

Вот сама программа (наверное маску «%.yandex-team.ru» нужно будет убрать, она вам будет мешать, если вы только не работаете в «Яндексе»):

-- Ищет по маскам хост, подставляя имя программы,
-- если найден, открывает его в браузере
-- bolknote.ru апрель 2011

tell application "System Events"
    set appname to the name of the current application
end tell

repeat with mask in {"%.yandex-team.ru", "www.%.ru", "www.%.com"}
    set AppleScript's text item delimiters to "%"
    set mask to text items of mask
    set AppleScript's text item delimiters to appname
    set hostname to "" & mask
    
    if resolve(hostname) then
        open location "http://" & hostname
        exit repeat
    end if
end repeat

on resolve(host)
    try
        do shell script "host '" & host & "'"
        return true
    on error
        return false
    end try
end resolve

Это пока мой первый опыт программирования в AppleScript, так что, возможно, какие-то вещи можно было сделать проще, напишите я поправлю.

Добавлено позднее: в комментариях один из читателей предложил более простой и изящный вариант. Ура!

41 комментарий

Вопрос про iPod Touch G1

Кто знает, подскажите, пожалуйста.

У подруги iPod Touch первого поколения. Когда был куплен (с рук, б/у), там стояла какая-то достаточно старая прошивка и он был джейбрейкнут.

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

Теперь вопрос — это ли способ, не делая джейлбрейк, ставить приложения на это устройство? Если нет, то чем сейчас модно делать джейлбрейк iPod’ов? Я уже попробовал ZiPhone, не получилось, выдаёт какую-то невразумительную ошибку, что какой-то файл не найден.

7 комментариев

Почему я использую «Семажик», чтобы постить в блог

По-моему, это одна из самых забавных тем, которые родились в процессе обсуждения моего перехода на «Мак» — почему я не перейду с «Семажика» на постинг… через что, друзья мои?

Когда я писал движок своего блога (а я его именно писал, а не взял готовый, мне было интересно попробовать кое-какие новые идеи), один из принципов, которыми я руководствовался, был «используй готовое где только можно». Клиент для ЖЖ тогда имелся для любой платформы и его использование полностью освобождало меня от написания админки для сайта.

Я просто реализовал протокол ЖЖ (на деле даже два — Flat и Metaweblog) и несколько лет, не зная горя, писал в блог даже с телефонов, где нет ничего, кроме J2ME.

Так что «выкинь „Семажик“ и пиши через веб» — это совет от незнания, чтобы постить через веб, нужно обеспечить такую возможность, а у меня нет времени и желания этим заниматься, мне просто не интересно.

Так что я просто потратил немного времени и нашёл себе клиент, который меня устраивает — это Phoenix. Он не такой удобный, как «Семажик», но, в целом, неплох.

Добавлено позже: не знаю как так вышло, но почему-то этот сран… клиент, который меня устраивает, этот Phoenix удалил мне запись вместе с комментариями.

8 комментариев

IE 10 Platform Preview 1

Microsoft уже предлагает скачать Internet Explorer 10 Platform Preview 1.

Помнится, многие рассуждали о том, что MS опять забросит свой браузер на годы. Как видите, нет, только вышел IE9, как уже можно скачать превью следующей версии.

31 комментарий

Размер табуляции в CSS

В CSS3 есть хорошее свойство tab-size, оно контролирует размер, в пробелах, отображаемой табуляции. Оно пока в стадии черновика; например, неизвестно будет ли введена новая единица измерения (скажем «sp») и можно ли будет указать размер в процентах, пикселях и т. п.

Тем временем, свойство уже реализовано в некоторых браузерах, с собственным префиксом. Свойство первым поддержала «Опера» (с версии 10.60, именно от этой компании и было выдвинуто предложение добавить это свойство в CSS3), затем оно было реализовано в четвёртой версии FireFox. Остальные браузеры пока не подтянулись.

Вот как выглядит CSS-код:

body {
    -o-tab-size: 4; /* размер табуляции — 4 (для Оперы 10.60+) */
    -moz-tab-size: 4; /* размер табуляции — 4 (для FireFox 4+) */
    -webkit-tab-size: 4; /* для будущих версий Safari и Chrome */
     tab-size: 4; /* размер табуляции — 4, на будущее */
}

Значение по-умолчанию для этого свойства — 8 (пробелов), так же можно указать специальное значение «inherit», в этом случае значение будет унаследовано от родительского элемента.

Добавлено позднее: для WebKit уже есть патч для поддержки свойства «-webkit-tab-size».

16 комментариев

Uptime в Windows (на CMD)

Я заморочился и написал uptime на чистом CMD, раз уж на PowerShell уже до меня сделали. CMD — ужасный язык программирования!

Это я после Турции до программирования дорвался.

Посмотреть что получилось.

@ECHO OFF
REM Евгений Степанищев //bolknote.ru 2011/04/09

REM Здесь CALL нельзя использовать, иначе хилый CMD не выдержит и лопнет —
REM подстановки переменных через %var% и !var! хватать перестанет
FOR /F "usebackq tokens=3" %%i ^
IN (`net stats srv ^| findstr /R "[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]*"`) DO SET DATE0=%%i

SET YEAR0=%DATE0:~6%
SET MONTH0=%DATE0:~3,2%
SET DAY0=%DATE0:~0,2%

SET YEAR1=%DATE:~6%
SET MONTH1=%DATE:~3,2%
SET DAY1=%DATE:~0,2%

REM Избавляемся от ведущих нулей, иначе CMD будет считать
REM числа восьмеричными
IF %DAY0:~0,1% == 0 SET DAY0=%DAY0:~1%
IF %MONTH0:~0,1% == 0 SET MONTH0=%MONTH0:~1%
IF %DAY1:~0,1% == 0 SET DAY1=%DAY1:~1%
IF %MONTH1:~0,1% == 0 SET MONTH1=%MONTH1:~1%

SET /A SUM=%DAY1%-%DAY0%
SETLOCAL ENABLEDELAYEDEXPANSION

IF %YEAR1% == %YEAR0% (
    SET /A END=%MONTH1%-1

    FOR /L %%M IN (%MONTH0%,1,!END!) DO (
        CALL :getMonthDays %%M %YEAR0%
        SET /A SUM+=!DAYS!
    )
) ELSE (
    REM 02.2010 -> +10 months
    FOR /L %%M IN (%MONTH0%,1,12) DO (
        CALL :getMonthDays %%M %YEAR0%
        SET /A SUM+=!DAYS!
    )

    REM 02.2011 -> +2 months
    FOR /L %%M IN (1,1,%MONTH1%) DO (
        CALL :getMonthDays %%M %YEAR0%
        SET /A SUM+=!DAYS!
    )

    REM Full years
    SET /A START=%YEAR0%+1
    SET /A STOP=%YEAR1%-1

    FOR /L %%Y IN (!START!,1,!STOP!) DO (
        CALL :isLeapYear %%Y
        IF !FEB! == 29 (
            SET /A SUM+=366
        ) ELSE (
            SET /A SUM+=365
        )
    )
)

ECHO %SUM%

GOTO :EOF

REM ====== Функции ==========

REM Високосный ли год?
REM isLeapYear <год>
:isLeapYear
SET /A FEB=(%1/400)*400

IF %FEB% == %1 (
    SET FEB=29
    GOTO :EOF
)

SET /A FEB=(%1/100)*100

IF %FEB% == %1 (
    SET FEB=28
    GOTO :EOF
)

SET /A FEB=(%1/4)*4

IF %FEB% == %1 (
    SET FEB=29
    GOTO :EOF
)

SET FEB=28
GOTO :EOF

REM Количество дней в месяце
REM getMonthDays <месяц> <год>

:getMonthDays
CALL :isLeapYear %2

IF %1 == 2 (
    SET DAYS=%FEB%
    GOTO :EOF
)

IF /I %1 LSS 8 (
    SET DAYS=30
    SET /A DAYS+="%1&1"
) ELSE (
    SET DAYS=31
    SET /A DAYS-="%1&1"
)

GOTO :EOF

14 комментариев

Uptime в Windows (на Powershell)

Написал на PowerShell скромный аналог утилиты uptime из Линукса (эта утилита показывает сколько дней компьютер работает без перезагрузки):

[math]::round(((Get-Date) - ($os=gwmi Win32_OperatingSystem).ConvertToDateTime($os.lastbootuptime)).TotalDays, 2)

В основном в сети предлагается запустить «net stats srv» и посмотреть строку «статистика после» («statistics since»), впрочем и вариантов на PowerShell тоже хватает. Кстати, можно сделать ещё и такой вариант:

[math]::round(((Get-Date) - (((net stats srv)[3] -split ' ')[2,3] -join ' ' | Get-Date)).TotalDays, 2)

Почти LISP. И всё-таки мне очень нравится PowerShell.

13 комментариев

Алиас для /etc/init.d/… restart

Устал я писать каждый раз строки вида «sudo /etc/init.d/nginx restart» для перезапуска многочисленных серверов, поэтому сделал себе короткий алиас. Я бы не упомянул об этом, если бы не два отличия.

Во-первых, алиас я сделал не через команду «alias», так как не знаю как для неё подставить параметр не в конец, а в произвольное место (если это вообще возможно), во-вторых, я сделал добавление для автодополнения в bash (если кто-нибудь знает ссылку на хороший мануал по этой теме, напишите в комментариях, пожалуйста).

Вот что получилось (у меня это в .bashrc):

rst () { command sudo /etc/init.d/$@ restart; }

function _rst() {
    local cur

    cur=${COMP_WORDS[COMP_CWORD]}
    if [ 1 -eq ${COMP_CWORD} ]; then
        COMPREPLY=( $(compgen -f -- /etc/init.d/$cur | xargs -n1 basename -- ) )
    else
        COMPREPLY=()
    fi
}

complete -F _rst rst

16 комментариев

Магия гитхаба

Прочувствовал магию «Гитхаба». Я сейчас дописываю один плагин для phpBB (это популярный движок форумов), в процессе написания нашёл ошибку в плагине automod (плагин, который нужен чтобы ставить другие плагины).

Поскольку automod выложен в git-репозитории на github, всё что нужно — зарегистрироваться, нажать кнопку «fork», склонировать репозиторий себе на машину, исправить ошибки, сделать commit/push и отослать pull request.

Авторы automod, когда увидят мой запрос, сделают merge с моей новой веткой и мои изменения помогут сделать мир лучше.

Очень просто и круто.

7 комментариев

«Парсер» студии Лебедева и nginx

Смог запустить Парсер на nginx. Поставил FastCGI-сервер SimpleCGI, он принимает на вход FastCGI-запрос и запускает по нему CGI-программу. Написан на Перле, который я хорошо знаю, поэтому мне сразу видно как его можно настроить. Настройки nginx получились следующие:

server {
        listen 80;

        server_name  parser.internal;
        set $root /var/www/parser;
        root $root;

        location ~ \.(p|cgi)$ {
            include fastcgi_params;
            fastcgi_pass unix:/var/www/cgiwrap-dispatch.sock;
            fastcgi_param  SCRIPT_NAME  $fastcgi_script_name;
            fastcgi_param  PATH_INFO    $fastcgi_script_name;
            fastcgi_param  PATH_TRANSLATED $root$fastcgi_script_name;
            fastcgi_param  SCRIPT_FILENAME  $root$fastcgi_script_name;
        }
}

SimpleCGI после запуска создаёт файловый сокет «cgiwrap-dispatch.sock» к которому и обращается nginx.

Причём, что интересно, с SimpleCGI спокойно работает и Парсер из пакета, на который я жаловался, что он падает из-под thttpd.

Добавлено позже: если нужно указать путь до auto.p, то можно добавить «fastcgi_param CGI_PARSER_CONFIG путь/до/auto.p».

2 комментария

«Парсер» студии Лебедева и thttpd

Сегодня потратил часть дня, чтобы выяснить, что Парсер не заработает с thttpd.

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

Что-то у меня с nginx в последнее время любовь как-то не складывается.

У меня Ubuntu 10.04, поэтому я всё поставил из пакетов, Парсер в том числе, пакет неофициальный, я его нашёл в разделе прислали пользователи.

После того как я всё настроил, ничего не заработало. Отладка через strace быстро показала, что Парсер падает. Я его перекомпилировал. Если будете перекомпилировать, вот мой рецепт.

Я поставил следующие пакеты (легко допускаю, что где-то поставил лишнего, так как ориентировался по названиям): libgmime2.1-dev libgmime-2.4-2 glibc-2.10-1 libgc1c2 bison.

В каталог /opt/pcre-8.12 я положил свежую версию PCRE, в /opt/pcre лежит скомпилированная версия. Строка компиляции получилась вот такая:

CPPFLAGS='-I/opt/pcre-8.12 -I/opt/pcre/include/' \
./configure --prefix=/opt/parser3/ --with-shared-gc --with-static-pcre=/opt/pcre
make
sudo make install

После того как я стал пользоваться собранным мною Парсером, что-то в браузер я получил, но всё было не то. Причём при запуске из консоли, я получал на экран результат работы скрипта.

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

Я создал небольшой shell-скрипт, в который накидал разных переменных окружения, необходимых по стандарту для CGI и подбором выяснил, что для Парсера критично наличие переменных GATEWAY_INTERFACE, SCRIPT_NAME, PATH_INFO и PATH_TRANSLATED. Вот небольшой пример:

if(cgi) {
                // few absolute obligatory
                const char* path_info=getenv("PATH_INFO");
                if(!path_info)
                        SAPI::die("CGI: illegal call (missing PATH_INFO)");
                const char* script_name=getenv("SCRIPT_NAME");
                if(!script_name)
                                SAPI::die("CGI: illegal call (missing SCRIPT_NAME)");
                // … ещё много кода …
        }

При помощи CGI, написанного на bash, я выгрузил все переменные окружения, которые предоставляет thttpd (у меня версия 2.25b), оказалось, что двух последних переменных в дампе нет. Причём в интернете эта проблема уже поднималась (правда без связи с Парсером) и была успешна разрешена авторами thttpd. Наверное в последней версии что-то сломали.

Добавлено: я что-то явно делаю не так. Я попробовал ещё сервер mini-httpd, там в changelog есть такая фраза: «Added PATH_INFO to CGI environment», но я опять же не вижу эту переменную в окружении. В исходниках так же упоминаются обе переменных «PATH_…».

Добавлено ещё позже: так, по коду выходит следующее: если mini-httpd или thttpd находит файл по URL на файловой системе сразу, то PATH-переменные не заполняется, но заполняется SCRIPT_NAME, если не находит, то алгоритм отрезает путь до первого слеша и кладёт орезанное в PATH_INFO, формируя PATH_TRANSLATED из этого пути, присоединяя к нему рабочий каталог.

При этом куда-то пропадает SCRIPT_NAME, в коде я ещё не нашёл куда и почему. Но, похоже, для Парсера mini-httpd и thttpd (у них один автор) не подойдут.

Чтобы понимать:

http://example.org/cgi-bin/test.cgi — формируется SCRIPT_NAME, но не PATH_INFO и PATH_TRANSLATED
http://example.org/cgi-bin/test.cgi/ — формируется PATH_INFO и PATH_TRANSLATED, но не SCRIPT_NAME

И ещё позднее: попробовал веб-серверы Boa, Bozohttpd и Cherokee. Парсер с ними не заработал, причина та же — нехватает переменных. Очень похоже, что Парсер сильно неправ в своей трактовке протокола CGI.

4 комментария

WebDAV

Пытаюсь настроить WebDAV, чтобы им можно было пользоваться на nginx. Пока не получилось. Модуль WebDAV из nginx не умеет обрабатывать OPTIONS и PROPFIND, из-за этого клиенты не видят листинг директории. Решение простое — дать nginx обрабатывать то, что он умеет, а OPTIONS и PROPFIND отпроксировать куда-то ещё.

Для этого я взял davserver — небольшой сервер, написанный на Пайтоне. У него два достоинства: он на Пайтоне и он есть в Debian-пакетах.

Выяснилось, что это плохая идея.

Во-первых, на PROPFIND что-то отдаётся, но это что-то клиент не понимает.

Во-вторых, оказалось, что когда формируется листинг PROPFIND, в качестве базового URL там используется имя хоста из параметров запуска (там у меня localhost), а не из заголовка «host» запроса. Это я починил, поправив код.

В-третьих (это выяснилось, когда я полез в исходники), сервер писали какие-то дикие люди и он мне однозначно изнутри не понравился. Я это использовать не хочу. Например, вот так они создают директорию:

try:
            os.system("mkdir '%s'" % path)
            self._log('mkcol: Created new collection %s' % path)
            return 201

Кажется, они даже не заморачиваются экранированием спецсимволов в path, правда я бегло смотрел.

Пока не решил возьму ли что-то другое (хотелось что-нибудь небольшое, а не Apache) или буду отказываться от идеи настроить WebDAV вообще.

18 комментариев

Чудеса с Гугл.Ридером

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

Добавлено позднее: неожиданно пришли комментарии. Правда не все. Видимо, Гугл лихорадит.

4 комментария

Перепрошил Intel WiFi 5100 ABG → ABN

Этот набор букв в заголовке означает, что благополучно перешил свой WiFi-адаптер в ноутбуке и теперь он поддерживает категорию «n» (категория «g», конечно, тоже поддерживается). Спасибо читателю Mixa за ссылку.

Скриншот окна «Диспетчер устройств» Windows с раскрытым разделом «Сетевые адаптеры», где видны Intel(R) 82567LM Gigabit Network Connection и Intel(R) WiFi Link 5100 AGN.
ABN

Если вы хотите повторить мой путь, то учтите, что можно легко запортить карту, а восстановление испорченной карты дело непростое. Итак, если вы решились (я перепрошивал из-под Windows 7), то помните — вы всё делаете на свой страх и риск.

Для начала вам потребуется последняя версия программы RW-Everything.

Шаг второй — нужно выяснить адрес вашей карты. Для этого откройте диспетчер устройств и найдите там ресурсы вашей карты, как на скриншоте:

Свойства устройства Intel(R) WiFi Link 5100 ABG: вкладка «Дополнительно», раздел «Параметры ресурсов» — диапазон памяти 00000000F69FE000–00000000F69FFFFFF, IRQ 4.
Ресурсы карты

Как видно, адрес моей карты — F69FE000. Убедитесь, что правильно копируете это число. Это важно.

Скачайте скрипт, который поможет вам сдампить текущие данные вашей карты, во второй строчке этого скрипта замените «########» на адрес вашей карты.

Запустите RW-Everything, выберите в меню «Command» (Access → Command), нажмите F3, выберите скаченный файл и дождитесь выполнения скрипта. В конце его работы в корне диска «C» у вас запишется файл RWLOG.TXT. Не удаляйте его, если испортите карту, он вам пригодится (я описывать процесс восстановления не буду).

Скачиваете скрипт, который пропатчит вашу карту, опять же, вместо «########» нужно написать адрес вашей карты.

Снова открываете в меню «Command», в длинное текстовое поле копируете скрипт (с заменённым адресом), нажимаете «Enter», когда скрипт завершит свою работу (курсор станет обычным, а в окне под текстовым полем появится лог работы скрипта), перезагружаете компьютер.

Молитесь. В нормальном случае, Windows скажет, что обнаружено новое устройство и поставит драйвер для карты Intel 5100 ABN. Если карта пропала, то, увы, что-то пошло не так. Попробуйте прочитать форум на IXBT, посвящённый этой теме и восстановить карту.

Добавлено: тут надо пояснить почему вообще есть возможность перепрошить этот адаптер на категорию «n». Всё дело в том, что он уже поддерживает эту категорию, и на уровне железа, и на уровне прошивки. Просто эта возможность там выключена. Как я понял из-за того, что эти адаптеры были сделаны ещё до того как в России были выделены частоты для этой категории.

169 комментариев

Как посмотреть какие каналы WiFi используются

Скриншот программы inSSIDer 2.0 Mini: таблица Wi-Fi сетей с SSID, RSSI, каналом, типом шифрования и скоростью.
inSSIDer

У Паши Катурова увидел хорошую программу под Windows, называется inSSIDer 2 (есть и под Linux, кстати), позволяет посмотреть какой канал Wi-Fi наименее загружен вашими соседями.

Если упрощённо, чем больше Wi-Fi точек на канале (чем больше точек делят одну частоту), тем больше они друг другу мешают, тем ниже скорость. Так что надо выбирать канал наименее нагруженный, в идеале — свободный.

Моя точка (по уровню сигнала легко понять, что я её назвал «WiMAX») работала на каналах №№1 и 5. На первом висят уже несколько точек, поэтому я переместил свою точку на третий и седьмой каналы (стоит походить по квартире, в разных участках могут быть видны различные точки).

Кстати, легко видеть, что трое моих соседей используют шифрование WEP, которое взламывается минут за 10—15 и ни одна из точек (кроме моей) не поддерживает полноскоростную категорию «n».

Добавлено: в комментариях (спасибо!) напомнили о проблеме, которую я уже успел начисто забыть: каналы WiFi перекрываются. Есть хорошая картинка, показывающая как именно они перекрыты, заодно она объясняет почему все мои соседи выбрали канал 1, 6 или 11. Руководствуясь этими данными, я выключил второй канал (всё равно устройств «n» у меня пока нет) и переставил свою точку на 13.

На закладке «2.4 GHz Channels» (это частота, на которой работают устройства категорий «b», «g» и «n»), можно визуально оценить как ваша точка использует частоты и где располагаются точки соседей. У меня сейчас картина такая:

График амплитуды Wi-Fi каналов: Akado_24, HomeNet (7997444), ak_88_10, akavita_11, WiMAX.
Распределение по частотам

Как видно я занимаю канал №13, который частично перекрывается с каналами 8—12, уровень сигнала моей точки у меня в квартире заметно выше соседских, кроме того, половина моего канала никем, кроме меня не занята.

При густой «населённости» правило, видимо, такое: старайтесь не делить канал с точками, которые приближаются к вашей по уровню сигнала.

Правда я ещё не до конца уверен правильно ли я сделал, что отключил второй канал у точки. Надо читать. Насколько я понимаю, мои устройства (а у меня их адово количество — 8 штук) могли раньше коннектиться по любому из двух каналов, чтобы меньше друг другу мешать.

33 комментария

Одно из различий Python и JavaScript

Чем дальше, тем между большим количеством языков программирования мне приходится переключаться. Одно из различий объектной модели Пайтона и ДжаваСкрипта мне портит кровь (у них парадигмы ООП вообще сильно различаются), если я вовремя не соображу, что надо переключиться с одного языка на другой.

Суть в том, что в Пайтоне метод ходит в гости со своим контекстом:

class A:
    test = 'A'
    def show(self): print self.test

class B: test = 'B'

a, b = A(), B()

show = a.show
b.show = a.show

show() # A
b.show() # A

Тогда как в JavaScript такой код будет работать совершенно иначе, там методы в чужой монастырь со своим уставом не ходят (если их не попросить):

var test = 'global';

A = function () {
    this.test = 'A',
    this.show = function () { alert(this.test) }
};

B = function () { this.test = 'B'; }

a = new A(); b = new B();

b.show =  a.show
show = a.show

show() // global
b.show() // B
b.show.apply(a) // A

Появление «global» наверное надо разъяснить тем, кто JS знает плохо. На самом деле первая строчка (в данном случае) может читаться как «window.test = ’global’», а «show = a.show» как «window.show = a.show», тогда, должно быть, всё более-менее встаёт на свои места.

10 комментариев

Изменения в speeddial новой альфы «Оперы»

Ребята из «Оперы» экспериментируют с возможностями SpeedDial в своём браузере. Помните, я писал о специальных версиях некоторых сервисов «Яндекса» для SpeedDial?

К сожалению, «Опера» их разломала в свежей альфе, зато добавила новую возможность, которой очень нехватало: специальным заголовком или мета-тегом можно указать время обновления страницы в speeddial.

Preview-refresh: 10

или

<meta http-equiv="preview-refresh" content="10" />

Время указывается в секундах. Кстати, теперь можно надёжно узнать через media queries, что «Опера» показывает страницу в SpeedDial: достаточно проверить, что «view-mode» в значении «minimized»; помимо этого есть возможность указать произвольное изображения для миниатюры (об этом лучше почитать подробнее в блоге «Оперы»).

7 комментариев

Сколько есть способов восстановить chmod?

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

Я решил подумать сколько способов исправить эту ситуацию я знаю.

Первый способ — скопировать chmod с соседней машины при помощи scp, sshfs и т. п. Если такая машина имеется, конечно.

Способ второй:

cp /bin/sh ~/chmod2
cat /bin/chmod > ~/chmod2
sudo ~/chmod2 +x /bin/chmod
rm ~/chmod2

Третий способ (при условии, что у вас какой-то дистрибутив, использующий пакеты, у меня способ для Debian):

sudo apt-get --reinstall install `dpkg -S /bin/chmod | cut -f1 -d:`

Способ четвёртый:

gcc -x c - <<CHMOD
#include <sys/stat.h>
main(){chmod("/bin/chmod", S_IXUSR | S_IXGRP | S_IXOTH);}
CHMOD

sudo ./a.out
rm a.out

Кто ещё знает способы?

28 комментариев

Пайтон: замкнувшее замыкание

Есть немало вещей, которые мне не нравятся в Пайтоне. Не так давно мы на работе наткнулись ещё на одну.

У нас есть код, который выглядит примерно так (можно написать и одну функцию, но так нагляднее):

def outer(useless = None):
    def inner():
        if useless is None:
            print "Pichal'ka"

    return inner

outer()()

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

Теперь меняем код:

def outer(useless = None):
    def inner():
        if useless is None:
            print "Pichal'ka"

        # тут много кода, начало мы уже потеряли
        if False: useless = None

    return inner

outer()()

Запускаем код и видим (Пайтон 2.6.5):

Traceback (most recent call last):
  File "sample.py", line 10, in <module>
    outer()()
  File "sample.py", line 3, in inner
    if useless is None:
UnboundLocalError: local variable 'useless' referenced before assignment

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

Избавиться от эффекта можно, изменив прототип внутренней функции на «inner(useless=useless)».

То же, кстати, происходит при импорте модуля:

import random

def outer():
    print random.choice
    #import random

outer()

Раскомментируйте строчку со вторым импортом и получите ошибку.

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

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

42 комментария

Кто в офисе?

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

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

# Кто сегодня в офисе?

# private data vars
WAPIHOST='██████.██████-████.██'
SAPIHOST='█████.██████.██'
SAPI='/█████/██████.xhtml?██████████=█&█████_██=██'

# non-private data vars
WAPI='/where/%s'
NOW=$(date +%s)

function whereAreYou {
    exec 3<>/dev/tcp/$WAPIHOST/80
    echo -e "GET ${WAPI/\%s/$1} HTTP/1.0\nHost: $WAPIHOST\n\n" >&3

    local DATA=$(grep -E '^.?$' -A1 <&3)

    if [[ ! $DATA =~ 'VPN' && $DATA =~ 'updated_at' ]]; then
        local date=$(echo $DATA | sed -E 's/^.*updated_at"[^"]+"([^"]+).+/\1/' | xargs -Io date -d'o' +%s)

        if [ $(($NOW-$date)) -le 3600 ]; then
            echo "$1 - $(date -d@$date +%k:%M)"
        fi
    fi
}

exec 4<>/dev/tcp/$SAPIHOST/80
echo -e "GET $SAPI HTTP/1.0\nHost: $SAPIHOST\n\n" >&4

while read -r line <&4; do
    nick=$(echo $line | egrep -o "href='http://$SAPIHOST/\w+" | cut -b$((15+${#SAPIHOST}))-)

    [ -n "$nick" ] && whereAreYou $nick
done

12 комментариев

← 1 41 42 43 44 45 67 →