LE Blog

Инженер с поэтической душой

28.05.2010 firtree_right Dog's dream

Плавно в течение суток мой бложек перетёк на новый хостинговый план. Со скоростью распространения информации по DNS. Кто-то, возможно, в дальних уголках интернет-пространства видит всё ещё старую копию. Хотя, на глаз не отличишь :)

♯♯♯

А ещё мне не хотелось поднимать свой почтовый сервер, и я узнал, что существует Яндекс почта для домена. Это гениально!

♯♯♯

Сегодня в комментариях (а мне нечасто в блоге пишут комментарии) некто Дмитрий подкинул мне прекрасную идею про «счастливый коммит». Это коммит, у хэша которого сумма левых 20-ти шестнадцатиричных цифр равна сумме правых 20-ти. Конечно же я тоже написал скрипт (конечно же на руби), который проверяет каждый коммит. И коммичу теперь почаще:

#!/usr/bin/env ruby
if `git log -1 --format=format:%H`.chars.each_slice(20).map{ |part| part.inject(0){ |sum, ch| sum + ch.hex } }.uniq.count == 1
puts '**************************************'
puts '*   Congratulations! Lucky commit!   *'
puts '**************************************'
end

♯♯♯

Наконец-то добрался и посмотрел последнюю серию:

via kuteev

Обтекаемо закончили :) Не то, что старик Линч.

26.05.2010 firtree_right Использование git hooks на руби в корыстных целях

Organizers

Введение

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

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

git hooks

Для множества различных целей у git есть хуки. (Как бы их перевести нормально?) Они находятся в каждом репозитории в папке: .git/hooks

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

Использование pre-commit для удаления пробелов на концах строк

Поскольку я, опять же, фанат руби, то и скрипты — благо есть такая возможность — напишу на руби. Создаём файлик .git/hooks/pre-commit:

#!/usr/bin/env ruby

Dir.glob("*.{txt,rb}").each do |p|
  lines = File.readlines(p)
  if lines.inject(false) { |memo, line| line.gsub!(/\s+$/, "") || memo }
    File.open(p, "w") do |f|
      f.puts lines.join("\n")
    end
    system "git add #{p}"
  end
end

Как видно, этот скрипт ищет файлы *.txt и *.rb в корневом каталоге репозитория, и если в них есть пробелы в конце строк, перезаписывает их и добавляет в индекс для коммита.

Не забыть сделать его запускаемым:

chmod +x .git/hooks/pre-commit

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

Материалы для самостоятельного изучения

Документация по git hooks

23.05.2010 firtree_right Дайджест

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

Мой хостинг-провайдер подогнал новый тарифный план, из которого следует, что умея настраивать сервер, не используя Plesk, можно сэкономить от $20 и более ежемесячно. Я так вообще недолюбливаю этот Plesk и практически не пользуюсь. Попробую с месяцок и перейду насовсем. Вообще в выходные вечерком я люблю понастраивать сервера пару часиков для удовольствия. Особенно, освоив vi.

Меж тем Яндекс обнародовал панорамы улиц другого поставщика. Что, конечно же, радует.

12.05.2010 firtree_right Напоминание

А я напоминаю, что всё ещё ищу двух толковых разработчиков. Один владеет C, другой — ActionScript, они находятся в Москве, умеют разговаривать и доделывают начатое. Связаться со мной можно по адресу kruk{at}neq4.ru.

03.05.2010 firtree_right Вакансии

Я ищу двух толковых ребят:

  1. Программист на C (хорошая школа обращения с памятью :)) Его задача — переписать с нуля наш rubygem gphoto4ruby, чтобы он работал:
    • Поверх libusb, а не поверх libgphoto2. У меня есть множество тестовых примеров.
    • Максимально позволял работать с протоколом из ruby. То есть спецификацию камеры читает ruby-программист, а не C-программист, которого я ищу.
  2. Программист на ActionScript чтобы написать на AIR оффлайновое API географических карт с рядом специфических требований.

Можно падать в комментарии или на почту kruk{at}neq4.ru. Если вас читают такие толковые ребята, то не грех и перепостить. :)

28.04.2010 firtree_right Использование Airake под Kubuntu

simple things

Введение

Уже некоторое время назад обнаружил гениальный инструмент. Правда только недавно опробовал его на своих рабочих проектах и зафанател ещё больше. Подготовка к докладу на секции Яндекса про панорамы на РИФе не позволила мне поделиться этим ранее. Исправляю ошибку.

Я люблю руби. И, естественно, rake, как инструмент, продолжающий славные традиции make в руби и с помощью руби. Так же я питаю нежные чувства к ActionScript. Мне нравится AIR, который позволяет писать действительно кросс-платформенные приложения довольно быстро. Так же я неплохо отношусь к TDD, как к одному из способов разработки.

Какова же была моя радость найти инструмент, который всё это объединяет! Хотя ему уже пара лет, он по-прежнему прекрасен.

Установка составляющих

Предполагаю, что ruby, rubygems и rake уже установлены у тех, кто читает этот блог.

Далее, качаем и разархивируем куда-нибудь Adobe AIR SDK и Adobe Flex SDK (или предыдущая версия, если вы консерватор), а так же устанавливаем Adobe AIR Runtime. Чтобы установить последний, после загрузки bin-файла нужно:

chmod +x AdobeAIRInstaller.bin
sudo ./AdobeAIRInstaller.bin

Теперь добавим в PATH пути к исполняемым файлам загруженных SDK. В .bashrc добавляем:

export PATH="/path/to/air_sdk/bin:$PATH"
export PATH="/path/to/flex_sdk_4/bin:$PATH"

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

sudo apt-get install sun-java6-jre

После установки, независимо от того, используете вы Flex3 или Flex4, нужно переписать содержимое AIR SDK поверх Flex SDK. Мне не совсем понятен сакральный смысл этих действий, но иначе ничего не работает.

Привѣтъ, Мiръ!

Создание пустого air-приложения теперь просто:

airake airake_hello_world

Чтобы запустить его, однако, следует исправить в src/AirakeHelloWorld-app.xml и test/Test-app.xml:

...
xmlns="http://ns.adobe.com/air/application/1.5"
...

Если вы решили использовать Flex4, то вам необходимо отредактировать сгенерированное приложение, чтобы запустить его. Это связано с изменениями в стилях. Поэтому проще просто удалить всё содержимое тэга WindowedApplication в файле src/AirakeHelloWorld.mxml.

Про использование TDD в ActionScript я уже писал, поэтому подробно останавливаться не буду. Для примера в код на github включён тривиальный тест. Запуск тестирования происходит привычным образом:

rake test

Документация, если вы пишете правильные комментарии ASDoc, тоже запускается привычным образом:

rake docs

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

rake -T

Использование rake

Конечно, вся прелесть rake не только в привычных и коротких командах для разработки, но и в том, что можно создавать свои сценарии. Например, вот как могла бы выглядеть работа с версиями приложения. Добавим в файл raketasks/version.rake следующий код:

require 'yaml'

desc "Print out current version"
task :version do
  if md = File.read(YAML.load_file('airake.yml')["appxml_path"]).match(/<version>(.*)<\/version>/)
    puts "Current version is #{md[1]}"
  else
    raise "Cannot detect current version.\nMake sure appxml file contains <version>X.X.X</version> tag."
  end
end

namespace :version do

  [:major, :minor, :patch].each do |subv|
    desc "Bump #{subv} in version"
    task :"bump_#{subv}" do

      unless `git status` =~ /nothing to commit/
        raise "There are uncommitted changes. Failed to proceed."
      end 

      appxml = YAML.load_file('airake.yml')["appxml_path"]
      str = File.read(appxml)

      msg = nil
      new_version = nil

      if str.gsub! /<version>(.*)<\/version>/ do |matched|
        old_version = $1
        major, minor, patch = old_version.split(".").map(&:to_i)
        eval("#{subv} += 1")
        new_version = [major, minor, patch].join(".")
        msg = "Version bump #{old_version} => #{new_version}"
        puts msg
        "<version>#{new_version}<\/version>"
      end.nil?
        raise "Cannot detect current version.\nMake sure appxml file contains <version>X.X.X</version> tag."
      else
        File.open(appxml, "w") do |f|
          f.write str
        end

        puts `git commit -am "#{msg}"`
        puts `git tag v#{new_version}`
      end
    end
  end
end

А в Rakefile соответственно:

# Custom rake tasks
Dir.glob("raketasks/*.rake").each { |rf| load rf }

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

rake version
rake version:bump_major
rake version:bump_minor
rake version:bump_patch

И это не предел!

Материалы для самостоятельного изучения

  1. Полный код статьи на github
  2. Инструкция по работе с airake, которая во многом повторена в этой статье с добавлением манипуляций, чтобы всё заработало.
  3. Документация по FlexUnit. Не уверен, что в поставке airake идёт самая последняя версия, но ничего не мешает написать rake task для обновления версии FlexUnit :)
  4. Документация по rake

27.04.2010 firtree_right Новьё

Новый постик в бложек лежит в черновиках. Не публикую ничего в начале и в конце недели. От Яндекса научился, хоть моя аудитория и поменьше :) На прошлой неделе не успел ничего написать из-за РИФа. Был там один день, но как докладчик, поэтому выпала первая половина недели, а не вторая.

Очень понравилась в последнее время концепция «достижений» (achievments) в играх и всём подряд. Например, Mass Effect 2 я проходил три раза, чтобы не только спасти всех компаньонов в самоубийственной миссии (no one left behind), но и сохранить их лояльность. А Portal начал проходить ещё раз, чтобы получить достижение «transmission received». Так же отлично это работает и в социальных сервисах. Например, в foursquare уже довольно подробно размечена вся Москва, потому что за внесение и посещение мест на карте дают очки, звания и знаки отличия. Например, сегодня я стал мэром «Улицы ОГИ». Павел «Ксан» Яковлев заявил, что социальным сетям типа Фейсбука или МоегоКруга следует создать для владельцев профилей Компаний систему достижений для их работников. Тогда работники компаний будут вполне себе мотивированно добиваться этих достижений, чтобы их было видно в профиле.

Ещё, с тех пор, как я приобрёл новый телефон мечты (HTC Desire), всё чаще мне попадаются упоминания и примеры Augmented Reality. Например, Google Goggles — пока мне не удалось получить от них много удививших меня результатов. Но он хорошо определяет продукты по баркодам. Если сфотографировать логотип Яндекс, то будут ссылки на википедию про Интернет и Историю Интернета, А если логотип Гугла, то ссылки будут на Гугл :). Однако второй пример поразил воображение не смотря на простоту. Карта звёздного неба от Гугла. Смотришь на небо через телефон, и все созвездия, звёзды и планеты подписаны. Это зашибись просто! Всего то нужно определить местоположение (по GPS или триангуляцией) и время с календарём. Потом акселерометр сообщает, как повернуть сферу.

А что нового у вас?

19.04.2010 firtree_right 10 000

Когда я сделал себе и Ире сайты, то решил, что у меня есть вполне себе универсальное нечто, что можно легко подстроить под определённый круг нужд. Если бы не обещанное друзьям портфолио, то я бы так и не узнал, насколько мысль о гибсоти и лёгкости настройки далека от истины :) Сегодня выставил его для тестирования — бОльшая часть работы сделана, ура!

В связи с этим часто вспоминаю правило 10 000 часов, о котором узнал из книжки Outliers (ссылка на русское издание, но читаю на английском). Её автор Malcolm Gladwell ссылается на невролога Daniel Levitin, который описал свои исследования ещё в 2006 году в книге This is your brain on music.

Исследования последнего показали, что чтобы стать экспертом мирового уровня в любом деле, необходимо 10 000 часов практики. Нет ни одного признанного мастера мирового уровня, который бы практиковался меньше, чем его конкуренты, при этом их опережая. [Это утверждение работает в обе стороны.] Первый же приводит известные примеры типа Билла Гейтса, «Битлз» и Моцарта, подтверждающих своей биографией это правило. [Кроме всех прочих тонкостей этих конкретных биографий.]

А вспоминаю я это правило в том ключе, что у меня присутствует необоснованное ожидание от себя, что всё получится сразу и круто. Поэтому когда не получается, то я здорово удивляюсь. Потом, конечно, вспоминаю, что практика прежде всего. Да. :)

З.Ы. На мероприятии Яндекса на РИФе мы с Артёмом будем рассказывать о наших технологиях съёмки и обработки панорам улиц. Корпус №6, зал №6, 21 апреля в 14:00.

09.04.2010 firtree_right Злободневно

Только ленивый не высказался сегодня про свинью, которую Apple подложил Adobe'у, внеся в лицензионное соглашение пункт, делающий бесполезной новую разработку Adobe во Flash CS5. Я никогда не разрабатывал под iPhone и, к счастью iРазработчиков, вряд ли буду. :) Фанаты Apple возрадуются, что толпы флэшеров не ринутся на их телефончики. Мой же взгляд давно направлен в сторону Android, и, наконец, я знаю, какой аппарат я куплю, когда он выйдет.

Самое интересное во всём этом — находится в будущем. Учитывая нынешнюю скорость развития технологий, это будущее не так далеко. Речь идёт о стратегических последствиях принятых решений. Стоит, к примеру, упомянуть историю конкуренции VHS и Betacam. Как открытие спецификаций сделало популярным VHS, вытеснив Betacam в сферу профессиональной видеозаписи. Меж тем, VHS уже почил, а Betacam ещё жив в своей нише. Хотя есть мнение, что наибольшее распространение получает тот формат, который выбирают производители порнографии. Возможно именно так разрешилась битва между Blu-ray и HD DVD.

Не смотря на все крики сторонников и противников, немедленных последствий для компании Apple, конечно же, не последует.

07.04.2010 firtree_right Немного о $SAFE

secure code

Введение

Совершенно не по работе заинтересовался переменной $SAFE и её ролью в жизни современного разработчика. Оказалось, что всё нужно проверять самому.

Нежная безопасность

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

print "child: "
child = gets.chomp
puts "child tainted: #{child.tainted?}"
(0..4).to_a.each do |i|
  puts "SAFE: #{i}"
  $a = "safe"
  th = Thread.new do
    $SAFE = i
    child_copy = child.dup
    Thread.current[:out] = ""

    begin
      load child_copy
      Thread.current[:out] += "1. Child loaded\n"
    rescue SecurityError => e
      Thread.current[:out] += "1. Security error: #{e.to_s}\n"
      begin
        child_copy.untaint
        load child_copy
        Thread.current[:out] += "2. Child untainted and loaded\n"
      rescue SecurityError => e
        Thread.current[:out] += "2. Security error: #{e.to_s}\n"
        begin
         Thread.current[:out] += "3. Read from file '#{child_copy}': '#{File.read(child_copy)}'\n"
        rescue SecurityError => e
          Thread.current[:out] += "3. Security error: #{e.to_s}\n"
          begin
           Thread.current[:out] += "4. Read from untainted file: '#{File.read("child.rb")}'\n"
          rescue SecurityError => e
            Thread.current[:out] += "4. Security error: #{e.to_s}\n"
          end
        end
      end
    end

    begin
      $a = "modified"
      Thread.current[:out] +=  "5. Global variable modified: $a = '#{$a}'\n"
    rescue SecurityError => e
      Thread.current[:out] += "5. Security error: #{e.to_s}\n"
    end

    begin
      Dir.mkdir "test"
      Thread.current[:out] += "6. Created directory 'test': #{File.exist?("test")}\n"
      Dir.rmdir "test"
    rescue SecurityError => e
      Thread.current[:out] += "6. Security error: #{e.to_s}\n"
    end

    begin
      Thread.current[:out] +=  "7. Dir glob: #{Dir.glob(File.join("..", "*")).inspect}\n"
    rescue SecurityError => e
      Thread.current[:out] += "7. Security error: #{e.to_s}\n"
    end

    begin
      Thread.current[:out] +=  "8. System ls output: '#{`ls`.chomp}'"
    rescue SecurityError => e
      Thread.current[:out] += "8. Security error: #{e.to_s}\n"
    end
  end
  th.join
  puts "Global variable: $a = '#{$a}'"
  puts th[:out] if th[:out]
end

Конструкция со Thread.current[:out] используется потому, что для $SAFE >= 4 нельзя ничего писать ни в какие устройства вывода.

Вроде бы всё логично. Первый уровень годится для умеренного карантина внешних данных. При желании их можно и расколдовать. Второй уровень запрещает изменения в файловой системе. Третий уровень похож на осаду с постоянным подозрением на шпионаж. Все созданные объекты считаются небезопасными. А четвёртый уровень — это самое близкое к песочнице (sandbox) в руби, что что есть.

Кстати, когда ещё github работал как репозиторий библиотек, спецификация gemspec выполнялась там под $SAFE = 3. Для разработчиков это выливалось в то, что нужно было перечислять все файлы своей библиотеки вручную вместо использования какого-нибудь листинга.

Суровый гайдлайн

Конечно же, только использование $SAFE не убережёт от действительно настойчивой атаки или блокирующего кода. Например:

Thread.new do
  $SAFE = 2
  class String
    def ==(other_string)
      true
    end
  end
end.join
puts "string modified: #{'a' == 'b'}"

И это на втором уровне! А на третьем открыть класс тоже можно, но вызов перегруженного оператора будет вызывать SecurityError.

На сегодняшний момент эту концепцию безопасности можно считать сырой. Актуальное поведение руби 1.8 слегка отклоняется от описаний, что я нашёл. Поведение в 1.9 изменилось, но подробно нигде не описано (я не нашёл).

Это не значит, что этой переменной нет применения в жизни прогрессивного человечества. Адекватное текущему состоянию применение — это гайдлайн при разработке. Руководство для программистов, которое само следит за своим исполнением. Жестковато, но зато действенно. :)

Материалы для самостоятельного изучения

  1. Код примеров в статье на github
  2. Старая, но самая подробная документация по $SAFE
  3. Просто дополнительно: шпаргалка по руби