LE Blog

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

08.10.2009 firtree_right Будни разработчика

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

Мы собрали новую установочку. Может быть, мне удастся понянчиться с ней пару дней и наделать разных ништяков в программе управления перед тем, как суровые люди заберут у меня её, чтобы мучать вдали от дома. О том, как мне приходится скрещивать скрипты на ruby с оболочкой bash, пока не программирую, я даже написал в блог.

А в остальном всё хорошо. И в Питере хорошо, и в Москве. А как у вас дела?

07.10.2009 firtree_right Некоторые тонкости стыковки ruby и bash

Введение

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

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

Условное выполнение

В bash кроме разделителей команд «&» и «;», существует ещё и условное выполнение списка команд с помощью «&&» и «||». Их работа зависит от кода, с которым произошёл выход. exit0:

#!/usr/bin/env ruby
puts "Выход с кодом 0"
exit 0

exit1:

#!/usr/bin/env ruby
puts "Выход с кодом 1"
exit 1

Теперь если запустить скрипты в следующем сочетании:

./exit0 && ./exit0 && ./exit1 && ./exit0

То вывод будет следующий:

Выход с кодом 0
Выход с кодом 0
Выход с кодом 1

А если запустить скрипты в следующем сочетании:

./exit1 || ./exit1 || ./exit0 || ./exit1

То вывод будет следующий:

Выход с кодом 1
Выход с кодом 1
Выход с кодом 0

То есть, «&&» выполняет следующую команду, если предыдущая вышла с кодом 0, а «||» выполняет следующую команду, если предыдущая вернула ненулевой код. Условно говоря, первый список выполняется пока всё срабатывает, а второй -- пока не срабатывает.

Перенаправление вывода с помощью pipeline

Тут всё просто. Если в bash команды разделены с помощью «|», то вывод первой команды будет перенаправлен на вход второй.

Простая демонстрация из трёх файлов. show:

#!/usr/bin/env ruby
$KCODE = "utf8"
$stdout.puts "stdin содержит: #{$stdin.read.inspect}"

out:

#!/usr/bin/env ruby
$stdout.puts "Привет из stdout!"

err:

#!/usr/bin/env ruby
$stderr.puts "[*stderr*] Привет из stderr!"
$stdout.puts "Привет из stdout!"

Думаю, не составит труда выяснить, что выводят запущенные отдельно out и err, но вот, что из этого можно сделать с помощью pipeline:

./out | ./show

выводит

stdin содержит: "Привет из stdout!\n"

Когда же есть обращение к другому каналу вывода, то

./err | ./show

выводит

[*stderr*] Привет из stderr!
stdin содержит: "Привет из stdout!\n"

Вывод stderr можно перенаправить в stdout:

./err 2>&1 | ./show

выводит

stdin содержит: "[*stderr*] Привет из stderr!\nПривет из stdout!\n"

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

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

03.10.2009 firtree_right Питер

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

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

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

Где баланс?

А мы тем временем поехали в Питер.

23.09.2009 firtree_right Получение irb-консоли в среде приложения

Задача

Допустим, есть приложение, написанное на руби. У него есть основной процесс и есть тесты, которые так или иначе загружают объекты приложения.

Хочется получить доступ в среду приложения в виде irb-консоли, чтобы вручную взаимодействовать с объектами и изменять данные. По типу script/console в rails.

Решение

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

irb
require ...
require ...
...

Раз уж мы захотели как в рельсах, то следует предположить, что окружение нашего приложения загружается одним файлом. Например, config/environment.rb. Это будет первым упрощением многократно повторённого процесса.

Теперь сам файл script/console:

#!/usr/bin/env ruby

APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), ".."))

libs = " -r irb/completion"
libs << " -r pp" # специально для консоли автоподставнока и pretty print
libs << %( -r "#{APP_ROOT}/config/environment.rb")

ENV["APP_ENV"] = "console" # пример того, как сообщить приложению, что оно в консоли

exec "irb #{libs} --simple=prompt"

Здесь стоит сделать два акцента:

  1. Для сообщения приложению, что оно запущено в консоли, мы использовали волшебный хэш ENV. К нему потом можно обратиться внутри config/environment.rb и сделать что-то по-другому.
  2. Для запуска консоли мы использовали Kernel.exec, который не просто выполняет системную команду, но и передаёт ей управление, заменяя текущий процесс.

Теперь, если сделать наш файл запускаемым, будет как в сказке:

chmod +x script/console

Я хочу, чтоб эта песня, эта песня не кончааалась

Если есть приятные библиотеки, которые хочется подключать при каждом запуске irb. А так же, если хочется сохранять историю команд консоли при выходе. То следует воспользоваться мощью ~/.irbrc. Создайте этот файл («~» -- это $HOME, на всякий случай) и напишите в него:

require 'pp' # pretty print
require 'irb/completion' # автоподстановка
require 'irb/ext/save-history' # сохранение истории

ARGV.concat [ "--readline", # не пробовал без readline
              "--prompt-mode", "simple" ] # --simple-prompt

IRB.conf[:SAVE_HISTORY] = 25 # сколько сохранять

IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb-history" # куда сохранять

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

  1. Подробная документация irb;
  2. Занятный способ получить irb-консоль прямо в запущенном работающем приложении.

16.09.2009 firtree_right Обработка исключений в руби

Введение

Сегодняшняя запись — это первая запись по заявкам читателей. :) Я напоминаю, что если у вас включен JavaScript, и если вы читаете эту запись на сайте, а не в rss-читалке, то вдоль левой границы окна будет оранжевая кнопка. Можно написать своё предложение либо нажав на неё, либо непосредственно на странице проекта. За предложения можно так же голосовать.

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

Базовые блоки

Я не буду слишком подробно останавливаться на каждом составляющем, а просто приведу пример, сделав некоторые пояснения:

class RetryException < StandardError
  attr :can_retry
  def initialize(rtr = true)
    @can_retry = rtr
  end
end

def exception_handling
  begin # Если бы не было else, то возвращалось бы последнее значение begin
    yield if block_given? 
  rescue ZeroDivisionError # Если програму запускает не Чак Норрис
    "На ноль делить нельзя"
  rescue RetryException => re # Возврат перезапускает блок begin
    puts "Не получилось, но мы попробуем снова"
    if re.can_retry
      retry
    else
      "Теперь точно не получилось"
    end
  rescue # Здесь ловится всё остальное
    puts "Случилось непредвиденное: #{$!.inspect}"
    raise
  else # Если всё прошло без ошибок
    "Всё прошло без ошибок"
  ensure # Этот блок выполняется в любом случае
    puts "Процесс окончен, но эта часть ничего не возвращает"
    "В любом случае" # Этой строки мы нигде не увидим
  end
end

blocks = [] # Массив с блоками

blocks.push(lambda {})
blocks.push(lambda { 1/0 })
blocks.push(lambda do
  @retry ||= 3 # Пробуем ещё раз не более трёх раз
  @retry -= 1
  raise RetryException.new(@retry > 0), "Временная ошибка"
end)
blocks.push(lambda { raise "Неведомая ошибка" })

blocks.each do |block|
  puts "Возвратилось: #{exception_handling(&block) rescue "Ошибка!"}"
  puts "------------------------"
end

Результат выполнения программы выглядит следующим образом:

Процесс окончен, но эта часть ничего не возвращает
Возвратилось: Всё прошло без ошибок
------------------------
Процесс окончен, но эта часть ничего не возвращает
Возвратилось: На ноль делить нельзя
------------------------
Не получилось, но мы попробуем снова
Не получилось, но мы попробуем снова
Не получилось, но мы попробуем снова
Процесс окончен, но эта часть ничего не возвращает
Возвратилось: Теперь точно не получилось
------------------------
Случилось непредвиденное: #<RuntimeError: Неведомая ошибка>
Процесс окончен, но эта часть ничего не возвращает
Возвратилось: Ошибка!
------------------------

Что мы отсюда почерпнули:

  1. Блок else (необязательный) выполняется в случае удачно выполненного begin и возвращает значение вместо него.
  2. Выполняется первый из блоков rescue, который соответствует ошибке
  3. Метод raise без параметров выбрасывает текущее исключение: то, которое находится в глобальной переменной $!
  4. Чтобы создать свой класс исключений, наследовать нужно от StandardError или от его потомков, иначе не сработает rescue без указания класса (попробуйте сами проверить)

Обратите внимание, что когда мы знаем, что вызов может бросить в нас ошибкой, то ловим её ещё раз. Запись

a = some_call rescue "some value"

эквивалентна

a = begin
  some_call
rescue
  "some value"
end

Особенности работы с потоками

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

Во-первых, без специальных указаний, никто даже не узнает о том, что в каком-то из потоков произошла ошибка:

begin
  Thread.new do
    sleep 1
    puts  "Первый поток"
  end

  Thread.new do
    sleep 3
    puts "Последний поток"
  end

  Thread.new do
    sleep 2
    raise "Ошибка внутри потока"
  end
rescue
  puts "Спасены!"
end

puts "Поехали!"

sleep 7

puts "Как-то слишком тихо"

Производит вывод:

Поехали!
Первый поток
Последний поток
Как-то слишком тихо

Первый способ засечь исключения -- это вызвать join на потоке. Тогда исключение будет переброшено в сам join. Но этот метод блокирует программу, пока поток не завершится. Что конечно же не очень удобно. Наше «поехали» перемещается ближе к концу:

begin
  Thread.new do
    sleep 1
    puts  "Первый поток"
  end

  Thread.new do
    sleep 3
    puts "Последний поток"
  end

  Thread.new do
    sleep 2
    raise "Ошибка внутри потока"
  end.join
rescue Exception => e
  puts "Спасены!"
  puts e.backtrace.join("\n")
end

puts "Поехали!"

sleep 7

Производит вывод:

Первый поток
Спасены!
thr.rb:16
thr.rb:14:in `join'
thr.rb:14
Поехали!
Последний поток

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

Thread.abort_on_exception = true

begin
  Thread.new do
    sleep 1
    puts  "Первый поток"
  end

  Thread.new do
    sleep 3
    puts "Последний поток"
  end

  Thread.new do
    sleep 2
    raise "Ошибка внутри потока"
  end
rescue Exception => e
  puts "Спасены!"
end

puts "Поехали!"

sleep 7

Тогда наша программа оборвётся в момент ошибки и выведет:

Поехали!
Первый поток
thr.rb:16: Ошибка внутри потока (RuntimeError)
        from thr.rb:14:in `initialize'
        from thr.rb:14:in `new'
        from thr.rb:14

И никакие rescue нас не спасут.

Для самостоятельного изучения

  1. Вопросы-задачи-загадки, на которые полезно ответить не пиша программы
  2. Иерархия классов исключений в руби
  3. Более подробно про азы, в которые я не стал здесь спускаться

13.09.2009 firtree_right День программиста

Поздравляю

Дорогие коллеги! Поздравляю вас с Днём программиста! Желаю прежде всего большого интереса в работе, прозрачного кода и быстрой отладки. :)

Бонус

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

С сегодняшнего дня в блоге появилась ворма обратной связи. Оранжевая, такая, кнопка слева. Через неё можно предлагать темы, о которых было бы интересно узнать. А так же голосовать за уже предложенные. Ура!

10.09.2009 firtree_right Создание init-скриптов с помощью руби

Предисловие

Сегодня у меня кроме всего прочего появился повод похвастаться! :) Когда вы смотрите широко обсуждаемые Яндекс-Панорамы, знайте, что их производством в составе моей любимой компании neq4 занимался и я.

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

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

Это и подводит нас вплотную к теме сегодняшней записи.

Введение

Безусловно, руби — лучший из языков. Для меня выбор в его пользу обусловлен прежде всего высокой скоростью разработки. Поэтому при возникновении какой-то задачи, особенно если её нужно сделать быстро, я пробую решить её на руби. В прошлый раз я рассказал об автоподстановке. А сегодня — создание init-скриптов

Скрипты эти располагаются в папке /etc/init.d и запускаются при старте в определённых условиях. Они должны принимать параметры start, stop, restart и status. Как правило, они служат для запуска и остановки служб.

Решение

Написать программу, которая принимает текстовый ключ на входе, не составляет труда (/etc/init.d/my_script):

#!/usr/bin/env ruby

APP_NAME = "my_script"

case ARGV.first
  when "status"
    if...
      ...
      exit 0
    elsif ...
      ...
      exit 1
    else
      exit ...
    end
  when "start"
    ...
  when "stop"
    ...
  when "restart"
    ...
  else
    puts "Usage: #{APP_NAME} {start|stop|restart|status}"
end

Круто, но до настоящего init-скрипта не дотягивает. Когда система проходит свой цикл жизни от запуска до перезагрузки или выключения, она проходит через шесть стадий. Нужно указать, в каких стадиях наш скрипт следует запустить с параметром start, а когда — с параметром stop. Для этого используется спецификация LSB (Linux Standard Base). Вот так:

#!/usr/bin/env ruby
# Start/stop my daemon
#
### BEGIN INIT INFO
# Provides:          my_script
# Required-Start:    $all
# Required-Stop:     $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Initializes my daemon at boot time
# Description:       In a Galaxy far, far away...
### END INIT INFO

APP_NAME = "my_script"

case ARGV.first
.....

Теперь лежащий в /etc/init.d скрипт нужно подрегулировать и зарегистрировать:

sudo chown root:root /etc/init.d/my_script
sudo chmod 755 /etc/init.d/my_script
sudo update-rc.d my_script defaults

Таким образом, вовсе необязательно изучать bash-скрипт. Вот, кстати, оболочка-альтернатива всяким башам, использующая синтаксис руби. Для таких, как я. :)

Для самостоятельного изучения

Спецификация LSB в части запуска и остановки скриптов

02.09.2009 firtree_right Автоподстановка задач rake в терминале с помощью ruby

Введение

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

Задача

Найти решение для автоподстановки задач rake с помощью bash несложно. Для этого используется complete. Огромное число примеров можно посмотреть у себя же в системе в _/etc/bashcompletion.

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

Можно ли из этого извлечь пользу?

Решение

Итак, чтобы подключить наш скрипт, нужно добавить в файл ~/.bashrc такую строчку.

complete -C ~/.bash/autocomplete/rake_complete -o default rake

Для начала положим по адресу _~/.bash/autocomplete/rakecomplete элементарную реализацию автоподстановки, которая будет запускать rake и фильтровать результат.

Следует знать два момента:

  1. Введенная строка, после которой была нажата табуляция, находится в ENV["COMP_LINE"].
  2. В задачах могут попасться пространства имён. Поэтому из окончательного результата нужно убрать часть введенной строки, содержащей двоеточие.

Элементарная реализация выглядит следующим образом:

#!/usr/bin/env ruby

class RakeComplete
  def initialize(cmd)
    @command = cmd[ /\s(.+)$/, 1 ] || ""
  end

  def search
    `rake -T`.split("\n")[1..-1].map{ |l| l.split(/\s+/)[1] }.select{ |cmd| cmd[0, @command.length] == @command }.map{ |cmd| cmd.gsub(cmd_before_column, "") }
  end
private
  def cmd_before_column
    @command[ /.+\:/ ] || ""
  end
end

puts RakeComplete.new(ENV["COMP_LINE"]).search
exit 0

Вроде бы всё понятно: из результата вывода rake -T , отбросив паразитную первую строчку, вытаскиваем только названия задач, подбираем те, начало который совпадает с введенным названием задачи, выводим массив, предварительно удалив у элементов введённую до двоеточия часть. Но уж больно медленно работает. Конечно же, встает вопрос о кешировании.

Кеширование

Приведу сразу результат:

#!/usr/bin/env ruby

class RakeComplete
  CACHE_NAME = ".rake_complete~"

  def initialize(cmd)
    @command = cmd[ /\s(.+)$/, 1 ] || ""
  end

  def search
    exit 0 if rake_file.nil?
    selected_tasks.map do |cmd|
      cmd.gsub(cmd_before_column, "")
    end
  end

private

  def cmd_before_column
    @command[ /.+\:/ ] || ""
  end

  def rake_file
    ["Rakefile", "Rakefile.rb", "rakefile", "rakefile.rb"].detect do |name|
      File.file?(File.join(Dir.pwd, name))
    end
  end

  def cache_file
    File.join(Dir.pwd, CACHE_NAME)
  end

  def generate_tasks
    tasks = `rake -T`.split("\n")[1..-1].map{ |l| l.split(/\s+/)[1] }
    File.open(cache_file, "w") do |f|
      tasks.each do |task|
        f.puts task
      end
    end
    tasks
  end

  def cached_tasks
    File.read(cache_file).split("\n")
  end

  def cache_valid?
    File.exist?(cache_file) && (File.mtime(cache_file) >= File.mtime(rake_file))
  end

  def tasks
    cache_valid? ? cached_tasks : generate_tasks
  end

  def selected_tasks
    tasks.select do |cmd|
      cmd[0, @command.length] == @command
    end
  end
end

puts RakeComplete.new(ENV["COMP_LINE"]).search
exit 0

Для самостоятельного изучения

Думаю, теперь не составит труда написать подобное для задач capistrano.

27.08.2009 firtree_right Приёмы работы с массивами и блоками в качестве аргументов в руби

Введение

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

Массивы

О том, как принять неограниченное число аргументов, знают все:

$KCODE = "utf-8"

def my_args(*args)
  puts args.inspect
end

my_args(1, 2, "собачка", "котик")  # => [1, 2, "собачка", "котик"]

Но что, если сами объекты, которые необходимо передать в качестве аргументов, уже находятся в массиве по какой-то причине?

arr = ["собачка", "котик", "ёжик", "медвежонок"]
selection = arr.select{ |a| a.chars.to_a.length > 5 }

my_args(selection)  # => [["собачка", "медвежонок"]]

Внезапно массив (args) стал двумерным, что неудивительно, т.к. метод интерпретировал переданный ему массив как первый аргумент типа Array. В данном случае, чтобы передать массив как список аргументов, нужно использовать ту же звезду:

my_args(*selection)  # => ["собачка", "медвежонок"]

Ура!

Кстати, звезду можно использовать и при получении значения от функции. Например, в таком случае:

def get_values
  ["красный", "зелёный", "синий"]
end

r, g, b = get_values

puts r.inspect   # => "красный"
puts g.inspect   # => "зелёный"
puts b.inspect   # => "синий"

r, gb = get_values

puts r.inspect   # => "красный"
puts gb.inspect  # => "зелёный"

r, *gb = get_values

puts r.inspect   # => "красный"
puts gb.inspect  # => ["зелёный", "синий"]

По-моему, здорово!

Блоки

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

class Button
  def on_click(&block)
    if block_given?
      @stored_block = block
    else
      puts "Блока нет, но жизнь продолжается"
    end
  end

  def click(*args)
    @stored_block.call(*args)
  end
end

my_btn = Button.new

my_btn.on_click  # => Блока нет, но жизнь продолжается

my_btn.on_click do |*args|
  puts "Произошёл клик!"
end

my_btn.click     # => Произошёл клик!

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

handler = lambda{ |*args| puts "Вот аргументы: #{args.inspect}" }

my_btn.on_click(handler)  # => wrong number of arguments (1 for 0) (ArgumentError)

То есть наш блок превратился в аргумент типа Proc. А метод совсем этого не ожидал. Можно модифицировать сам метод, чтобы он выдерживал подобные условия, но не для этого я всё это пишу :) Если мы чему-нибудь научились в предыдущей главке, то решение по аналогии приходит само:

my_btn.on_click(&handler)

my_btn.click("усы", "хвост")  # => Вот аргументы: ["усы", "хвост"]

Надеюсь, что было полезно.

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

  1. Аргументы в руби
  2. Ещё про аргументы в руби

19.08.2009 firtree_right Бинарные операции для работы с цветом в ActionScript

Введение

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

Задача

Возможно, это уже давно всем известно, но тем не менее, мне бы хотелось об этом написать. В ActionScript существует как минимум два способа обращаться с цветом. Если мы работаем с BitmapData, то используется ARGB, то есть прозрачность и цвет задаются одним числом типа uint. Кстати, 0xffffffff — непрозрачный белый цвет — максимальное число этого типа.

Когда же мы работаем с, например, Graphics, то цвет и прозрачность задаются отдельно друг от друга. Причём прозрачность в диапазоне от 0 до 1. Хотелось бы быстро раскладывать ARGB на части и снова собирать.

Решение

В одну сторону:

var argb:uint = 0xccabcdef;
var a:Number = (argb >>> 24) / 255.0;
var rgb:uint = argb & 0xffffff;

Аналогично, кстати, можно выделить и каналы по отдельности:

var red:uint  =(argb & 0xff0000) >>> 16;
var green:uint  =(argb & 0xff00) >>> 8;
var blue:uint  =(argb & 0xff);

Оператор >>> вместо >> используется чтобы избежать проблем со знаком.

В обратную сторону, соответственно:

argb = (uint(a*255) << 24) | rgb;
argb = (uint(a*255) << 24) | (red << 16) | (green << 8) | blue;

Всё совершенно логично. Чтобы разделить, используется бинарное и, а чтобы объединить — бинарное или.

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