27.12.2009Итого
- Это был крутой год!
- В этом году я стал гораздо ближе с собой (к себе, ко мне :)
- В этом году я ощутил кайф от взаимодействия с живыми (неидеальными) людьми.
- В этом году я потешил свои тщеславие и злобность настолько, насколько хотел.
- В этом году я перестал искать идеальную работу и нашёл любимую. Почувствовал себя программистом комфортно.
- В этом году я увидел свои границы и возможности. Скорее всего, не все, но всё же.
Этот журнал я пытался делать тоже программерским, как и блог. Но я хочу здесь расширить рамки. Мне это кажется интересным.
Новый год буду встречать с родителями. Очень давно так не делал.
15.12.2009Одновременное использование двух версий руби на одной системе
Введение
Как молодой язык с неутверждённой спецификацией, руби переживает подростковую болезнь, через которую большинство известных языков уже прошли. Есть новая более быстрая версия, на которую уже стоит переходить, но уже много написано на предыдущей, и так боязно всё ломать...
Поэтому необходимо найти удобный для себя способ (а лучше несколько) чтобы начать использовать руби 1.9.
Постановка задачи
Сейчас практически панацеей для использования более одной версии руби является rvm. Очень удобно в использовании, полностью прозрачно, и позволяет иметь разные версии руби в разных окнах терминала.
Но недавно мне понадобилось скомпилировать wxRuby под свою систему (kubuntu 9.10 amd64), и rvm не справилась с этой задачей. По какой-то причине в момент компилляции были недоступны заголовки руби. Поэтому я решил поставить две версии руби более явно: одна системная (1.8.7) и одна в папке /opt (1.9.1). Причем все команды, связанные с руби 1.9 будут вызываться с суффиксом: ruby1.9, irb1.9, gem1.9, rake1.9.
Возможно, подобных инструкций уже полно, но мне будет удобнее, если я точно буду знать, где находится одна из них :) При всём этом, конечно, rvm продолжает работать. Мы никак ему не помешаем.
Решение
Сначала нужно поставить новый readline. Без него, когда мы будем использовать irb1.9, мы не сможем наслаждаться доступом к истории с помощью стрелок вверх-вниз и перемещаться по введенному тексту с помощью стрелок в стороны.
sudo apt-get install libreadline5-dev
Теперь хорошо бы вписать пути в наше окружение. В конце ~/.bashrc добавим:
export PATH=$PATH:/opt/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/lib
Теперь следует скачать и разархивировать последнюю версию руби. Зайдя в папку скомпилировать и установить:
./configure --prefix=/opt --enable-shared --program-suffix=1.9
make
sudo make install
Теперь у нас есть две отдельных установки руби и сопутствующих инструментов. Единственное, что обе установки используют общие конфигурационные файлы: ~/.gemrc, ~/.irbrc и т.п., что вполне удобно. Также для обеих систем общей директорией джемов будет ~/.gem, куда будут устанавливаться библиотеки, запусти мы их установку без sudo (в случае с sudo, конечно же, директории установки различаются).
Так же я не нашёл быстрого способа добавить /opt/bin в переменную PATH для sudo. Поэтому в таких случаях пока использую полный путь. Например, первая команда, которую следует выполнить:
sudo /opt/bin/gem1.9 update --system
Потому что в пакете с руби идёт версия 1.3.1, а настоящие пацаны уже во всю используют 1.3.5.
Материалы для самостоятельного изучения
15.12.2009Польза
Маша решила утилизировать использованные картриджи от hp LazerJet 1010 (1015, 1020 и подобное, такой, гробик). Их принимают минимум по 15 штук. У меня как раз завалялось два на такой случай, потому что это, видимо, самая популярная модель. Итого у нас три использованных лазерных картриджа. Нужно ещё 12. Присылайте ваши картриджи. Сохраним планету вместе! :)
Какое-то время назад поймал себя на мысли, что выбираю тему для очередной статьи в блоге по принципу «понравится ли она пытливому читателю». В результате чего заметно упала частота обновлений. При этом я заметил, что за некоторыми вещами, которые периодически использую, я лезу именно к себе в блог. И решил это усилить. Теперь я понаписал туда всего и хожу по работе почти каждый день. Так что я — свой самый регулярный читатель. Думаю в связи с этим перестать сюда постить ссылки на статьи, потому что множество здешних читателей и тамошних пересекается только в моём лице :)
09.12.2009Забота о неблокировании потоков в руби
Введение
Сегодня будет блиц-молния, которой место, скорее в твиттере. Однако, мне необходимо развеять тучи, которые я сам же и нагнал. :)
Постановка задачи
Не так давно я писал о том, что при создании руби-оболочки вокруг библиотеки на си, легко получить код, который блокирует все потоки приложения. Можно ли с этим что-то сделать?
Решение
Если мы имеем чуть больше контроля над участками кода на си, которые выполняются долго (обычно в цикле), то можно добавить спасительный код в тело цикла:
if (!rb_thread_alone()) rb_thread_schedule();
Это позволит каждый раз при очередном витке передавать управление соседним потокам, если они есть, и сделает приложение более отзывчивым.
То есть, если мы сделаем точную копию функции из той статьи и добавим наш спасительный код внутрь for, то в выводе такой программы:
# coding: utf-8
require "block_thread.so"
t1 = Thread.new do
10.times { |i| puts i; sleep 0.1 }
end
t2 = Thread.new do
puts "Блокируем"
BlockThread.cycle
puts "Разблокируем"
end
t3 = Thread.new do
puts "Стараемся не блокировать"
BlockThread.cycle_with_schedule
puts "Закончили стараться"
end
t1.join
t2.join
t3.join
Будет картина гораздо приятнее:
Блокируем
0
Стараемся не блокировать
Разблокируем
1
2
3
4
5
Закончили стараться
6
7
8
9
Заметьте, кстати, что два лишних вывода между «Блокируем» и «Разблокируем» благодаря передаче управления соседнимпотокам между cycle и puts.
Понятно, что соседние потоки тормозятся. Но теперь не на всё время, а только на исполнение одного витка цикла.
Материалы для самостоятельного изучения
01.12.2009Делаем quake-like консоль в любом DE на базе XServer (KDE, GNOME, XFCE, ...)
Недавно
Почесав репу, я написал свой способ организовать вывод консоли по F12, который подходит для любого Desktop Environment, работающего на «иксах», а значит почти любого.
- Устанавливаем три пакета: xdotool, xbindkeys, xbindkeys-config. На убунте это так:
sudo apt-get install xdotool xbindkeys xbindkeys-config
Выбираем свою консоль, которой мы хотим управлять по горячей клавише. И настраиваем для неё уникальное слово, которое будет выводиться в заголовке окна. Я выбрал konsole. И настроил так, чтобы в заголовке окна всегда было «qk : ». Этот пункт будет разным для всех :)
Сердце гайда — скрипт, использующий xdotool для управления нашим окном. Я как фанат руби, все скрипты на нём и пишу. Вот это лежит у меня в $HOME/.quake-console/quake-console:
#!/usr/bin/ruby
PROG_NAME = "konsole"
WIN_NAME = "qk : "
win_id = `xdotool search "#{WIN_NAME}" 2> /dev/null`.split("\n").select{ |l| l =~ /^\d+$/ }.first
if win_id.nil?
system "#{PROG_NAME} &"
else
if `xdotool search --onlyvisible "#{WIN_NAME}"`.include? win_id
if `xdotool getactivewindow`.chomp.strip == win_id
system "xdotool windowunmap #{win_id}"
else
system "xdotool windowraise #{win_id} && xdotool windowfocus #{win_id} && xdotool windowmove #{win_id} 0 0"
end
else
system "xdotool windowmap #{win_id} && xdotool windowfocus #{win_id} && xdotool windowmove #{win_id} 0 0"
end
end
Как видно, если окна с «qk : » в заголовке нет, то запускается моя выбранная консоль. Если оно есть, но не сверху и не в фокусе, то выводим его на верх и в фокус, если оно в фокусе, то прячем, а если спрятано, то показываем и выводим в фокус.
- Теперь запускаем xbindkeys, который как раз нужен для привязки горячих клавиш, и конфигурируем его:
xbindkeys --defaults > $HOME/.xbindkeysrc
xbindkeys
xbindkeys-conifg
Используя интуитивно-понятный графический интерфейс, настраиваем нужную нам клавишу F12 на запуск нашего скрипта, который мы предварительно сделали запускаемым.
- Наслаждение
19.11.2009Пломбир
Забыл сказать. На прошлой неделе написал про неравенства и сравнения в руби. Этот пост годится для прочтения даже начинающим изучать руби.
А сегодня написал про тему, на которую съел мозг как минимум трём человекам (Петров, Руди, Лёха), прежде чем четвёртый прислал мне решение, не зная, что я его ищу. Это очень специфичный пост для тех кто в теме про то, как сделать ссылку на правильную библиотеку.
А ещё я сделал жене пломбир!
Раз вы заглянули под кат, то скажу вот ещё что. Не так давно пересматривал передачу «гордонкихот», в которой Задорнов и Джигурда троллили Гордона и учёных дядек. Я неоднократно видел, как вспыхивали эмоциональные споры при упоминании этой передачи, подобные тем, что там показаны. Даже как-то наблюдал своих папу и маму за спором на повышенных тонах на эту тему.
Так вот, мне кажется, что основное эмоциональное зерно передачи заключено не в роли славянского языка в мировой лингвистике, а в том, достаточно ли того, что я просто есть, чтобы себя любить, или это надо как-то заслужить?
Это важный вопрос для любого уважающего себя программиста. Так то!
18.11.2009Сборка руби-билиотеки в заданной среде
Постановка задачи
Как разработчику gphoto4ruby мне приходится сталкиваться с особыми задачами. Связано это с тем, что этот gem является оболочкой поверх ещё одной библиотеки. И как у всякой более-менее развитой сторонней библиотеки, у libgphoto2 есть версия, распространяемая через системные репозитории и порты и есть, так сказать, последний писк моды (bleeding edge).
Отсюда вытекает необходимость:
- Иметь разные версии библиотеки не конфликтующие между собой, установленные не одной системе,
- Компилировать свою руби-библиотеку под любую из версий.
Установка двух gphoto2 :)
Проделаю весь путь с самого начала. Для пущей целостности. Для начала установка из системного репозитория:
sudo apt-get install libgphoto2-2-dev gphoto2
gphoto2 --version
Теперь можно скачать нужную версию и установить её отдельно. Поскольку я в основном использую две версии, то версию из исходников нужно установить в /opt. Предположим, что исходники libgphoto2 и gphoto2 скачаны:
tar zxvf libgphoto2-x.x.x.tar.gz
cd libgphoto2-x.x.x.tar.gz
./configure --prefix=/opt
make
sudo make install
tar zxvf gphoto2-x.x.x.tar.gz
cd gphoto2-x.x.x.tar.gz
./configure --prefix=/opt --with-libgphoto2=/opt
make
sudo make install
/opt/bin/gphoto2 --version
Теперь мы имеем две библиотеки и две утилиты командной строки, поставленные раздельно и правильно залинкованные. Каждая утилита командной строки знает, где искать свою библиотеку. Надо, чтобы это же умел и gem
Компиляция джема
Если скачать исходник библиотеки, то можно проделать руками то, что делает команда gem install. Для создания Makefile используется утилита mkmf, которая входит в ruby-dev и с которой работает файл extconf.rb. В моём случае последовательность действий установщика такая:
cd ext
ruby extconf.rb
make
Теперь в папке ext мы имеем скомпилированную библиотеку (*.so или *.bundle в зависимости от системы). Установщик потом копирует её в папку lib, но мы пока остановимся. Мы можем посмотреть, какие другие библиотеки использует эта:
ldd gphoto4ruby.so
По выводу этой команды видно, что используется библиотека установленная из центрального репозитория. Теперь попробуем скомпилировать под версию «по последней моде». Поскольку я написал в extconf.rb
dir_config("gphoto2")
То это означает, что пользователю будет доступен целый ряд опций, позволяющих сказать компилятору, где искать libgphoto2. Попробуем:
ruby extconf.rb --with-gphoto2-dir=/opt
make
ldd gphoto4ruby.so
Но что это? Вывод показывает нам, что библиотека привязалась опять к тому, что установлено из репозиториев, а не тому, что в /opt. То есть компилятор, конечно, находит нужные ему заголовки (*.h), но ничего в них не говорит о том, где искать соответствующие им библиотеки. Об этом ему должны сказать мы:
ruby extconf.rb --with-gphoto2-dir=/opt --with-dldflags="-Wl,-rpath,/opt/lib"
make
ldd gphoto4ruby.so
Вуаля!
Теперь, собственно, главное. Как это сделать при установке джема. Чтобы передать ключи для extconf нужно задать их после дополнительного «--»:
sudo gem i gphoto4ruby -- --with-gphoto2-dir=/opt --with-dldflags="-Wl,-rpath,/opt/lib"
Вот такой экскурс в жизнь разработчиков библиотек. Как это звучало в школьные времена: «Спэтсыално дла джэма».
Материалы для самостоятельного изучения
Руководство по расширению руби с помощью C (см. главу про extconf.rb)
11.11.2009Сравнения и неравенства в руби
Постановка задачи
Собрать в одном месте важные, на мой взгляд, особенности сравнений и неравенств в руби.
Основа неравенств в руби
Основным методом сравнения является <=>. Определив его, мы определяем все остальные операции, включив модуль Comparable:
class MyComp
attr :value
include Comparable
def initialize(val)
@value = val
end
def <=>(other)
@value <=> other.value
end
end
v1 = MyComp.new(1)
v2 = MyComp.new(2)
puts v1 < v2 # > true
puts v1 <= v2 # > true
puts v1 > v2 # > false
puts v1 >= v2 # > false
puts v1 == v2 # > false
Сам метод можно было бы описать как «возвращает -1, 0 или 1 в зависимости от того, меньше равен или больше объект, чей метод вызывается в сравнении с объектом переданным в качестве параметра». Но на самом деле, скорее, наоборот понятия «больше», «меньше» и «равен» определяются исходя из работы <=>.
Далее всё понятно и более ли менее очевидно для чисел, массивов и строк. Но есть и интересная особенность.
Сравнение модулей и классов
Сравнение для модулей и классов определено таким образом, что в результате мы знаем направление наследования или включение одного модуля другим:
module T1
end
module T2
include T1
end
T3 = T1
class C1
end
class C2 < C1
end
C3 = C1
puts "T1 <=> T2: #{(T1 <=> T2).inspect}" # > 1
puts "T1 <=> T3: #{(T1 <=> T3).inspect}" # > 0
puts "C1 <=> C2: #{(C1 <=> C2).inspect}" # > 1
puts "C1 <=> C3: #{(C1 <=> C3).inspect}" # > 0
puts "C1 <=> T1: #{(C1 <=> T1).inspect}" # > nil
puts "T1 <=> C1: #{(T1 <=> C1).inspect}" # > nil
C3.send(:include, T1)
puts "после включения"
puts "C1 <=> T1: #{(C1 <=> T1).inspect}" # > -1
puts "T1 <=> C1: #{(T1 <=> C1).inspect}" # > 1
Наследник или модуль, который включает другой модуль, меньше, чем родитель или включаемый модуль. Это видно даже из синтаксиса наследования.
Равенство
Существует три метода равенства: ==, eql?, equal?. Последний из которых никогда не следует переопределять, т.к. он отвечает за идентичность. Первые же два обычно работают одинаково. Канонический пример различия из документации:
3 == 3.0 # > true
3.eql? 3.0 # > false
Что лишь свидетельствует о том, что == проводит конвертацию чисел перед сравнением. Обычно == соответствует случаю, когда <=> возвращает 0.
Сравнение case...when
Все мы знаем, что в case...when оператор сравнения — это ===. В большинстве случаев он эквивалентен равенству из предыдущего параграфа. Но если равенство симметрично
(a.==(b)) == (b.==(a))
И если это не так, то это можно считать ошибкой. То === вовсе не обязано таковым быть. Нужно помнить, что в конструкции case...when вызывается метод сравнения объекта, стоящего после when, а в качестве параметра ему передаётся объект, стоящий после case:
puts String === "строка" # > true
puts "строка" === String # > false
puts /ок/ === "строка" # > true
puts "строка" === /ок/ # > false
puts (1..10) === 5 # > true
puts 5 === (1..10) # > false
Материалы для самостоятельного изучения
06.11.2009Политика
Вот, кстати, заметьте! В то время как Майкрософт и его преспешники пишут тролльскую статью про то, что юнит-тесты никому не нужны. Гугл выпускает фреймворк для тестирования JavaScript. Вот более короткое и простое описание от фанатов.
И сразу всё понятно. Будете смеяться над TDD, запомнитесь, как и Майкрософт, за свои глюки.
01.11.2009Дабл
Ну что ж, похоже, моё альтер-эго успокоилось. Писать одновременно в два жжурнала невозможно. Думаю, когда это будет возможно, нужно будет пойти ко врачу. :) Кстати, у меня и бумажный дневник есть.
Так же в моей жизни произошло знаменательное событие. Ко мне в почту упал человек, который пользуется (пытается) нашей (нек4) библиотекой gphoto4ruby. И кроме того, что он мне помог с документацией и ему всё нравится, у него не всё получается, потому что у него Canon. Я что-то, конечно, попробовал сделать удалённо, но. Мне очень нужно для тестирования на недельку какой-нить Canon EOS (40D, 400D, 1000D, 300D, 450D, 5D Mark II). У них, видите ли, есть одна особенность. Нет ли у кого погонять? Могу даже расплатиться деньгами, едой или вязанием крючком.
Ире вчера пришлось два раза ходить в Альфа-Банк, чтобы закрыть там счета. С одного раза не получилось. Моё терпение в их отношении кончилось гораздо раньше, поэтому я уже давно закрыл свои.
Последнее время в блог получается писать раз в две недели. Тема последнего поста — удалённые объекты в руби — так или иначе витала надо мной ещё со времён, когда я был молодым java-программистом-студентом. Но заглянуть в неё плотно и разобраться мне удалось уже только после того, как я сам написал нечто подобное с нуля в программе для съёмок проекта Градоглядъ. Оказалось, что пока я там сам пишу что-то с нуля (но, к моей чести, стандартное решение всё равно неприменимо без значительного допиливания), Лёха во всю строит распределённую систему обработки данных, которая как раз и обращается к объектам на разных машинах, вызывая их методы. И тут он уезжает на две недели и оставляет меня с ней. :) Отличный способ разобраться.