Forever: перезапуск приложений с одинаковым названием стартового скрипта

Есть такая приблуда forever, если говорить коротко о её назначении - логирование ошибок и автоматический перезапуск приложения после его падения.
Собственно эта заметка о том как перезапускать приложение при помощи forever, если рядом запущено ещё одно приложение с таким же именем запускающего приложение скрипта. Обычно проще всего убивать по имени, но вот так получилось что имена скриптов у меня перекрылись и перезапуск одного приложения стал вызывать смерть других. Логично, на помощь приходит uid, который позволяет каждому приложению присвоить понятно что, и таким образом решить "проблему" с уникальностью имен скриптов, запущенных одновременно на одной машине.
Пример скрипта для перезапуска приложения по uid
#!/bin/bash
APP_DIR="/home/user/app-folder"
FOREVER="/usr/local/bin/forever"
APP_UID=appUniqUid
DAEMON=$FOREVER" --append --uid $APP_UID start --spinSleepTime 10000 --minUptime 1000 --sourceDir "$APP_DIR
APP_SCRIPT="app.js"

## vars for app env
export APP_PORT=4433
export NODE_ENV=production
####

echo -n "Stopping $APP_SCRIPT: "
$FOREVER stop $APP_UID && echo 'ok, stoped' || echo 'error on stop node app'
echo -n "Starting $APP_SCRIPT: "
$DAEMON $APP_SCRIPT && echo 'ok, started' || echo 'error on start node app'
Теперь в консоли можно выполнить
forever list
и увидеть искомый uid.
Зачем это вообще надо, ведь forever сам должен уметь перезапускать всё и после деплоя и после падения?? Должен, да, но вот бывает такая фигня, когда после деплоя взлетает всё криво, или по каким-то другим причинам приложению требуется насильная перезагрузка. А вспоминать как там рулить forever в таких случаях мне лень. Я лентяй, поэтому я написал этот скриптик.

OS X: add app to firewall whitelist / разблокируем приложение в брандмауэре

Суть проблемы: nginx установлен через brew и стандартный интерфейс добавления приложений через Системные настройки -> Защита и безопасность -> Брандмауэр -> Параметры брандмауэра -> "+" его разумеется не видит и добавить через путь не дает (защита от криворукости видимо). Выход - консоль. Пример добавления
# добавляем приложение в список
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add /usr/local/Cellar/nginx/1.6.2/bin/nginx
# разрешаем входящие соединения
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --unblockapp /usr/local/Cellar/nginx/1.6.2/bin/nginx
# запрещаем входящие соединения
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --blockapp /usr/local/Cellar/nginx/1.6.2/bin/nginx
Теперь nginx нужно будет перезапустить и всё должно взлететь. REM: замечено что после выхода из спящего режима настройка эта по непонятным причинам работать перестает. То есть приходится удалять правило, останавливать nginx, добавлять правило обратно, запускать nginx. Причины я пока не понял, перезапускаю скриптиком по необходимости.

Laravel4: вложенная папка для контроллеров / subfolder for controllers

Натолкнулся на интересный вопрос. Проект большой, контроллеров много становится, захотелось как-то структурировать их чтобы не искать каждый раз. Красноглазие же развивается от этого. Натолкнулся на тему с namespace-ами для групп роутов, да, все работает, но надо изрядно перепиливать все вызовы фасадов, размечать неймспейсы.. короче рутинной возни ненужной куча. Если просто попробовать впилить папку в роут в стиле
Route::post('/lala-page', ['as' => 'lalala', 'uses' => 'subfolder/LalaController@myfinc']);
То оно мало того что выглядит убого так ещё и не работает. А решение было элементарнейшее: перестроить карту классов композером. То есть просто создаем все свои папки где будут жить контроллеры, раскладываем туда контроллеры
app/controllers
app/controllers/my
app/controllers/admin
...
В самих контроллерах ничего не меняем. Просто перестраиваем автозагрузку (из папки проекта разумеется)
composer dump-autoload
И вуаля, все вложенные контроллеры заработали. Мне бы такие инструменты лет 10 назад.. Ну пять хотя-бы :)
REM: см. git diff vendor/composer/autoload_classmap.php

Laravel4: Валидация и кастомные название полей в сообщениях об ошибках

Есть ли у вас такая проблема что.. Нет, правильнее будет сразу скриншот показать.



Маркером отмечены сообщения о том, что валидатору не нравятся данные которые вы пытаетесь сохранить через модель. Все хорошо и здорово кроме названия поля в сообщениях. Смышленый пользователь конечно способен догадаться что за поле имеется ввиду, но очевидно чем больше полей в исходной форме, тем сложнее придется пользователю. Да и как-то.. в общем перфекционисты от этого страдают. Есть "способ вылечить" это (на самом деле документированная возможность). Выглядит это примерно так.
public function isValid()
{
  $validator = Validator::make(
    $this->toArray(),
    [
      'name' => 'required',
      'full_link' => 'required|url|unique:table_links,full_link,' . $this->id,
    ]
  );

  $validator->setAttributeNames([
    'name' => 'Название ссылки',
    'full_link' => 'Ссылка для подсчета кликов',
  ]);

  if ($validator->fails()) {
    $this->errors = $validator->errors();
  }

  return $validator->passes();
}

Теперь сообщения будут выглядеть вот так


Стало куда веселее. Осталось почитать оф. документацию по валидации.
Метод setAttributeNames можно поискать в API, он там есть, но описание скромненькое конечно.
Что касается метода isValid, то это метод модели который используется для валидации вот так
public static function boot()
{
  parent::boot();

  // before update and create
  MyModel::creating(function($item) {
    if (!$item->isValid()) return false;
  });

  MyModel::saving(function ($item) {
    if (!$item->isValid()) return false;
  });
}
То есть на модель вешаются хуки, как видно на события сохранения и создания объекта, которые собственно и вызывают наш метод валидации при соответствующих обстоятельствах.
Пример использования такой модели в контроллере выглядит примерно так
public function itemSave()
{
  $item = MyModel::find(Input::get('id'));
  if (!$item) {
    App::abort(404);
  }

  $item->name = Input::get('name');
  $item->full_link = Input::get('full_link');

  if (!$item->save()) {
    return Redirect::route('item-edit', [$item->id])->withErrors($item->errors);
  }

  return Redirect::route('item-edit', [$item->id])->withItemSaved(1);
}
Пример я упростил, но суть та же. Если сохранение отработало с ошибкой, возвращаемся назад с ошибками из валидатора, иначе всё тип-топ, сообщаем об успешном сохранении. Это всё.

P.S.: Для поддержки языковых версий можно вписывать названия свойств вот так
$validator->setAttributeNames([
  'name' => Lang::get('error.name'),
  'full_link' => Lang::get('error.full_link'),
]);

PhpStorm: Multiple Selection / Одновременное редактирование нескольких строк

Что-то я совсем выпал из жизни.. Тем временем в PhpStorm 8 ещё в марте, с EAP, появилась фишка "Multiple Selection". Такая функциональность давно есть в Vim и в Sublime Text, теперь есть и в PhpStorm.



В видео используется способ добавления новых курсоров через Alt, теоретически тоже довольно удобно, но мышка это долго, хочется делать тоже самое клавиатурой и такая возможность есть. Правда по умолчанию хоткеи для клавиатуры не установлены поэтому придется их установить самому. Открываем настройки, идем править Keymap. На скриншоте ниже мои хоткеи для добавления курсора через Alt+Click и для добавления курсора на строку выше/ниже текущей с помощью одной клавиатуры.



Поведение напоминает описанное мной раньше для вима, но несколько умнее. Например если мы хотим обернуть пять строк в массив, то после исправления начала всех строк даже при разной длинне строк мы сможем перейти к хвосту каждой строки просто нажав End. И это сработает для каждой строки, то есть каждый курсор переместятся к хвосту "своей" строки. Выйти из режима мультикурсоров можно нажатием Esc. Просто, очень удобно.