December 30, 2013

FuelPHP Advent Calendar 2013 が電子書籍として達人出版会から出版されました。

FuelPHP Advent Calendar 2013 の計25記事が一冊の電子書籍になり、達人出版会から2013年12月27日に出版されました。
http://tatsu-zine.com/books/fuelphpadvent2013

無料ですし、様々なTIPが掲載されていますので、ぜひダウンロードして読んでみてください。TIP集な構成ですので、読みたい箇所から読めます。尚、EPUB版とPDF版があります。

今年はDay 12、Day 18、Day 21と担当させて頂きました。その中では、Day 21で書いた「Rocketeer」の話が、調べていて面白かったです。

昨年もそうでしたが、この速さで出版されるのって凄いですね。kenjiさん、色々な取りまとめ、お疲れ様でした & ありがとうございました。


参考:
電子書籍『FuelPHP Advent Calendar 2013』が達人出版会より出版されました!

December 23, 2013

chefのcomposerリソースを作ってみました。

レポジトリは以下です。
https://github.com/mp-php/chef-resources-composer

GitHubにはcomposerリソースがいくつかあるのですが、BerkshelfでPHPに依存していた関係で、かえって使いづらい事もあったので、試しに自作してみました。

例えばVagrant + chefで使うには、(所定の場所に配置した上で)まずVagrantfileでchef.add_recipeします。
chef.add_recipe "composer"
composerのインストールは、レシピの好きな場所でします。
composer '/usr/local/bin' do
  action :install
end
composer self-update は"action :update"、アンインストールは"action :uninstall"です。

各プロジェクトでの composer install は以下の通りです。
composer_project '[プロジェクトのルート]' do
  action :install
  user 'vagrant'
  group 'vagrant'
end
各プロジェクトでの composer update は"action :update"です。

とりあえずGitのサブモジュールで個人的に使ってます。まあ、リソース作りの練習ということで。(README.mdは気が向いた時に書きます。。。)

chefでnpmパッケージをインストールする時に、インストール済みパッケージをスキップする

chefの小ネタです。(凄く中途半端な終わり方ですが。。。)

自前でnpmパッケージをインストールする時に
%w{xxx yyy}.each do |p|
  execute p do
    command 'npm install -g ' + p
  end
end
としていたんですが、これだと毎回installが実行されてしまうので、レシピの実行時間が長くなってしまっていました。

なんとか解決したいなーと調べてみると https://github.com/balbeko/chef-npm/pull/9/files を見つけたので、真似してみました。
%w{xxx yyy}.each do |p|
  execute p do
    command 'npm install -g ' + p
 not_if "npm -g ls 2> /dev/null | grep '^[├└]─[─┬] #{p}@'"
  end
end
みたいな感じで手動で試してみたらうまくいくんですけど、chefからだとうまくいかないのは何故だ。。。(解決したら更新します。。。)


Rocketeerで1サーバNステージに対応する

先に、Rocketeerの基本的な使い方については、以下を御覧ください。
http://madroom-project.blogspot.jp/2013/12/fac20131221.html
FuelPHPでの使用例ですが、一般的なPHPプロジェクトで、ほぼそのまま使えると思います。

今回の記事は、あまり良くない気もしますが、例えば1つのサーバにstagingとproductionの2つの環境(以下、ステージ)を持つ場合に、Rocketeerでどう対応するか。です。

まず rocketeer/stages.php を以下のような感じで修正します。
// Adding entries to this array will split the remote folder in stages
// Like /var/www/yourapp/staging and /var/www/yourapp/production
'stages' => array('staging', 'production'),

// The default stage to execute tasks on when --stage is not provided
'default' => 'staging',
これで、Rocketeerを今まで通りに実行すれば、"staging"がデフォルトステージとして扱われます。

ディレクトリ構成で言うと
/[root_directory]/[application_name]/releases/

/[root_directory]/[application_name]/staging/releases/
になりました。

Rocketeer実行時に "-S=production"(または"--stage=production") とすると
/[root_directory]/[application_name]/production/releases/
になりました。

また、rocketeer/config.php の "on.stages" で、ステージ毎の設定ができるみたいです。そうなると "on.connections" はコネクション毎の設定なんだと思います。(まだ試していません。)

December 21, 2013

FuelPHPをRocketeerで自動デプロイしてみる。マイグレーションとPHPUnitも実行してみる。

2014/1/25 追記: 先日、Rocketeerのバージョンが1.0.0になりました。当記事の内容は、それよりも古いバージョンで確認しています。当記事記載のサンプルレポジトリは対応済みです。詳しはそちらのコミットログを御覧ください。

--

FuelPHP Advent Calendar 2013 21日目です。@madmamor が担当します。昨日は @Altsencturely さんの「FuelPHPとFluentdの連携」でした。

今日は、PHP製デプロイツール「Rocketeer」を使って、FuelPHPをコマンド一つでデプロイしてみます。デプロイする最中に、PHPUnitやマイグレーションも実行してみます。

Rocketeer公式ドキュメント:
http://rocketeer.autopergamene.eu/
ライセンスファイルへのリンクが切れてしまっていますが、MITライセンスと書かれています。

RocketeerのGitHub:
https://github.com/Anahkiasen/rocketeer

今回の内容は、Mac OSX Mavericks(以下、ローカル)とVagrantで起動しているUbuntu 13.10(以下、リモート)な環境で確認しています。ローカルは普段通りな開発を行う場所で、そこからコマンドを実行して、リモートにデプロイするイメージです。FuelPHPは1.7.1を使いましたが、Composer対応以降のバージョンであれば、あまり関係は無いはずです。

記事内のソースのライセンスについては、Rocketeerが生成するファイルはRocketeerのライセンスに準じます。私が作成したファイルは、ソースにも書きますが、WTFPLライセンスにします。 http://www.wtfpl.net/txt/copying/


1. 下準備(ローカル)

php.iniで以下の設定をします。
phar.readonly = Off
これをしないと、後述のrocketeer.pharが自身の内部を更新する関係か、以下の警告が大量に出ました。
failed to open stream: phar error: write operations disabled by the php.ini setting phar.readonly
併せて、FuelPHPプロジェクトを作成して、Gitレポジトリへコミットしておいて下さい。このGitレポジトリはリモート側からアクセスできる必要があります。アクセスには、ユーザ名とパスワード、あるいはユーザ名と鍵ファイル(と鍵のパスワード)による認証が使えます。

注意: リモートで鍵の設定時 ~/.ssh/config に以下が無いとエラーになる可能性が有ります。あるいは、一度手動でcloneして、ホストの登録を済ませておきましょう。

StrictHostKeyChecking no
尚、この記事を作成するにあたって作成したFuelPHPプロジェクトのサンプルを公開してあります。
https://github.com/mp-php/fuelphp-advent-calendar-2013-rocketeer-sample


2. 下準備(リモート)

git、PHPとmcrypt extension、Composerをインストールしておきます。更に、以下のsymlinkを貼っておきます。このリンク先は、今現在は存在しませんが、それで構いません。
$ sudo ln -s /home/vagrant/www/fuel-rocketeer-sample/current/public /var/www/fuel-rocketeer-sample


3. Rocketeerのインストールと設定ファイルの準備

ローカルで、以下をダウンロードして、プロジェクトルートに配置します。ダウンロード方法は何でも構いません。
http://rocketeer.autopergamene.eu/versions/rocketeer.phar

以下のコマンドでコマンド一覧やヘルプが表示できればRocketeerのインストールは完了です。
$ php rocketeer.phar # コマンド一覧
$ php rocketeer.phar -h # ヘルプ
次に、設定ファイルを準備します。以下のコマンドを実行して下さい。設問は、とりあえず全て未入力でEnterで良いです。
$ php rocketeer.phar ignite
以下のファイルが生成されたはずです。
  • rocketeer/config.php ... 主にリモートの接続情報を設定する
  • rocketeer/hooks.php ... 主にデプロイ時等のbefore/afterのタスクを設定する(今回は使いません。)
  • rocketeer/paths.php ... phpやcomposer等のコマンドのパスを設定する
  • rocketeer/remote.php ... リモートのデプロイ先に関する色々な設定をする
  • rocketeer/scm.php ... Gitレポジトリの設定をする(SVNも使えるみたいです)
  • rocketeer/stages.php ... 同一サーバに複数ステージ(stagingやproduction)がある場合に使う?(今回は使いません。)
注意: rocketeer.pharは自身の内部にキャッシュ的に接続設定を保存するようです。以降の設定が正しく反映されない場合以下のコマンドを実行してみてください。
$ php rocketeer.phar flush
また、その性質上、rocketeer.pharをパブリックなレポジトリにコミットするのはリスクが有るかもしれません。.gitignoreで除外してしまうのも有りかと思います。


4. リモートの接続情報を設定して確認してみる

rocketeer/config.php を修正します。以下は例なので、適切に書き換えて下さい。(以降、同様です。)
'connections' => array(
    'production' => array(
        'host'      => '192.168.33.10',
        'username'  => 'vagrant',
        'password'  => '',
        'key'       => '/Users/mamor/.vagrant.d/insecure_private_key',
        'keyphrase' => '',
    ),
),
SSHのポートを22以外にしている場合は"xxx.yyy.com:2222"のように指定してあげればOKです。

早速、正しく設定できたか確認してみましょう。
$ php rocketeer.phar check

No repository is set for the repository, please provide one :
No username is set for the repository, please provide one :
No password is set for the repository, please provide one :
Checking presence of git
Checking PHP version
Checking presence of Composer
Checking presence of mcrypt extension
Your server is ready to deploy
Execution time: 0.8238s
正しく接続できて、gitコマンド、PHPバージョン、composerコマンド、mcrypt extensionのチェックが行われました。必要なPHPバージョンは、すみません、確認していません。が、Rocketeerのcomposer.jsonには "php": ">=5.3.0" と書かれています。ちなみに手元は5.5です。


5. デプロイの設定をしてデプロイしてみる

rocketeer/remote.php を修正します。
$ git diff rocketeer/remote.php
diff --git a/rocketeer/remote.php b/rocketeer/remote.php
index a21279b..51424c4 100644
--- a/rocketeer/remote.php
+++ b/rocketeer/remote.php
@@ -11,12 +11,12 @@
        ),

        // The root directory where your applications will be deployed
-       'root_directory'   => '/home/www/',
+       'root_directory'   => '/home/vagrant/www/',

        // The name of the application to deploy
        // This will create a folder of the same name in the root directory
        // configured above, so be careful about the characters used
-       'application_name' => '',
+       'application_name' => 'fuel-rocketeer-sample',

        // The number of releases to keep at all times
        'keep_releases'    => 4,
@@ -25,23 +25,24 @@
        // Use this to list folders that need to keep their state, like
        // user uploaded data, file-based databases, etc.
        'shared' => array(
-               '{path.storage}/logs',
-               '{path.storage}/sessions',
+               'fuel/app/cache',
+               'fuel/app/logs',
+               'fuel/app/tmp',
        ),

        'permissions' => array(

                // The permissions to CHMOD folders to
                // Change to null to leave the folders untouched
-               'permissions' => 755,
+               'permissions' => 777,

                // The folders and files to set as web writable
                // You can pass paths in brackets, so {path.public} will return
                // the correct path to the public folder
                'files' => array(
-                       'app/database/production.sqlite',
-                       '{path.storage}',
-                       '{path.public}',
+                       'fuel/app/cache',
+                       'fuel/app/logs',
+                       'fuel/app/tmp',
                ),

                // The web server user and group to CHOWN folders to
"root_directory"の下に"application_name"な名前のディレクトリが作成され、その中にデプロイされます。この例だと "/home/vagrant/www/fuel-rocketeer-sample" になりますね。

"shared"では、デプロイをまたいで共有したいディレクトリやファイルを設定します。大抵の場合、ログディレクトリやキャッシュディレクトリ等、.gitignoreに書かれているものになると思います。裏を返せば、例えばデプロイ毎にキャッシュをクリアしたければ、あえて共有しなければOKです。尚、共有はsymlinkによって実現されます。

注意: ディレクトリを共有する場合、ディレクトリそのものがレポジトリに含まれている必要があります。.gitkeepや、以下のような.gitignoreファイルをそのディレクトリに入れるなどしておいて下さい。
*
!.gitignore
"permissions"は、指定したディレクトリやファイルを、指定したパーミッションに変更します。今回の例では777を指定していますが、適切な値を設定するようにお願いします。

次に rocketeer/scm.php を修正します。
'repository' => 'https://github.com/mp-php/fuelphp-advent-calendar-2013-rocketeer-sample.git',
"repository"に、GitのレポジトリURLを設定します。今回の例はGitHub上のレポジトリなので、usernameとpasswordは空のままで構いません。また、ファイル内のコメントにも書かれているように、既に鍵認証の設定がされている場合も、空で構いません。

以上で基本的な設定が済んだので、お待ちかねのデプロイを実行してみましょう。
$ php rocketeer.phar deploy

No username is set for the repository, please provide one :
No password is set for the repository, please provide one :
Cloning repository in "/home/vagrant/www/fuel-rocketeer-sample/releases/20131220204831"
Initializing submodules if any
Installing Composer dependencies
Setting permissions for /home/vagrant/www/fuel-rocketeer-sample/releases/20131220204831/fuel/app/cache
Setting permissions for /home/vagrant/www/fuel-rocketeer-sample/releases/20131220204831/fuel/app/logs
Setting permissions for /home/vagrant/www/fuel-rocketeer-sample/releases/20131220204831/fuel/app/tmp
Sharing file /home/vagrant/www/fuel-rocketeer-sample/releases/20131220204831/fuel/app/cache
Sharing file /home/vagrant/www/fuel-rocketeer-sample/releases/20131220204831/fuel/app/logs
Sharing file /home/vagrant/www/fuel-rocketeer-sample/releases/20131220204831/fuel/app/tmp
Successfully deployed release 20131220204831
No releases to prune from the server
Execution time: 61.1617s
Gitレポジトリのclone(submodulesがあればそれも)が行われ、composer installが行われ、パーミッション変更が行われ、共有が行われました。

ブラウザから http://[ドメイン]/fuel-rocketeer-sample/ にアクセスして、おなじみのトップ画面が表示されればデプロイ成功です。

ざっとディレクトリ構造を見てみましょう。
$ ll /home/vagrant/www/fuel-rocketeer-sample/
total 20
drwxrwxr-x 4 vagrant vagrant 4096 Dec 20 11:49 ./
drwxrwxr-x 3 vagrant vagrant 4096 Dec 20 11:48 ../
lrwxrwxrwx 1 vagrant vagrant   63 Dec 20 11:49 current -> /home/vagrant/www/fuel-rocketeer-sample/releases/20131220204831/
drwxrwxr-x 3 vagrant vagrant 4096 Dec 20 11:48 releases/
drwxrwxr-x 3 vagrant vagrant 4096 Dec 20 11:49 shared/
"current"ディレクトリが、先ほどデプロイしたディレクトリへsymlinkされています。

$ ll /home/vagrant/www/fuel-rocketeer-sample/current/fuel/app/
total 56
drwxrwxr-x 11 vagrant vagrant 4096 Dec 20 11:49 ./
drwxrwxr-x  6 vagrant vagrant 4096 Dec 20 11:49 ../
-rw-rw-r--  1 vagrant vagrant  718 Dec 20 11:48 bootstrap.php
lrwxrwxrwx  1 vagrant vagrant   61 Dec 20 11:49 cache -> /home/vagrant/www/fuel-rocketeer-sample/shared/fuel/app/cache/
drwxrwxr-x  5 vagrant vagrant 4096 Dec 20 11:48 classes/
drwxrwxr-x  6 vagrant vagrant 4096 Dec 20 11:48 config/
drwxrwxr-x  3 vagrant vagrant 4096 Dec 20 11:48 lang/
lrwxrwxrwx  1 vagrant vagrant   60 Dec 20 11:49 logs -> /home/vagrant/www/fuel-rocketeer-sample/shared/fuel/app/logs/
drwxrwxr-x  2 vagrant vagrant 4096 Dec 20 11:48 migrations/
drwxrwxr-x  2 vagrant vagrant 4096 Dec 20 11:48 modules/
drwxrwxr-x  2 vagrant vagrant 4096 Dec 20 11:48 tasks/
drwxrwxr-x  5 vagrant vagrant 4096 Dec 20 11:48 tests/
lrwxrwxrwx  1 vagrant vagrant   59 Dec 20 11:49 tmp -> /home/vagrant/www/fuel-rocketeer-sample/shared/fuel/app/tmp/
drwxrwxr-x  2 vagrant vagrant 4096 Dec 20 11:48 vendor/
drwxrwxr-x  3 vagrant vagrant 4096 Dec 20 11:48 views/
共有設定したディレクトリが、"shared"ディレクトリ下にsymlinkされています。パーミッションも、設定したとおりに変更されています。

以上が、Rocketeerによる基本的なデプロイ方法です。


6. マイグレーションも実行してみる

だいぶ長くなってきましたが、続いてマイグレーションの実行です。簡単なマイグレーションファイルを作成してコミットしておきます。
$ php oil generate migration create_users name:text email:string password:string
リモート側でDBやDBユーザの作成、それに対するFuelPHPのconfigのdb.phpの設定も済ませておいて下さい。

次に、2ファイルを新規作成します。

まず、rocketeer/tasks/Migrate.php を新規作成します。今回はサンプルなので、名前空間はつけていません。尚、"Rocketeer\Traits\Task" を継承しますが、このクラスはabstract classであってトレイトではないようです。
<?php

/**
 * Migrate class
 *
 * @author    Mamoru Otsuka http://madroom-project.blogspot.jp/
 * @copyright 2013 Mamoru Otsuka
 * @license   WTFPL License http://www.wtfpl.net/txt/copying/
 */
class Migrate extends Rocketeer\Traits\Task
{
    /**
     * @inheritDoc
     */
    protected $description = 'Migrates the database';

    /**
     * @inheritDoc
     */
    public function execute()
    {
        // 実行時にメッセージとして表示されます
        $this->command->info($this->description);

        // currentディレクトリ内でコマンドを実行します
        $output = $this->runForCurrentRelease('php oil r migrate');

        // 第一引数は失敗時のメッセージです
        // 第二引数は失敗時の詳細です
        // 第三引数は成功時のメッセージです
        return $this->checkStatus('Migrate failed', $output, 'Migrate successfully');
    }
}
rocketeer/tasks.php を新規作成します。
<?php
/**
 * rocketeer/tasks.php
 *
 * @author    Mamoru Otsuka http://madroom-project.blogspot.jp/
 * @copyright 2013 Mamoru Otsuka
 * @license   WTFPL License http://www.wtfpl.net/txt/copying/
 */

// Migrateクラスをカスタムタスクとして登録します
// $ php rocketeer.phar migrate で個別に実行できます
require_once __DIR__.'/tasks/Migrate.php';
Rocketeer\Facades\Rocketeer::add('Migrate');

// deploy後に自動実行されます
// 自動実行したくない場合は書かないで下さい
Rocketeer\Facades\Rocketeer::after('deploy', 'Migrate');
Gitレポジトリにコミット(Push)したら、デプロイを実行してみます。
$ php rocketeer.phar deploy
... 略 ...
Migrates the database
Migrate successfully
Removing 1 release from the server
Execution time: 34.8705s
マイグレーションも実行できました。以下のコマンドでマイグレーションのみを個別に実行することもできます。
$ php rocketeer.phar migrate
注意: deployのafterタスクは、既にsymlinkが貼替えられていることに注意して下さい。尚、マイグレーションを行う場合は、別途、何らかの方法でメンテナンスモードに切り替えるタスクを作成する必要があるかと思います。(もちろん、手作業でも良いですが。)また、後述のPHPUnit失敗時の挙動も気になるところで、マイグレーションを自動化するのはそれなりのリスクが伴いそうです。が、今回はとりあえず自動化して進めます。

after(やbefore)タスクにはクラスの他に、インラインによるコマンド設定や、クロージャの設定もできるみたいです。(まだやったことはありません。)クラスにすると "$this->runForCurrentRelease('コマンド')" のように、便利なメソッドでパス周りの調整が簡単になるので、迷ったらクラスで良いのかなと思います。クラスだと、前述のように個別で実行もできますね。


7. PHPUnitも実行してみる

そろそろ最後です。ソースをcloneして、PHPunitを実行して、全てのテストが成功したらデプロイ続行、一つでも失敗したらデプロイ中止(symlinkの貼替えを行わない)できたら良いですね。

composer.jsonに以下を追記します。
$ git diff composer.json
diff --git a/composer.json b/composer.json
index e1b21ea..5ef630e 100644
--- a/composer.json
+++ b/composer.json
@@ -20,6 +20,9 @@
         "monolog/monolog": "1.5.*",
        "fuelphp/upload": "2.0.1"
     },
+    "require-dev": {
+        "phpunit/phpunit": "3.*"
+    },
     "suggest": {
         "mustache/mustache": "Allow Mustache templating with the Parser package",
         "smarty/smarty": "Allow Smarty templating with the Parser package",
プロジェクト直下にphpunit.xmlを用意します。fuel/core/phpunit.xmlをコピーして、パス周りを整えただけです。
<?xml version="1.0" encoding="UTF-8"?>

<phpunit colors="true" stopOnFailure="false" bootstrap="fuel/core/bootstrap_phpunit.php">
    <php>
        <server name="doc_root" value="./"/>
        <server name="app_path" value="fuel/app"/>
        <server name="core_path" value="fuel/core"/>
        <server name="package_path" value="fuel/packages"/>
        <server name="vendor_path" value="fuel/vendor"/>
        <server name="FUEL_ENV" value="test"/>
    </php>
    <testsuites>
        <testsuite name="core">
            <directory suffix=".php">fuel/core/tests</directory>
        </testsuite>
        <testsuite name="packages">
            <directory suffix=".php">fuel/packages/*/tests</directory>
        </testsuite>
        <testsuite name="app">
            <directory suffix=".php">fuel/app/tests</directory>
        </testsuite>
    </testsuites>
</phpunit>
rocketeer/paths.php を修正します。
$ git diff rocketeer/paths.php
diff --git a/rocketeer/paths.php b/rocketeer/paths.php
index f366b41..2b4d6d4 100644
--- a/rocketeer/paths.php
+++ b/rocketeer/paths.php
@@ -19,4 +19,6 @@
        // Path to the Artisan CLI
        'artisan'  => '',

+       // Path to PHPUnit
+       'phpunit' => 'fuel/vendor/bin/phpunit',
 );
Rocketeerは /usr/local/bin 等のグローバルな場所のphpunit、あるいはプロジェクト直下の vendor/bin/phpunit は勝手に見つけてくれます。FuelPHPの場合は fuel/vendor/bin/phpunit になるので、この設定が必要です。(この設定がない場合は、対話式でパスの入力が可能ですが。)

-t オプションをつけてデプロイを実行してみます。
$ php rocketeer.phar deploy -t
... 略 ...
Running tests...
Tests passed successfully
... 略 ...
Execution time: 194.1824s
テストが行われました。単体でも実行できます。
$ php rocketeer.phar test
... 略 ...
Testing the application
Running tests...
... 略 ...
[vagrant@192.168.33.11] (production) Time: 9.23 seconds, Memory: 23.25Mb
[vagrant@192.168.33.11] (production) OK (361 tests, 413 assertions)
Tests passed successfully
Execution time: 9.6588s
注意: テストの実行をafterタスクで行うこともできますが、その時には既にsymlinkが貼替わってしまっています。-tオプションを使うようにしましょう。

最後に、必ず失敗するテストを作成して、どうなるかも確認してみます。(ソースは割愛します。)
$ php rocketeer.phar deploy -t

No username is set for the repository, please provide one :
No password is set for the repository, please provide one :
Cloning repository in "/home/vagrant/www/fuel-rocketeer-sample/releases/20131220215555"
Initializing submodules if any
Installing Composer dependencies
Running tests...
Tests failed
PHPUnit 3.8-g5fb30aa by Sebastian Bergmann.

Configuration read from /home/vagrant/www/fuel-rocketeer-sample/releases/20131220215555/phpunit.xml

The Xdebug extension is not loaded. No code coverage will be generated.

...............................................................  63 / 362 ( 17%)
............................................................... 126 / 362 ( 34%)
............................................................... 189 / 362 ( 52%)
............................................................... 252 / 362 ( 69%)
............................................................... 315 / 362 ( 87%)
..............................................F

Time: 8.6 seconds, Memory: 23.25Mb

There was 1 failure:

1) Test_Example::test_fail

/home/vagrant/www/fuel-rocketeer-sample/releases/20131220215555/fuel/app/tests/example.php:14

FAILURES!
Tests: 362, Assertions: 413, Failures: 1.
Tests failed
Rolling back to release 20131220215158
Migrates the database
Migrate successfully
Execution time: 196.3031s
デプロイが中断されました。symlinkは以前のままです。中断されたデプロイのディレクトリはゴミとして残りますが、rocketeer/remote.phpの"keep_releases"により、そのうち掃除されると思いますので、あまり気にしなくても良いかなと思います。マイグレーションが実行されてしまうのは想定外だったので、この点は今後の課題にします。。。


8. まとめ

(全ての機能を把握できているわけではありませんし、公式ドキュメントに記載されているプラグイン機能も気になるところですが)Rocketeerを使って、FuelPHPをコマンド一つで、PHPUnitやマイグレーションの実行を含めてデプロイできました。

デプロイツールはRuby製のCapistranoが有名ですが、RocketeerはPHP製ということもあり、ComposerやPHPUnitの扱いを標準でサポートしてくれていて助かります。今日現在、Rocketeerの使い方に関する日本語の情報はかなり少ない(というか無いかもしれません。あったらすみません。)ので、今後、盛り上がってくれると良いなーと思います。

chefとvagrantのおかげで、こういったサーバが絡む実験もやりやすくなったので、ぜひ試してみてください。

以上です。お疲れ様でした。



December 18, 2013

FuelPHPとMongoDBとTraceKitでJavaScriptのエラー情報を収集してみる

FuelPHP Advent Calendar 2013 18日目です。@madmamor が担当します。昨日は @suno88 さんの「レンタルサーバー XREA/CORESERVER で FuelPHP を使う [実践編]」でした。

今日は、FuelPHPとMongoDBとTraceKitを使って、JavaScriptのエラー情報を監視、収集する方法を紹介します。

TraceKitはJavaScriptのエラーを簡単に監視できる、MITライセンスなJavaScriptライブラリです。
https://github.com/occ/TraceKit

また、FuelPHPではMongoDBを簡単に扱えるので、それらを組み合わせることで、JavaScriptのエラーを容易に収集できるのでは。と思いつき、試してみました。

記事内のソースは、WTFPLライセンスとします。 http://www.wtfpl.net/txt/copying/

以下、手順です。


1. 下準備

MongoDBとPECLモジュールのインストールを済ませておいて下さい。

MongoDB
http://www.mongodb.org/

PECL :: Package :: mongo
http://pecl.php.net/package/mongo

PHPからMongoDBが使用可能かは、phpinfo()で確認できます。

FuelPHPのインストールも済ませておいて下さい。トップページが見れる状態です。尚、当記事ではv1.7.1で確認しています。


2. TraceKitのインストール

https://github.com/occ/TraceKit のtracekit.jsをダウンロードして、public/assets/jsに置きます。


3. FuelPHPの設定

config/db.php にMongoDB用の設定を追加します。以下、例です。
'mongo' => array(
    'tracekit' => array(
        'hostname'   => 'localhost',
        'port'       => '27017',
        'database'   => 'tracekit',
        'username'   => 'YOUR_USERNAME',
        'password'   => 'YOUR_PASSWORD',
    ),
),


4. コントローラの作成

app/classes/controller/tracekit.php を作成します。
<?php

/**
 * TraceKitが送信するエラー情報をMongoDBにInsertするコントローラ
 *
 * @author    Mamoru Otsuka http://madroom-project.blogspot.jp/
 * @copyright 2013 Mamoru Otsuka
 * @license   WTFPL License http://www.wtfpl.net/txt/copying/
 */
class Controller_Tracekit extends Controller
{

    /**
     * AjaxでPOSTされたエラー情報をMongoDBにInsertする
     */
    public function post_errors()
    {
        if (Input::is_ajax() and Security::check_token())
        {
            $input = Input::post();
            unset($input[Config::get('security.csrf_token_key')]);

            $mongodb = Mongo_Db::instance('tracekit');
            $mongodb->insert('errors', $input);
        }
    }

}


5. viewの修正

app/views/welcome/index.php を修正します。
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>FuelPHPとMongoDBとTraceKitでJavaScriptのエラー情報を収集してみる</title>
    <!-- エラー情報の送信にjQueryを使います -->
    <?php echo Asset::js('http://code.jquery.com/jquery-1.10.2.min.js'); ?>
    <!-- tracekit.jsです -->
    <?php echo Asset::js('tracekit.js'); ?>
    <!-- jQueryでPOSTする際のトークン生成に使います -->
    <?php echo Security::js_fetch_token(); ?>
    <script>
        // TraceKitでエラーを購読します
        TraceKit.report.subscribe(function myLogger(errorReport) {
            // トークンをセットします
            errorReport.<?php echo Config::get('security.csrf_token_key'); ?> = fuel_csrf_token();
            // エラー情報をPOSTします
            $.post('<?php echo Uri::create('tracekit/errors') ?>', errorReport);
        });
        // 意図的にエラーを発生させてみます
        throw new Error('oops');
    </script>
</head>
<body>
    <h1>FuelPHPとMongoDBとTraceKitでJavaScriptのエラー情報を収集してみる</h1>
</body>
</html>


6. 動作の確認

プラウザからトップページにアクセスして、MongoDBを確認します。MongoDBに以下のようなエラーデータが入っていれば正しく動いています。
{
   "_id": ObjectId("52aef1afece0b97316058000"),
   "mode": "onerror",
   "message": "Uncaught Error: oops",
   "url": "http://192.168.33.10/fuel/",
   "stack": [
     {
       "url": "http://192.168.33.10/fuel/",
       "line": "41",
       "func": "myLogger",
       "context": [
         "\t\t\terrorReport.fuel_csrf_token = fuel_csrf_token();",
         "\t\t\t// エラー情報をPOSTします",
         "\t\t\t$.post('http://192.168.33.10/fuel/tracekit/errors', errorReport);",
         "\t\t});",
         "\t\t// 意図的にエラーを発生させてみます",
         "\t\tthrow new Error('oops');",
         "\t</script>",
         "</head>",
         "<body>",
         "\t<h1>FuelPHPとMongoDBとTraceKitでJavaScriptのエラー情報を収集してみる</h1>",
         "</body>" 
      ] 
    } 
  ],
   "useragent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" 
}
どんな情報が含まれているか、簡単に確認してみましょう。
  • message ... エラーメッセージです。普段、コンソールに出るヤツです。
  • url ... エラーが発生したURLです。
  • stack.line ... エラーが発生した行。のように見えますが、ズレています。。。
  • stack.func ... JavaScript側の送信関数名です。
  • stack.context ... 発生した行と、前後5行ずつのソースです。
  • useragent ... ユーザエージェントです。
注意: stack.contextにminifyされたJavaScriptが含まれると、相当な量になってしまいます。


7. まとめ

FuelPHPとMongoDBとTraceKitを組み合わせると、JavaScriptのエラーを簡単に保存できました。ブラウザで発生するエラーも、こういった方法で把握して、改善していきたいものです。


19日目は @omoon さんの「FuelPHP 5 分で API を実装するチュートリアル(スクリーンキャストあり)」です。

MongoDBのコマンドメモ

単なるメモです。。。(書き足していくかも)
$ mongo # MongoDBに接続する
$ show dbs # データベース一覧を表示する
$ use [データベース名] # データベースに接続する
$ db.getCollectionNames() # コレクション一覧を表示する
$ db.[コレクション名].find() # コレクションのデータを表示する
$ db.[コレクション名].drop() # コレクションを削除する

December 17, 2013

PHPとIMAPでGmailに下書き保存してみる

まず、PHPでIMAPを使えるようにしておく必要があります。
http://www.php.net/manual/en/book.imap.php

Ubuntuだと、以下でインストールできます。
$ sudo apt-get install php5-imap -y
以下、PHP + IMAPでGmailに下書きを保存するサンプルです。
$mailbox = '{imap.gmail.com:993/imap/ssl}';
$username = 'xxxxxxxxxx@gmail.com';
$password = 'yyyyyyyyyyyyyyyyyyyy';
$connection = imap_open($mailbox, $username, $password) or die(imap_last_error());

$envelope['subject']  = 'Subject';
$body[0]['contents.data'] = 'Contents';
$message = imap_mail_compose($envelope, $body);

imap_append($connection, $mailbox.'[Gmail]/Drafts', $message) or die(imap_last_error());
尚、以下で言語を英語にしておく必要がありました。
https://www.google.com/settings/language

December 14, 2013

chefで非対話にコマンドを実行する小ネタ

例えばUbuntuの"ufw enable"は[y|n]の回答を求めてきます。

その場合、chef(というか自動処理)だと非対話に進める必要があるので
execute 'ufw-enable' do
  command 'printf y | ufw enable'
end
としてやるとうまくいきました。

December 12, 2013

FuelPHPでChatWorkパッケージを使ってみる

FuelPHP Advent Calendar 2013 12日目です。@madmamor が担当します。昨日は @chatii0079 さんの「FuelPHP をもっと Composer で使う」でした。


今日は、FuelPHPのChatWorkパッケージを紹介します。

ChatWorkのAPIは、昨月(2013年11月)末にプレビュー版として公開されました。
http://blog-ja.chatwork.com/2013/11/api-preview.html
そこで早速、FuelPHPのパッケージとして実装してみました。
https://github.com/mp-php/fuel-packages-chatwork

ChatWorkパッケージのライセンスはMITライセンスです。
http://opensource.org/licenses/MIT

では、準備と使い方の説明です。


1. ChatWorkのAPIトークンを発行する

現在、ChatWorkのAPIはプレビュー版なので、利用の申請が必要です。
の"お申し込み方法"の通りに、APIの利用申請をします。後日、利用開始のメールが届きます。

利用開始のメールが届いたら、APIトークンを発行します。
の、"APIトークンの取得"の通りです。発行されたAPIトークンは後で使いますので、控えておいて下さい。


2. FuelPHPとChatWorkパッケージのインストール

FuelPHPのインストールを済ませて、トップページが閲覧可能な状態にします。以下の手順はFuelPHP1.7.1で確認していますが、composer対応以降のバージョンであれば問題無いと思います。

次に、composer.json の"require"に以下を追記します。
"mp-php/fuel-packages-chatwork": "dev-master"

次に、composer update します。
$ php composer.phar update

FuelPHPとChatWorkパッケージのインストールは以上です。尚、ChatWorkのAPIを使用する関係で、curlとOpenSSLを有効にしておいて下さい。


3. ChatWorkパッケージの設定

fuel/packages/chatwork/config/chatwork.php を fuel/app/config/ にコピーして、ChatWorkのAPIトークンを設定します。
<?php

return array(
    'api_token' => 'ここにChatWorkのAPIトークンを設定します。',
);

次に、ChatWorkパッケージを有効にします。方法は二つあります。

各所でChatWorkパッケージを使う場合は
fuel/app/config/config.php の"always_load.packages"に"chatwork"を追記します。
'always_load'  => array(
    ...
    'packages'  => array(
        ...
        'chatwork',
        ...

局所的にChatWorkパッケージを使う場合は
その場所(や、そのクラスのコンストラクタ等)でロードします。
Package::load('chatwork');

これで、全ての準備が完了です。


4. ChatWorkパッケージを使ってみる

自分の情報を取得してみます。
http://developer.chatwork.com/ja/endpoint_me.html#GET-me
$response = Chatwork::get('/me');
Debug::dump($response);

指定したルームに投稿してみます。
http://developer.chatwork.com/ja/endpoint_rooms.html#POST-rooms-room_id-messages
$response = Chatwork::post('/rooms/[ルームID]/messages', array('body' => '内容'));
Debug::dump($response);
ルームIDは、URLに含まれる"rid"に続く数値です。"rid0123456789"であれば、ルームIDは"0123456789"になります。

どちらも簡単ですね。

今回はChatwork::get()メソッドとChatwork::post()メソッドを紹介しましたが、Chatwork::put()メソッドとChatwork::delete()メソッドも用意してあります。


5. その他のAPIを使ってみる

現在、APIは大きく分けると
  • /me
  • /rooms
  • /my
  • /contacts
の4種類で、それぞれ、更に細かくAPIが用意されています。
http://developer.chatwork.com/ja/endpoints.html

どれを使うにしても
  • HTTPメソッドは何か
  • 必須パラメータは何か、任意パラメータにどんなパラメータがあるか
を確認すれば簡単に使えるので、ぜひ試してみてください。


最後に、現在のChatWork APIの認証はトークン式のみなので、自分(トークンの持主)が使う前提になっています。パッと思いつく使い方としては、GitHubのHookから特定のルームにメッセージを投稿する。というような、通知の自動投稿でしょうか。タスクを管理する何か。も面白そうですね。

尚、今後、OAuth 2.0式の認証も提供予定とのことです。


以上、ChatWorkパッケージの紹介でした。

December 8, 2013

Ubuntu13.10で、chefのserviceでsshをリロードできない

chefのserviceでsshをリロードしようとしたら
Recipe: default::default
  * service[ssh] action restart
================================================================================
Error executing action `restart` on resource 'service[ssh]'
================================================================================


Mixlib::ShellOut::ShellCommandFailed
------------------------------------
Expected process to exit with [0], but received '1'
---- Begin output of /etc/init.d/ssh restart ----
STDOUT:
STDERR:
---- End output of /etc/init.d/ssh restart ----
Ran /etc/init.d/ssh restart returned 1
のエラーが出たので、原因はよく読んでいませんが
https://github.com/opscode-cookbooks/openssh/pull/30/files
を参考に
provider Chef::Provider::Service::Upstart
を追加してやったら上手くいくようになりました。

(mongodbでも似たようなエラー出て同じ方法で解決(?)した...)

November 23, 2013

PHPMDの設定(XML)メモ

PHPMD
http://phpmd.org/

PHPMDは使用するルールをxmlで設定できます。
$ phpmd app/ text phpmd.xml
以下のxmlだと cleancode, codesize, controversial, design, naming, unusedcode と、一通りのルールが適用されます。
<?xml version="1.0" encoding="UTF-8"?>
<ruleset>
    <rule ref="rulesets/cleancode.xml">
    </rule>

    <rule ref="rulesets/codesize.xml">
    </rule>

    <rule ref="rulesets/controversial.xml">
    </rule>

    <rule ref="rulesets/design.xml">
    </rule>

    <rule ref="rulesets/naming.xml">
    </rule>

    <rule ref="rulesets/unusedcode.xml">
    </rule>
</ruleset>
"exclude"で各ルールのチェックを一部除外する事ができます。
<rule ref="rulesets/naming.xml">
    <exclude name="ShortVariable" />
    <exclude name="ShortMethodName" />
</rule>
各ルールのチェックの設定値を変更する場合はとりあえず"exclude"して、別途指定するのが良いでしょうか。
<rule ref="rulesets/codesize.xml">
    <exclude name="TooManyMethods" />
</rule>
<rule ref="rulesets/codesize.xml/TooManyMethods">
    <properties>
        <property name="maxmethods" value="15" />
    </properties>
</rule>
といった感じのメモでした。

PHPの開発に便利なツールの一括インストールツール

最近ブログを放置気味だったので、大したものではないですが、紹介です。

以前、phingとかphpmdとかその他いろいろ調べていたんですが、インストールにpearを使っていました。今はそれら全てcomposerでインストールできるようだったので、一括してインストールするツールを作ってみました。(結構前に。。。)

レポジトリは https://github.com/mp-php/phpdev-tools です。

以下の方法でインストールできます。
$ git clone https://github.com/mp-php/phpdev-tools.git
$ cd phpdev-tools/
$ php composer.phar install
以下の各ファイルに対して /usr/local/bin とかからリンクを貼るのが楽かなと思います。
$ ls vendor/bin/
apigen.php phing  phpcpd  phpdcd  phpmd
pdepend  php-cs-fixer phpcs  phploc  phpunit

November 7, 2013

Android StudioでAndroidライブラリプロジェクトを読み込むメモ

メモです。

1. Androidライブラリプロジェクトを読み込む
file > Project Structure > Modules > +ボタンで"Import Module"を選択 > Androidライブラリプロジェクトのディレクトリを選択

2. 依存関係を設定する
メインプロジェクトのDependenciesで左下の+ボタンで"Module Dependency"を選択 > 先ほどのAndroidライブラリプロジェクトを選択


November 2, 2013

gearmanのキューをクリアするコマンドメモ

メモです。
$ gearman  -t 1000 -n -w -f [job_name] > /dev/null
みたいな感じで、gearmanのキューをクリア出来ました。

Ubuntu13.04にPECLのgearmanパッケージをインストールして簡単なサンプルを実行してみる

Gearman公式
http://gearman.org/

PECL :: Package :: gearman
http://pecl.php.net/package/gearman

PHP: Gearman - Manual
http://us3.php.net/gearman

1. 必要なパッケージをインストールします。
# Gearman Job Serverがリモートならlibgearman-devのみで良いかもしれません。
$ sudo apt-get -y install gearman libgearman-dev

$ sudo pecl install gearman-1.0.3
PECLのgearmanパッケージを最新バージョン(今日現在で1.1.2)でインストールしようとすると、libgearmanのバージョンとの関係で以下のエラーが出ました。
configure: error: libgearman version 1.1.0 or later required
このエラーはgearman-1.1.0をインストールすれば解決出来ましたが、更に以下のエラーが出たのでgearman-1.0.3にしています。
http://www.phamviet.net/2012/10/10/ubuntu-php-5-4-x-and-gearman-troubleshooting/
(ホントは頑張って最新にすべきなのかもしれませんが、とりあえず。。。)

コマンドから使用されるphp.iniに以下を追記します。
extension=gearman.so
Gearman Job Serverを起動してステータスを確認してみます。
$ sudo gearmand -d
$ gearadmin --status
.
何のジョブもないので、ドットが一つ表示されるだけです。



2. ジョブを実行するworker.phpを作成します。
<?php

// GearmanWorkerのインスタンスを作成します。
$worker = new GearmanWorker();

// 第一引数はホスト(デフォルト127.0.0.1)、第二引数はポート(デフォルト4730)です。
$worker->addServer();

// ジョブ名とコールバック関数を対にして登録します。
// $countは動作確認のために渡しています。
$count = 0;
$worker->addFunction('my_first_job', 'callback_function', $count);

// 無限ループで常駐させます。
while ($worker->work());

// "my_first_job"に対するコールバック関数です。
function callback_function(GearmanJob $job, &$count)
{
    $data = json_decode($job->workload());
    $count++;
    return "Hello {$data->name}! You have visited {$count} times.";
}
起動します。
$ php worker.php
別のコンソールでGearman Job Serverのステータスを確認すると、"my_first_job"に対する1つのワーカー(右端の数値)が確認できます。
$ gearadmin --status
my_first_job 0 0 1
.
各数値は左から順に"未実行タスク"、"実行中タスク"、"ワーカー数"。らしいです。

尚、ワーカーを永続化するには、Supervisorあたりが良いでしょうか。
http://supervisord.org/



3. ジョブを登録するclient.phpを作成します。
<?php

// GearmanClientのインスタンスを作成します。
$client = new GearmanClient();

// 第一引数はホスト(デフォルト127.0.0.1)、第二引数はポート(デフォルト4730)です。
$client->addServer();

// ジョブ名とパラメータを対にして登録します。
$result = $client->do('my_first_job', '{"name":"mamor"}');

// 結果をコンソールに出力します。
echo $result.PHP_EOL;
3回ほど実行してみます。
$ php client.php
Hello mamor! You have visited 1 times.
$ php client.php
Hello mamor! You have visited 2 times.
$ php client.php
Hello mamor! You have visited 3 times.
尚、結果を待つ必要がないジョブの場合、GearmanClientのdo()ではなくdoBackground()でバックグラウンド実行ができるっぽいです。
http://www.php.net/manual/en/gearmanclient.dobackground.php

October 29, 2013

CordovaのInAppBrowserを使うメモ

メモです。

ドキュメント:
http://docs.phonegap.com/en/edge/cordova_inappbrowser_inappbrowser.md.html#InAppBrowser

Cordovaアプリの作成
$ cordova create 
$ cd test/
$ cordova platform add ios
index.htmlに適当な内容を記述
document.addEventListener ("deviceready", function() {
    var ref = window.open("http://apache.org", "_blank", "location=yes");
    ref.addEventListener("exit", function() {
        alert("InAppBrowser closed.");
    });
});
InAppBrowserのインストールとiOSエミュレータでの確認
$ cordova plugin add org.apache.cordova.inappbrowser
$ cordova build
$ cordova emulate ios

VagrantのWEB(Apache)にAndroidの実機からアクセスするメモ

ローカル上のVagrantのWEB(Apache)にAndroidの実機からアクセスしてみました。PCとAndroidは同じWi-Fi上の前提です。

適当な場所で vagrant init します。
$ mkdir xxx
$ cd xxx/
$ vagrant init
Vagrantfileの以下を有効にします。Apacheは直接あるいはレシピでインストールして下さい。
config.vm.network :public_network
vagrant upします。"Available bridged network interfaces"で"Wi-Fi"を選びます。
$ vagrant up
...
[default] Available bridged network interfaces:
1) en0: Ethernet
2) en1: Wi-Fi (AirPort)
3) p2p0
What interface should the network bridge to? 2
vagrant sshでログインしてWi-FiのIPアドレスを確認します。
$ vagrant ssh
$ ifconfig

Androidのブラウザからアクセスしてみます。

スマホアプリでAPIサーバが必要になった時、開発環境のネタにできるかなー。

October 28, 2013

weinreでCordovaアプリをiOSシミュレータとAndroid実機でデバッグしてみる

Homebrewとios-simのインストール
* ios-simをインストールしておくとcordovaコマンドからiOSシミュレータを起動できるようになります。
$ ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"
$ brew install ios-sim
weinreのインストールと起動
$ sudo npm -g install weinre

# Androidの実機でも確認したいので、PCのWiFiのIPアドレスを指定する
# ファイアウォールでポートを閉じていないか注意
$ weinre --boundHost 192.168.xxx.yyy --httpPort 9090
Cordovaアプリの作成
$ cordova create weinre com.example.weinre "Weinre Test"
weinre/www/index.htmlに以下を追記
<script src="http://192.168.xxx.yyy:9090/target/target-script-min.js#anonymous"></script>
プラットフォームの追加 〜 確認
$ cd weinre/
$ cordova platform add ios
$ cordova platform add android
$ cordova build
$ cordova emulate ios # iOSシミュレータの場合
$ cordova run android # USB & WiFi接続しているAndroid実機の場合
Chromeから http://192.168.xxx.yyy:9090/client/#anonymous にアクセスして、"Targets"からiOSシミュレータ/Android実機をそれぞれ選択すると、いつもの感じでデバッグできるようになりました。

参考:
https://developer.mozilla.org/en-US/docs/Mozilla/Firefox_OS/Platform/Gaia/Weinre_As_Remote_Debugger
http://gihyo.jp/dev/serial/01/phonegap2/0003
http://stackoverflow.com/questions/16507430/debugging-web-page-in-android-app-using-weinre-cannot-find-debug-target

October 26, 2013

Steroidsのチュートリアルを試してみる

Steroidsプロジェクトルートで以下のコマンドを実行します。
$ steroids generate tutorial begin
config/application.coffeeのsteroids.config.locationを変更して画面を確認します。
$ steroids.config.location = "tutorial.html"
色々と書かれているので、気が向いた時に見てみます。。。

SteroidsアプリにAndroid端末からアクセスしてみる

Steroids公式:
http://www.appgyver.com/steroids

Steroidsアプリを、試しにAndroid端末で動かしてみたのでメモしておきます。

--

Node.jsをインストールしておきます。

手元のAndroid端末に https://play.google.com/store/apps/details?id=com.appgyver.android をインストールしておきます。

Android端末をWi-Fiで接続しておきます。このネットワークは、使用しているPCと同じネットワークでないといけません。また、Steroidsはlocalhostの4567ポートを使用するので、ファイアウォールの設定に注意して下さい。(少しハマってしまった。。。)

以下のコマンドを実行します。
# Steroidsをインストールしてアプリケーションを生成
$ sudo npm install steroids -g
$ steroids create myapp
$ cd myapp

# ローカルでSteroidsサーバを起動して接続?
$ steroids login
$ steroids connect
ブラウザにQRコードが表示されるので、予めインストールしておいたアプリで読み取り、Welcome画面が表示されればOKと思います。

文言を適当に変更してエンターキーを押すと、端末側も自動でリロードされました。

helpコマンドの実行結果は以下のとおりです。
AppGyver Steroids command  help

 Command          Shortcut      Description
------------------------------------------------------------------------------
 push             <enter>       Push code to connected devices

 sim <device>     s             Launch iOS Simulator; <device> can be:
                                 iphone, iphone_retina_3_5_inch, iphone_retina_4_inch
                                 ipad, ipad_retina

                                Optionally, append @<iOS-version> to launch with a specific iOS
                                version; e.g. iphone@5.1

 qr               -             Show the QR code for connecting devices

 edit             e             Launch editor (set in application.coffee), defaults to subl

 debug <path>     d             Open Safari debugger for matching path in URL location
                                Note: Enable Develop menu in: Safari -> Preferences -> Advanced

 quit             q             Exits connect. Also: exit
以下、関連するドキュメント等です。
http://academy.appgyver.com/courses/2/lessons/32
http://academy.appgyver.com/courses/2/lessons/4
http://guides.appgyver.com/steroids/guides/steroids_npm/troubleshooting-connectivity/

次はiOS(iPod touch)とiOSシミュレータで確認してみたいと思います。

iOSシミュレータは "s" で起動しました。要Xcodeです。iPod touchとかiPhoneは未確認ですが、Android端末と同じ要領でOKなんだと思います。

CordovaでAndroidアプリを作成してみる

Node.jsやAndroid SDKはインストールされている前提です。

手順は、公式ドキュメントだと
http://cordova.apache.org/docs/en/3.1.0/guide_cli_index.md.html#The%20Command-line%20Interface
http://cordova.apache.org/docs/en/3.1.0/guide_platforms_android_index.md.html#Android%20Platform%20Guide
あたりになります。

尚、Android StudioだとAndroid SDKは
/Applications/Android Studio.app/sdk/tools
に有りました。

以下、ドキュメントそのままですが、コマンド例です。
$ sudo npm install -g cordova
$ cordova create hello com.example.hello "HelloWorld"
$ cd hello
$ cordova platform add android
$ cordova build
Android Studioの"Import Project"からplatforms/androidディレクトリを選択してエミュレータで実行します。


P.S.
iOS用のドキュメントは
で、要Xcodeです。


October 12, 2013

「FuelPHP & CodeIgniter ユーザの集い」でFuelPHPとRatchetについてお話させて頂きました

FuelPHPユーザの方とCodeIgniterユーザの方を対象にしたイベント「FuelPHP & CodeIgniter ユーザの集い」 http://atnd.org/events/43750 にて、以前に調べていたFuelPHPとRatchet http://madroom-project.blogspot.jp/2013/06/fuelphpwebsocket.html についてお話させて頂きました。

使用したスライドは http://mamor.github.io/reveal-fuel-ratchet です。

ZeroMQ不要の簡易版があると、ぐっと敷居が下がりそうなので、そのうち作りたいなと思いました。

LTの中には、当パッケージを実際に使用したお話もあり、参考になりました。

September 27, 2013

Bootstrapのプログレスバーの移動速度を変更する

BootstrapのCSSを見てみると
.progress-bar {
... 略 ...
  -webkit-transition: width 0.6s ease;
          transition: width 0.6s ease;
}
とあり
.progress-bar {
    -webkit-transition: width 0s ease;
    transition: width 0s ease;
}
のように、適当な場所でオーバーライドすれば変更出来ました。0にすると即時に移動する感じになりました。

参考:
http://stackoverflow.com/questions/12320669/how-do-you-animate-a-twitter-bootstrap-progress-bar-smoothly

JSでSoundCloudの曲を再生して、duration(総再生時間)を自動で取得、position(現在再生時間)も自動で取得する

前回、簡単な再生、一時停止、停止等のサンプルを書きました。
http://madroom-project.blogspot.jp/2013/09/javascriptsoundcloud.html

今回は、duration(総再生時間)とposition(現在再生時間)を自動で取得するメモです。先にサンプルです。
SC.stream('/tracks/293', {
    autoPlay: true,
    onload: function() {
        console.log(this.duration);
    },
    whileplaying: function() {
        console.log(this.position);
    }
});
autoPlayは、その名の通り自動再生フラグです。上記のソースをそのまま書いてしまうと、完全に自動で再生されてしまいます。再生ボタン押下イベントのコールバック等にすると良いと思います。

autoPlayをtrueにすると曲のローディングは自動で開始され、ローディング完了と思われるタイミングでonloadの処理が行われました。前回の記事で、ローディングに合わせてdurationが増えているようだと書きましたが、onloadで取得すれば一定でした。

whileplayingは、再生中、何度もコールされます。ですのでpositionは増えていきます。autoPlay、onload、whileplayingを組み合わせると、再生時間のプログレスバーを作成できそうです。尚、再生開始のタイミングとローディング完了のタイミングは全く別であることには注意が必要で、実は上記のサンプルだとonloadよりも先にwhileplayingが実行されました。(この辺はうまく調整しないと。。。)


ドキュメント(SoundManager 2)
http://www.schillmania.com/projects/soundmanager2/doc/


--
追記:
autoPlayフラグではなくautoLoadフラグを用いてローディングだけ自動で開始させ、onloadでdurationを取得しつつ再生(this.play()です。)できました。この方法であれば、whileplayingの初回実行は必ずonloadよりも後になります。

September 25, 2013

JavaScriptでSoundCloudの曲を再生してみる

なかなかまとまった情報も無さそうなので、簡単にですが、調べてみました。先に、サンプルです。client_id値は置換して下さい。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SoundCloud TEST</title>
<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script src="http://connect.soundcloud.com/sdk.js"></script>
</head>
<body>

<button id="play">再生</button>
<button id="pause">一時停止</button>
<button id="stop">停止</button>
<button id="duration">総再生時間をコンソールに出力</button>
<button id="position">現在の再生時間をコンソールに出力</button>

<script>
// 初期化
SC.initialize({client_id: 'xxxxxxxxxx'});

// ストリーミングオブジェクト(?)
var s = null;
SC.stream('/tracks/293', function(sound){
    s = sound;
});

// 再生
$('#play').click(function() {
    s.play();
});

// 一時停止
$('#pause').click(function() {
    s.pause();
});

// 停止
$('#stop').click(function() {
    s.stop();
});

// 総再生時間をコンソールに出力
$('#duration').click(function() {
    console.log(s.duration);
});

// 現在の再生時間をコンソールに出力
$('#position').click(function() {
    console.log(s.position);
});
</script>
</body>
</html>
上記サンプルのポイントは
* SC.initialize()にクライアントIDを渡して初期化する
* SC.stream()のコールバックでストリーミングオブジェクト(という表現が正しいのかわからない...)をグローバルな場所に保存
くらいでしょうか。

尚、durationは、再生直後に取得した場合と、少し間をおいた場合とで、値が変わりました。ローディングに合わせて増えていくように見えました。

SoundCloudのドキュメントは以下です。
http://developers.soundcloud.com/docs/api/sdks#streaming

ドキュメントに記載されているように、SoundCloudのJSライブラリは、SoundManager 2というライブラリを用いているようです。
(これが結構がっつりしてる気がする...)

September 24, 2013

ReflectionClassでpublicではないプロパティやメソッドにアクセスするメモ

ユニットテストでpublicではないメソッドのテストをする際に、結構使えたのでサンプルのソースをメモしておきます。
// ReflectionClassでプロパティをpublicにして取得する
function getProperty($object, $propertyName)
{
    $reflection = new ReflectionClass($object);
    $property = $reflection->getProperty($propertyName);
    $property->setAccessible(true);

    return $property->getValue($object);
}

// ReflectionClassでメソッドをpublicにして取得する
function getMethod($object, $methodName)
{
    $reflection = new ReflectionClass($object);
    $method = $reflection->getMethod($methodName);
    $method->setAccessible(true);

    return $method;
}

// publicではないプロパティとメソッドを持つクラス
class Foo
{
    private $bar = 'BAR';

    private function baz($arg)
    {
        return strtoupper($arg);
    }

}

// インスタンス
$foo = new Foo;

// エラーになります
// Fatal error: Cannot access private property Foo::$bar ...
echo $foo->bar;


// エラーになります
// Fatal error: Call to private method Foo::baz() ...
echo $foo->baz('test');

// 'BAR'と表示されます
echo getProperty($foo, 'bar');

// 'TEST'と表示されます
echo getMethod($foo, 'baz')->invokeArgs($foo, ['test']);

September 20, 2013

JSDuckでCoffeeScriptから生成したJSファイルのドキュメントを生成するメモ

メモです。

JSDuckは"/** ... */"の内容からドキュメントを生成するらしいのですが、CoffeeScriptのブロックコメントである"### ... ###"だと"/* ... */"になってしまいます。なので、CoffeeScriptのブロックコメントを"###* ... ###"にすればOKでした。
https://github.com/senchalabs/jsduck/issues/12

September 17, 2013

apache 2.4でバーチャルホストする際の注意(Ubuntu)

以下二点、メモです。

1.
/etc/apache2/sites-available/ に作成するファイルに".conf"の拡張子を付けないとa2ensiteで反応しませんでした。


2.
"Require all granted"を書かないとforbiddenを解決できませんでした。
<VirtualHost *:80>
    ServerName xxx.yyy.com
    DocumentRoot /xxx/yyy/public
    <Directory '/xxx/yyy/public'>
        AllowOverride all
        Require all granted
    </Directory>
</VirtualHost>

September 2, 2013

RubyMineでVagrantのRubyを指定してRSpecを実行する

1. リモート(Vagrant)のRubyを設定する
http://madroom-project.blogspot.jp/2013/09/rubyminevagrantrubyirb.html
と同様。



2. RSpecの設定をする

Run > Edit Configurations > Rake > spec > Configuration
と進む。

"Arguments:"に"RAILS_ENV=test"を入力。
"Path mappings:"でWorking directoryに対するローカルとリモートのマッピングを行う。
参考: http://madroom-project.blogspot.jp/2013/09/rubyminevagrantrubyirb.html



3. RSpecを実行する

Run > 'Run spec'
と進み、テストが正しく実行されるか確認する。



4. おまけ
Macで確認したんですが、通知してくれました。
(Pendingがfailed扱いになっていますが。。。)


RubyMineでVagrantのRubyを指定してIRBコンソールを起動する

結構便利そうなので、メモしておきます。


1. リモート(Vagrant)のRubyを設定する

Project Settings > Ruby SDK and Gems > Add SDK... > New remote...
と進む。

"Fill from Vagrant config"からvagrantのルート(Vagrantfileがある場所)を指定するか、以下を入力。
* Host
* Port
* User name
* Private key file(Auth type がKey pair(Open SSH)の場合)

"Ruby interpreter path" は仮想OS内のrubyのパスを指定する。



2. IRB consoleの設定をする

Run > Edit Configurations > IRB console > IRB console > Configuration
と進む。

"Path mappings"で
* IRB scriptに対するローカルとリモートのマッピング
* Working directoryに対するローカルとリモートのマッピング
をそれぞれ行う。



3. IRB Console が正しく動くか確認する

Run > 'Run IRB Console'
と進み、試しに RUBY_VERSION を出力。






TextMate2でRubyの便利な使い方メモ

外人さんがYouTube動画で小ネタをまとめていたのでメモしておきます。
手元の開発環境が仮想マシンなので、いくつかできていないのもありますが。。。

Text Mate Tip #2 (Hashes)
http://www.youtube.com/watch?v=I8zYNPZ-aRs
Control + l "=>"を補完
":" -> tab ハッシュペアを補完
"Hash" -> tab Hash.new ... のスニペット補完

TextMate Tip #2 (Blocks)
http://www.youtube.com/watch?v=IzJRSFa1Kkw
"do" -> tab do ... end を補完
"{" -> tab { ... } を補完
control + "{" "do ... end" と "{ ... }" の切替??(できなかった...)

TextMate Tip #3 (Don't Leave The Editor)
http://www.youtube.com/watch?v=Ds6hWgl-Z2I
Shift + Command + D 対象ファイルを開く??(できなかった...)
Control + Shift + V 文法のチェック
Control + Shift + E その行を実行する

TextMate Tip #4 (rDoc)
http://www.youtube.com/watch?v=2bDnPyxHypY
Command + R そのファイルを実行する
Control + ":" SymbolとStringの切替
Control + H ドキュメントを開く

TextMate Tip #5 (Counting Iterators)
http://www.youtube.com/watch?v=Z5fCw0fxAzI
TextMateTips #6 (Declarations)
http://www.youtube.com/watch?v=Wws9NIu5OLY
TextMateTip #7 (Declarations Continued)
http://www.youtube.com/watch?v=ClHt1mFnUkU
"dow" -> tab downto のスニペット補完
"ste" -> tab step のスニペット補完
"tim" -> tab times のスニペット補完
"upt" -> tab upto のスニペット補完
"begin" -> tab begin ... rescue ... end のスニペット補完
"case" -> tab case ... when ... end のスニペット補完
"if" -> tab if ... end のスニペット補完
"ife" -> tab if ... else ... end のスニペット補完
"elsif" -> tab elsif ... のスニペット補完
"unless" -> tab unless ... end のスニペット補完
"while" -> tab while ... end のスニペット補完
"until" -> tab until ... end のスニペット補完
"def" -> tab def ... end のスニペット補完
メソッド名を入力 -> Shift + Enter def メソッド名(args) ... end の形式に変換

TextMate Tip #8 (Ruby on Rails Navigating Files)
http://www.youtube.com/watch?v=UrYZGIIqUQE
Command + Shift + "+" 拡大
Command + "-" 縮小
Option + Shift + Command + "↓" 関連するModelやControllerに移動??(できなかった...)

August 25, 2013

RailsでHaml(とRSpec)を使えるようにするメモ

GitLabのソースでも見ながら勉強しようかなーと思っていて、GitLabがHaml(とRSpec)を使っているので、メモです。

1. Gemfile に以下を追記
gem 'haml-rails'
2. bundle install を実行してコントローラを作成
$ bundle install
$ rails g controller index index
      create  app/controllers/index_controller.rb
       route  get "index/index"
      invoke  haml
      create    app/views/index
      create    app/views/index/index.html.haml
      invoke  test_unit
      create    test/controllers/index_controller_test.rb
      invoke  helper
      create    app/helpers/index_helper.rb
      invoke    test_unit
      create      test/helpers/index_helper_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/index.js.coffee
      invoke    scss
      create      app/assets/stylesheets/index.css.scss
3. 生成された views/index/index.html.haml ファイルを確認
%h1 Index#index
%p Find me in app/views/index/index.html.haml
4. index#indexにアクセス
5. RSpec
Gemfile に以下を追記して、同じ要領でした。
gem 'rspec-rails'

August 23, 2013

FluentdでMySQLのスロークエリログをMongoDBに保存してみる

1. スロークエリの設定を済ませます。

MySQLのスロークエリログの設定メモ
http://madroom-project.blogspot.jp/2013/08/mysql.html

2. fluent-plugin-mysqlslowquery をインストールします。
$ sudo fluent-gem install fluent-plugin-mysqlslowquery
3. /etc/fluent/fluent.conf を編集します。
<source>
  type mysql_slow_query
  path /var/log/mysql/mysql-slow.log
  tag mongo.mysql.slow
</source>
<match mongo.mysql.slow>
  type mongo
  database fluent
  collection mysql.slow
</match>
3. fluentdを起動します。
$ sudo fluentd
4. 適当にクエリを発行します。
$ mysql -u root -p[パスワード] mysql
mysql> select * from db where 1 = 1;
5. MongoDB側を確認します。(反映まで少しラグがあるかも)

MySQLのスロークエリログの設定メモ

なんでもない内容ですが。。。メモです。

1. my.cnfに以下を記述
[mysqld]
slow_query_log = ON
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = [秒数(小数可)]
2. MySQLを再起動
"long_query_time"よりも時間がかかるクエリの内容が"slow_query_log_file"に出力されます。とりあえず確認する目的なら"long_query_time"を0にすると、たぶん全部出力されます。

次はこれをFluentdでMongoDBに保存してみようと思います。

FluentdでApacheのアクセスログをMongoDBに保存するメモ(Ubuntu13.04)

メモです。

FluentdとMongoDBのインストールは済ませてある前提です。

1. fluent-plugin-mongoをインストール
sudo fluent-gem install fluent-plugin-mongo

2. /var/log/fluent ディレクトリを作成

3. /etc/fluent/fluent.conf を作成
<source>
  type tail
  format apache2
  path /var/log/apache2/access.log
  pos_file /var/log/fluent/apache2.access_log.pos
  tag mongo.apache.access
</source>
<match mongo.apache.access>
  type mongo
  database fluent
  collection apache.access
</match>
4. 確認
$ sudo fluentd
ブラウザから適当にアクセスして DB => fluent, コレクション => apache.access を確認。

5. 備考
まだあまり調べていないんですが、見た感じ、sourceでpath値のログファイルをtailしつつ、pos_file値の名前で保存。tag値でタグを付けておく。matchでタグ名(ワイルドカードも可)を指定して、設定の通りに処理をする。みたいな感じですかね。

次はMySQLのスロークエリをやってみよう。

--

追記:
"pos_file"について参考 http://colopl.co.jp/tech/blog/2012/05/1808/

August 18, 2013

Vagrantのローカル開発環境でホスト側からMySQLに接続する

SSHで接続しても良いんですが、XAMPPとかMAMPのような感じでローカルの3306(ではなく3366)に直接接続できる感じにしてみました。以下、飽くまでローカル開発環境の話なので、セキュリティ周りは考慮していません。尚、使用しているOSはUbuntu13.04です。

ポイントは以下の3つでした。

1. Vagrantfileで":forwarded_port"の設定をします。ホストの3366がゲストの3306を指すようになります。
config.vm.network :forwarded_port, guest: 3306, host: 3366
2. "/etc/mysql/my.cnf"の"bind-address"の設定をします。ゲストのMySQLにリモートからのアクセスを許可させます。
bind-address  = 0.0.0.0
3. SQLを実行します。rootユーザがリモートからアクセスできるようになります。
GRANT ALL PRIVILEGES ON *.* TO root@'%' IDENTIFIED BY 'xxxxxxxxxx' WITH GRANT OPTION
2と3を反映したChefのレシピは、以下のようになりました。
* node['mysql']['password'] はVagrantfileの"chef.json"に書きます。レシピに直書きでもOKです。
# MySQLのインストール
package 'mysql-server' do
    action :install
    notifies :run, 'execute[mysqladmin]'
    notifies :run, 'execute[mysql]'
end

# パスワードの変更(初回のみ実行される)
execute 'mysqladmin' do
    action :nothing
    command 'mysqladmin password -u root ' + node['mysql']['password']
end

# リモート(ホスト)からの接続を許可(初回のみ実行される)
execute 'mysql' do
    action :nothing
    command "mysql -u root -p#{node['mysql']['password']} -e \"GRANT ALL PRIVILEGES ON *.* TO root@'%' IDENTIFIED BY '#{node['mysql']['password']}' WITH GRANT OPTION\""
end

# サービスを有効にしてスタート
service 'mysql' do
    supports :status => true, :restart => true, :reload => true
    action [:enable, :start]
end

# 前述の"bind-address"の設定を反映する
template '/etc/mysql/my.cnf' do
    notifies :restart, 'service[mysql]'
end
適当なGUIクライアントソフトからlocalhost:3366にrootユーザで接続できればOKです。(ほんとは専用のユーザを用意した方が良いんだろうけど。)

August 17, 2013

Gmailを新しいデザインにしたらInboxのラベル識別子が変わっていたのでAndroidアプリを修正した

以前はGmailのInboxのラベル識別子(?)は "^i" でしたが、WEBで新しいデザインにしたら "^sq_ig_i_personal" に変わってました。他に2つあるようですが今回は関係ないので割愛します。

Android用に提供されている com.google.android.gm.contentprovider の GmailContract クラスだと
GmailContract.Labels.LabelCanonicalNames.CANONICAL_NAME_INBOX
から
GmailContract.Labels.LabelCanonicalNames.CANONICAL_NAME_INBOX_CATEGORY_PRIMARY
に変わることになります。

GmailContractクラスの最新版(?)は
https://developers.google.com/gmail/android/
の "To see an example of this API in action, check out the sample app." からダウンロードしました。

GmailUnreadCounter Widget 2というウィジェットアプリを公開しているので、対応しておきました。
https://play.google.com/store/apps/details?id=net.madroom.gucw2
https://github.com/mp-android/GmailUnreadCounterWidget2/blob/master/src/net/madroom/gucw2/TranslucentActivity.java

August 15, 2013

GruntでCoffeeScriptとSCSSのファイルを監視して自動コンパイルしてみる

Grunt公式: http://gruntjs.com/

1. grunt-cliのインストール
$ npm install -g grunt-cli
2. 作業ディレクトリの作成
$ mkdir grunt_test
$ cd grunt_test
3. package.jsonの作成
vim package.json
4. package.json
{
  "name": "my-project-name",
  "version": "0.1.0",
  "devDependencies": {
  }
}
5. gruntと必要なプラグインのインストール
$ npm install grunt grunt-contrib-watch grunt-contrib-coffee grunt-contrib-sass --save-dev
* --save-devオプションをつけるとpackage.jsonの"devDependencies"を上書きしてくれるみたいです。
* "description"とか書いてないので警告が出ます。
* grunt-contrib-watch: https://github.com/gruntjs/grunt-contrib-watch
* grunt-contrib-coffee: https://github.com/gruntjs/grunt-contrib-coffee
* grunt-contrib-sass: https://github.com/gruntjs/grunt-contrib-sass

6. Gruntfile.jsの作成
$ vim Gruntfile.js
7. Gruntfile.js
module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        coffee: {
            compile: {
                expand: true,
                flatten: true,
                src: ['coffee/*.coffee'],
                dest: 'js',
                ext: '.js'
            }
        },
        sass: {
            compile: {
                expand: true,
                flatten: true,
                src: ['scss/*.scss'],
                dest: 'css',
                ext: '.css'
            }
        },
        watch: {
            coffee: {
                files: ['coffee/*.coffee'],
                tasks: ['coffee']
            },
            sass: {
                files: ['scss/*.scss'],
                tasks: ['sass']
            }
        }
    });
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-coffee');
    grunt.loadNpmTasks('grunt-contrib-sass');
    grunt.registerTask('default', ['coffee', 'sass']);
};
8. 監視開始
$ mkdir coffee
$ touch coffee/hello.coffee
$ mkdir scss
$ touch scss/test.scss
$ grunt watch
Running "watch" task
Waiting...
9. coffee/hello.coffeeを編集
console.log 'hello'
10. コンパイルされたファイルを確認
$ more js/hello.js 
(function() {
  console.log('hello');

}).call(this);
11. scss/test.scssを編集
#main {
  h1{ margin: 10px }
  p{ font-size: 90% }
}
12. コンパイルされたファイルを確認
$ more css/test.css 
#main h1 {
  margin: 10px; }
#main p {
  font-size: 90%; }

npmでCoffeeScriptをインストールしてコンパイルしてみるメモ

メモです。
# CoffeeScriptのインストール
$ npm install -g coffee-script

# hello.coffeeの作成
$ mkdir coffee
$ cd coffee
$ vim hello.coffee

# hello.coffeeに以下を記述
console.log 'hello'

# コンパイルと内容の確認
$ coffee -c hello.coffee
$ more hello.js
// Generated by CoffeeScript 1.6.3
(function() {
  console.log('hello');

}).call(this);

# ヘルプコマンドを実行
$ coffee -h

Usage: coffee [options] path/to/script.coffee -- [args]

If called without options, `coffee` will run your script.

  -b, --bare         compile without a top-level function wrapper
  -c, --compile      compile to JavaScript and save as .js files
  -e, --eval         pass a string from the command line as input
  -h, --help         display this help message
  -i, --interactive  run an interactive CoffeeScript REPL
  -j, --join         concatenate the source CoffeeScript before compiling
  -m, --map          generate source map and save as .map files
  -n, --nodes        print out the parse tree that the parser produces
   --nodejs       pass options directly to the "node" binary
  -o, --output       set the output directory for compiled JavaScript
  -p, --print        print out the compiled JavaScript
  -s, --stdio        listen for and compile scripts over stdio
  -l, --literate     treat stdio as literate style coffee-script
  -t, --tokens       print out the tokens that the lexer/rewriter produce
  -v, --version      display the version number
  -w, --watch        watch scripts for changes and rerun commands
変更の監視は
$ coffee -wc *.coffee
で出来ました。

gemでsassをインストールしてコンパイルしてみるメモ

メモです。
# sassのインストール(compassでまとめてインストールしてみます)
$ gem install compass

# test.scssの作成
$ mkdir scss
$ cd scss
$ vim test.scss

# test.scssに以下を記述
#main{
 h1{ margin-bottom: 20px }
 p{ font-size:90% }
}

# コンパイルと内容の確認
$ scss test.scss test.css
$ more test.css 
#main h1 {
  margin-bottom: 20px; }
#main p {
  font-size: 90%; }

# ヘルプコマンドを実行
$ scss -h
Usage: scss [options] [INPUT] [OUTPUT]

Description:
  Converts SCSS or Sass files to CSS.

Options:
    -s, --stdin                      Read input from standard input instead of an input file
        --trace                      Show a full traceback on error
        --unix-newlines              Use Unix-style newlines in written files.
        --sass                       Use the Indented syntax.
        --watch                      Watch files or directories for changes.
                                     The location of the generated CSS can be set using a colon:
                                       scss --watch input.scss:output.css
                                       scss --watch input-dir:output-dir
        --update                     Compile files or directories to CSS.
                                     Locations are set like --watch.
        --stop-on-error              If a file fails to compile, exit immediately.
                                     Only meaningful for --watch and --update.
        --poll                       Check for file changes manually, rather than relying on the OS.
                                     Only meaningful for --watch.
    -f, --force                      Recompile all Sass files, even if the CSS file is newer.
                                     Only meaningful for --update.
    -c, --check                      Just check syntax, don't evaluate.
    -t, --style NAME                 Output style. Can be nested (default), compact, compressed, or expanded.
        --precision NUMBER_OF_DIGITS How many digits of precision to use when outputting decimal numbers. Defaults to 3.
    -q, --quiet                      Silence warnings and status messages during compilation.
        --compass                    Make Compass imports available and load project configuration.
    -g, --debug-info                 Emit extra information in the generated CSS that can be used by the FireSass Firebug plugin.
    -l, --line-numbers               Emit comments in the generated CSS indicating the corresponding source line.
        --line-comments
    -i, --interactive                Run an interactive SassScript shell.
    -I, --load-path PATH             Add a sass import path.
    -r, --require LIB                Require a Ruby library before running Sass.
        --cache-location PATH        The path to put cached Sass files. Defaults to .sass-cache.
    -C, --no-cache                   Don't cache to sassc files.
    -E encoding                      Specify the default encoding for Sass files.
    -?, -h, --help                   Show this message
    -v, --version                    Print version
変更の監視は
$ scss -w *.scss
で出来ました。

Railsでscaffoldを実行してみる

今までRubyは開発系のツールで少し触っていた程度でしたが、今後、Railsも触っていく必要が出てきたので、とりあえずScaffold(とマイグレーション)を実行してブラウザからアクセスしてみるまでのメモです。
# railsアプリケーションの初期化
$ rails new rails_example

# scaffoldの実行
$ cd rails_example
$ rails generate scaffold User name:string email:string

# マイグレーションの実行
$ rake db:migrate
ソースを反映(デプロイ)して、ブラウザから /users にアクセス


ルーティングの一覧は、以下のコマンドで確認できました。
$ rake routes
   Prefix Verb   URI Pattern               Controller#Action
    users GET    /users(.:format)          users#index
          POST   /users(.:format)          users#create
 new_user GET    /users/new(.:format)      users#new
edit_user GET    /users/:id/edit(.:format) users#edit
     user GET    /users/:id(.:format)      users#show
          PATCH  /users/:id(.:format)      users#update
          PUT    /users/:id(.:format)      users#update
          DELETE /users/:id(.:format)      users#destroy
Chef + VagrantでApache + Passengerの環境構築に時間がかかりすぎた。。。

August 14, 2013

TextMate2の設定ファイルメモ

グローバルな設定ファィルと思われるファイルが
/Users/admin/Library/Application Support/TextMate/Global.tmProperties
にありました。

ユーザ設定ファイルは
~/.tm_properties
として作成できるみたいです。
参考: https://gist.github.com/dvessel/1478685

でも
http://madroom-project.blogspot.jp/2013/08/textmate2.html
の内容は、どちらに書いても反応しませんでした。

まあ、大した内容ではないですが、PCの乗り換え時とかには知っておくと良いかも。。。

TextMate2でサイドバーを左にするメモ

以下のコマンドで出来ました。
defaults write com.macromates.TextMate.preview fileBrowserPlacement left
参考: https://github.com/textmate/textmate/issues/699#issuecomment-19202919

August 13, 2013

Macの言語設定は日本語のままでiTunesを常に英語で起動するメモ

Language Switcherを使うと簡単にできます。
http://www.tj-hd.co.uk/en-gb/languageswitcher/

iTunesを選択、英語を選択して、"常にこの言語で起動"をクリックすれば完了です。

前からそうしていたのですが、HDDをSSDに変えたついでのメモでした。
(言語設定が日本語だと、購入した曲に付くカタカナ曲名とか日本語読み仮名とか、糞なので。。。)

August 10, 2013

Macでvagrant upが進まない

再現手順とかよくわかってないんですが、Macにて、ssh周りが原因で、以下の状態から進まなくなることがたまに発生します。
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
[default] Importing base box 'ubuntu1304'...
[default] Matching MAC address for NAT networking...
[default] Setting the name of the VM...
[default] Clearing any previously set forwarded ports...
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Forwarding ports...
[default] -- 22 => 2222 (adapter 1)
[default] -- 80 => 8080 (adapter 1)
[default] Booting VM...
[default] Waiting for VM to boot. This can take a few minutes.
http://qiita.com/gyhino@github/items/8c10249019cf1e9fce34 を参考にさせて頂き
$ sudo rm ~/.vagrant.d/insecure_private_key
すると進むようになりました。

August 4, 2013

Vagrant + ChefでオレオレPHP開発環境を作ってみました

追記:
依存レシピの管理はBerkshelfを使うべきと思いますが、とりあえずGitのサブモジュールにしてしまっています。

使用するIPアドレスとポートが"192.168.33.10"から"localhost:8081"(php5.5ブランチ)と"localhost:8080"(masterブランチ)に変わりました。記事内の各所は適宜置換して下さい。

--

レポジトリは https://github.com/mp-php/vagrant-phpdev-ubuntu です。

主な環境は
* PHP5.5
* Apache2.4
* MySQL5.5
です。MySQLのrootユーザのパスワードはデフォルトだと"root"になります。これは cookbooks/chef.json で変更できます。

以下、READMEの通りですが、大まかな流れを書いておきます。

まず git clone して vagrant up します。初回は結構時間がかかります。
$ git clone --recursive https://github.com/mp-php/vagrant-phpdev-ubuntu.git
$ cd vagrant-phpdev-ubuntu/
$ vagrant up
起動後、ホストOS側の"share"ディレクトリとゲストOS側の"/share"ディレクトリが共有されます。

新しいプロジェクトを作成するには、ホストOS側の"share"ディレクトリの中に、そのプロジェクトのディレクトリを作ります。
$ mkdir share/my_first_workspace
$ vim share/my_first_workspace/index.php

<?php
phpinfo();
vagrant ssh 等でゲストOSに入って
$ sudo ln -s /share/my_first_workspace /var/www/my_first_workspace
して、シンボリックリンクを貼ります。

これで http://192.168.33.10/my_first_workspace/ のようにアクセスして正しく表示されればOKです。

mod_rewriteを有効にしてあるので、各プロジェクトで .htaccess が使えるはずで、バーチャルホストの設定をしなくてもとりあえず動かせるかなーと。

その他の機能として
* phpMyAdmin http://192.168.33.10/phpmyadmin/
* DBDocs http://192.168.33.10/dbdocs/ (ちゃっかり入れてますw)
* phpunit コマンド
* apigen コマンド
* php-cs-fixer コマンド
* jsduck コマンド
が入っています。

DBDocsは、試しに http://192.168.33.10/dbdocs/index/generate から MySQL を選択
* Document name … 適当
* Description … 適当
* Host … localhost
* Database … phpmyadmin
* user … root
* password … root
* Charset … utf8
と入力して"Generate"ボタンを押してみて下さい。phpmyadminのテーブル一覧が生成されます。

開発上必要なパッケージがスカスカで足りないと思いますが、随時付け足していこうと思います。

良かったら使ってみて下さい。

August 3, 2013

chef-soloとknife-soloでPHP開発環境を構築するレシピを書いてみた

2013/08/04 追記
Vagrant用のレポを作りました。
https://github.com/mp-php/vagrant-phpdev-ubuntu
http://madroom-project.blogspot.jp/2013/08/chef-solophp.html

https://github.com/mp-php/chef-phpdev-ubuntu
は後日削除するかもしれませんm(_ _)m

--

chef-soloの学習がてら、PHP開発環境を構築するレシピを書いてみました。
https://github.com/mp-php/chef-phpdev-ubuntu

細かなパッケージはまだスカスカなので、ブランチは"pre-master"としています。暫く様子を見ながら使っていき、そのうち"master"として1コミットにまとめようかなと思います。

PHPは http://ppa.launchpad.net/ondrej/php5/ubuntu (今日現在、5.5.1)
apache2は http://ppa.launchpad.net/ondrej/apache2/ubuntu (今日現在、2.4.6)
からインストールしていて、MySQLは標準レポジトリからインストールしています。rootのパスワードはデフォルトだとrootになります。

NginxではなくApacheなのは、複数プロジェクトをまとめて動かす場合に.htaccessを使えた方が、サブディレクトリ型のURLで扱いやすいかなという理由です。

その他、開発の補助ツールとして、とりあえず
* phpMyAdmin (/phpmyadmin でアクセスできます。)
* fuel-dbdocs (/dbdocs でアクセスできます。)
* jsduck (jsduck コマンドが使えます。)
* PHPUnit (phpunit コマンドが使えます。)
* ApiGen (apigen コマンドが使えます。)
* PHP-CS-Fixer (php-cs-fixer コマンドが使えます。)
を入れてみました。

このレシピをサブモジュール管理した、Vagrant用のレポって作れるのかなー。作ったら、細かな使い方とか、改めて書きたいと思います。

July 27, 2013

MacからJenkinsをアンインストールするメモ

http://www.axelscript.com/2012/12/17/notes-to-self-uninstall-jenkins-mac/
の通りのことしかやっていませんが、メモしておきます。
$ sudo launchctl unload /Library/LaunchDaemons/org.jenkins-ci.plist
$ sudo rm -rf /Applications/Jenkins "/Library/Application Support/Jenkins" /Library/Documentation/Jenkins
$ sudo rm -rf /Users/Shared/Jenkins
$ sudo dscl . -delete /Users/jenkins
$ sudo dscl . -delete /Groups/jenkins

July 26, 2013

knife-soloでVagrantにnginxをインストールしてみる

nginx.confとかは全くやっていませんが、とりあえず、手順メモです。

Chefレポジトリをknife-soloで作成
$ knife solo init repo-name
$ cd repo-name
$ git init
$ git add .
$ git commit -m "initial commit"
対象ノードにchef-soloをインストール
$ knife solo prepare hostname
$ git add nodes/hostname.json #自動生成された対象ノード用のjsonファイル
$ git commit -m "added nodes/hostname.json"
nginxをインストールするレシピの作成
$ knife cookbook create nginx -o site-cookbooks
$ vim site-cookbooks/nginx/recipes/default.rb
site-cookbooks/nginx/recipes/default.rb
#
# Cookbook Name:: nginx
# Recipe:: default
#
# Copyright 2013, YOUR_COMPANY_NAME
#
# All rights reserved - Do Not Redistribute
#
package "nginx" do
    action :install
end

service "nginx" do
    supports :status => true, :restart => true, :reload => true
    action [ :enable, :start ]
end
$ vim nodes/hostname.json
nodes/hostname.json
{
    "run_list":[
        "nginx"
    ]
}
実行
# Vオプションで詳細が表示されました
$ knife solo cook hostname -V
Vagrantfileの"config.vm.network :private_network, ip:"で指定しているIPアドレスにアクセス

P.S. 実行した時、以下の表示がされ全く進まなくなる現象が出ました。(これでハマった...)
INFO: Chef client  is running, will wait for it to finish and then run.
Vagrantのイメージ上でゴミ(?)プロセスを消して再度実行したらうまくいきました。
$ ps -ef | egrep "(ruby|chef)"
$ sudo kill xxx