Как установить OS X 10.7 Lion с USB

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

  1. Купив льва не запускать установку, а зайти в папку /Программы/
  2. Найти там Install Mac OS X Lion.app (Установка Mac OS X Lion)
  3. Нажать правой кнопкой и выбрать Показать содержимое пакета
  4. В открывшемся окне заходим в Contents/SharedSupport
  5. Копируем файл InstallESD.dmg это и есть имидж со Львом
  6. Вставляем флешку
  7. Открываем /Программы/Утилиты/Дисковая утилита
  8. Выбираем флешку в списке слева
  9. А в списке справа выбираем вкладку Восстановить
  10. Источник тот самый InstallESD.dmg
  11. Восстановить

Для того чтобы установить с нее надо во время загрузки Mac’а зажать Alt

Разграничение доступа с использованием Gitosis

Gitosis - это система управления несколькими git-репозиториями и управления доступом к ним без необходимости создавать под каждого разработчика отдельного пользователя системы. Все все разработчики будут входить через общий аккаунт и индивидуальные ssh ключи. Преимущества такого решения:

  • Нет необходимости заведения пользователей под каждого разработчика
  • Простая и быстрая настройка доступа к репозиториям
  • Работа через SSH-протокол

Установка Gitosis

Т.к. gitosis написан на python, то для его установки необходим python и пакет python-setuptools

sudo apt-get install python python-setuptools

Ставим непосредственно gitosis. В конечном счете все сводится к клонированию репозитория gitosis и установке через setuptools

git clone git://eagain.net/gitosis.git
cd gitosis
python setup.py install

Настройка на сервере

Нам нужен пользователь под которым будет работать gitosis. Как правило такого пользователя называют git. Добавляем пользователя и группу.

sudo adduser --system --group --disabled-password --home /home/git git

Далее необходим ssh ключ с помощью которого можно будет управлять настройками gitosis, можно использовать свой публичный ключ, если есть или сгенерировать новый (не забыв при этом приватную часть перенести на тот компьютер с которого планируется администрирование gitosis).

ssh-keygen -b 2048 -t rsa -f /tmp/gitosis -C "Gitosis admin"

Запускаем gitosis от имени пользователи git

sudo -H -u git gitosis-init < /tmp/gitosis.pub

Т.к. конфиг gitosis хранится в виде репозитория и применяется при git push, то необходимо сделать исполняем post-update хук.

sudo chmod 755 /home/git/repositories/gitosis-admin.git/hooks/post-update

Управление репозиториями

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

git clone git@АДРЕС_СЕРВЕРА:gitosis-admin.git
cd gitosis-admin

В папке будет локальная версия конфигурации gitosis.conf и папка keydir. Открываем файл gitosis.conf, содержимое будет примерно следующее:

[group gitosis-admin]
writable = gitosis-admin
members = username

Для удобства я рекомендую добавить уровень отладки, который позволит выводить намного больше информации при push’e коммитов в репозитории, для этого надо добавить следующие строки в файл конфигурации:

[gitosis]
loglevel = DEBUG

Для добавления нового репозитория, в конфиг необходимо добавить следующее:

[group projectname]
writable = project_name
members = developer1 developer2

[group projectname-read]
readonly = project_name
members = developer3

Мы добавили две группы projectname и projectname-read. В первой группе мы разрешили разработчикам developer1 и developer2 писать в репозиторий project_name, а во второй группе мы разрешили разработчику developer3 только читать из этого же репозитория.

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

Открытые ключи указанных в конфиге пользователей нужно скопировать в каталог gitosis-admin/keydir. Важно чтобы файлы ключей имели имена вида имя_пользователя.pub. В данном примере в папку необходимо добавить публичные ключи developer1.pub, developer2.pub, developer3.pub.

Для сохранения всех настроек надо закоммитить изменения в репозиторий и push’ить на сервер.

git commit -am "Добавлен проект project_name"
git push

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

mkdir project_name
cd project_name
git init
git remote add origin git@АДРЕС_СЕРВЕРА:project_name.git

Теперь, можно пушить свой репозиторий на сервер с gitosis. Удаленный репозиторий создастся автоматически при первом коммите.

git push origin master

На этом простейшая настройка gitosis закончена.

Обязательная авторизация на Yii

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

Как оказалось сделать подобную конструкцию довольно просто. Для этого в контроллере необходимо переопределить метод beforeAction, например так:

class Controller extends CController
{
    // ...

    protected function beforeAction($action)
    {
        if (Yii::app()->user->isGuest AND $this->id . '/' . $action->id !== 'account/login') {
            Yii::app()->user->loginRequired();
        }

        return true;
    }

    // ...
}

После этого все контроллеры, к которым надо закрыть доступ, наследовать от этого контроллера вместо CController. Если проект создавался через yiic webapp, то такой контроллер можно найти в /protected/components/Controller.php. Таким образом все не авторизованные пользователи обращаясь к любому контроллеру будут принудительно перенаправлены на страницу авторизации, установленную в конфиге (loginUrl).

Использование Sphinx для нахождения ближайших объектов по координатам

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

Наверняка многие воспринимают Sphinx исключительно как полнотекстовый поисковый движок, я же хотел бы обратить внимание на магическую функцию @geodist, которую можно использовать для нахождения в индексе объектов на заданном расстоянии по их координатам (широте и долготе). Для нахождения ближайших объектов требуется не много:

  • База данных поддерживаемая Sphinx’ом с координатами объектов поиска
  • Установленный Sphinx Search
  • А также PHP + sphinxapi (или любой другой язык для которого есть sphinxapi)

База данных

Я не буду описывать процесс установки Sphinx’а на хостинг, мануалов в инете полно да и сам процесс установки банален и не требует каких-то особенных умений. Ниже привожу примерный файл конфига который понадобится для индексации таблицы с координатами объектов

# Источник данных для поиска
source main
{
    # параметры подключения к mysql
    type = mysql
    sql_host = localhost
    sql_user = user
    sql_pass = password
    sql_db = database
    sql_port = 3306

    # запросы после установки соединения
    sql_query_pre = SET NAMES utf8
    sql_query_pre = SET SESSION query_cache_type=OFF

    # не засыпать между шагами индексации
    sql_ranged_throttle = 0
}

# Источник данных для гео-поиска (наследует блок main)
source geo : main
{
    # запрос выборки широты и долготы в радианах для индексации
    sql_query = \
        SELECT `id`,
            radians(`latitude`), \
            radians(`longitude`) \
        FROM table_with_objects

    # обрабатывать поля как float
    sql_attr_float = latitude
    sql_attr_float = longitude
}

# настройка индекса
index geo
{
    # использовать соответствующий блок из source
    source = geo

    # путь для хранения файлов индекса
    path = /usr/home/www/sphinx/indexes/closest

    docinfo = extern
    mlock = 0
    min_word_len = 1
    charset_type = utf-8
}

# настройки демона
searchd
{
    # прослушивание порта
    listen = 9312

    # хранение логов
    log = /usr/home/www/sphinx/log/searchd.log
    query_log = /usr/home/www/sphinx/log/query.log
    pid_file = /usr/home/www/sphinx/log/searchd.pid
}

Индексация

При первоначальном запуске просто скармливаем созданных конфиг индексатору

indexer --config /usr/home/www/sphinx/sphinx.conf --all

Для переиндексации текущего индекса необходимо использовать параметр «–rotate», он добавит к созданному индексу новые данные

indexer --config /usr/home/www/sphinx/sphinx.conf --rotate

Запускаем демона

searchd --config /usr/home/www/sphinx/sphinx.conf

PHP + sphinxapi

Для проверки работы создадим простую форму

<form action="" method="get">
    Долгота <input name="longitude" size="40" value="<?=$_GET['longitude']?>"><br>
    Широта <input name="latitude" size="40" value="<?=$_GET['latitude']?>" /><br>
    Радиус <input name="radius" size="40" value="<?=$_GET['radius']?>"><br>
    <input type="submit" value="Искать!">
</form>

<?php
if(isset($_GET['longitude']) and strlen($_GET['longitude']) > 2)
{
    require_once('sphinxapi.php');

    function collectIds($arr)
    {
        return $arr['id'];
    }

    $_longitude = $_GET['longitude'];
    $_latitude = $_GET['latitude'];
    $_radius = (intval($_GET['radius']) > 0) ? intval($_GET['radius']) : 10;

    $search = new SphinxClient(); // Создание экземпляра клиента

    $search->SetServer("localhost", 9312); // Подсключаемся
    $search->SetMatchMode(SPH_MATCH_ALL); //
    $search->SetArrayResult(true); // Не использовать id документов в качестве ключей
    $search->SetLimits(0, 5); // Ограничить поиск 5 объектами
    $search->SetSortMode ( SPH_SORT_EXTENDED, "@geodist asc"); // Сортировать объекты по расстоянию от исходной точки поиска
    $search->SetGeoAnchor('latitude', 'longitude', deg2rad($_latitude), deg2rad($_longitude));

    $circle = (float) $_radius;
    $search->SetFilterFloatRange('@geodist', 0.0, $circle);

    $result = $search->Query('', 'closestStores');

    if($result AND $result['total'] > 0)
    {
        print_r($result);

        $ids = array_map('collectIds', $result['matches']);

        print_r($ids);
    }
}
?>

Если все настроено правильно, то под формой отобразится массив с результатами выборки.

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

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

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

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

В общем виде этот алгоритм можно описать так:

  1. Выбрать случайное число между единицей и суммой “весов” всех элементов
  2. Спускаться по списку элементов добавляя к счетчику вес текущего элемента
  3. Проверить, если счетчик (шаг №2) больше или равен случайному числу (шаг №1), то закончить цикл и вернуть текущий элемент. В противном случае перейдите к шагу №2.

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

/**
 * Выборка случайного элемента с учетом веса
 *
 * @param array $values индексный массив элементов
 * @param array $weights индексный массив соответствующих весов
 * @return mixed выбранный элемент
 */
function weighted_random_simple ( $values, $weights )
{
    $total = array_sum( $weights );
    $n = 0;

    $num = mt_rand( 1, $total );

    foreach ( $values as $i => $value )
    {
        $n += $weights[$i];

        if ( $n >= $num )
        {
            return $values[$i];
        }
    }
}

Вот пример скрипта который выведет либо A, B, C или с вероятностью 15%, 35% и 50% соответственно:

$values = array('A', 'B', 'C');
$weights = array(3, 7, 10);

echo weighted_random_simple($values, $weights);

Алгоритм случайной выборки из тысяч элементов

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

Однако, алгоритм может быть расширен, чтобы сделать его значительно быстрее. Вместо вычисления общего веса (шаг №1) и счетчика (шаг №2) каждый раз, можно сделать это один раз и сохранить значения счетчиков в массиве. Тогда мы сможем использовать бинарный поиск, чтобы быстро выбрать правильный элемент. Ниже приведен модифицированный вариант функции:

/**
 * Случайно выбирает один из элементов на основе их веса.
 * Оптимизирован для большого числа элементов.
 *
 * @param array $values индексный массив элементов
 * @param array $weights индексный массив соответствующих весов
 * @param array $lookup отсортированный массив для поиска
 * @param int $total_weight сумма всех весов
 * @return mixed выбранный элемент
 */
function weighted_random($values, $weights, $lookup = null, $total_weight = null)
{
    if ($lookup == null OR $total_weight == null)
    {
        list($lookup, $total_weight) = calc_lookups($values, $weights);
    }

    $r = mt_rand(1, $total_weight);

    return $values[binary_search($r, $lookup)];
}

/**
 * Создание массива используемого в бинарном поиске
 *
 * @param array $values
 * @param array $weights
 * @return array
 */
function calc_lookups($values, $weights)
{
    $lookup = array();
    $total_weight = 0;

    for ($i=0; $i < count($weights); $i++)
    {
        $total_weight += $weights[$i];
        $lookup[$i] = $total_weight;
    }

    return array($lookup, $total_weight);
}

/**
 * Ищет в массиве элемент по номеру и возвращает элемент если он найден.
 * В противном случае возвращает позицию, где он должен быть вставлен,
 * или count($haystack)-1, если $needle больше чем любой элемент в массиве.
 *
 * @param int $needle
 * @param array $haystack
 * @return int
 */
function binary_search($needle, $haystack)
{
    $high = count($haystack) - 1;
    $low = 0;

    while ( $low < $high )
    {
        $probe = (int)(($high + $low) / 2);

        if ($haystack[$probe] < $needle)
        {
            $low = $probe + 1;
        }
        elseif ($haystack[$probe] > $needle)
        {
            $high = $probe - 1;
        }
        else
        {
            return $probe;
        }
    }

    if ( $low != $high )
    {
        return $probe;
    }
    else
    {
        return ($haystack[$low] >= $needle) ? $low : $low + 1;
    }
}

Описанный выше скрипт также содержит две новые функции - calc_lookups которая вычисляет массив для использования в бинарном поиске, и непосредственно функция binary_search которая осуществляет бинарный поиск. Пример использования скрипта:

// Рассчет массивов (1 раз)
list($lookup, $total_weight) = calc_lookups($values, $weights);
//....
// Каждый раз когда вам необходимо выбрать случайный элемент:
$val = weighted_random($values, $weights, $lookup, $total_weight);

В заключение

Чтобы дать вам представление о том, какова скорость этих алгоритмов: Для каждого из них я использовал массив включающий 10 000 элементов, 10 000 раз подряд. Первый алгоритм отработал за 13 секунд, а второй всего 0,09 секунд.