December 7, 2014

Android Studioが"Java not found"で起動しない(Mac)

久々にAndroid Studioを起動してバージョンアップすると、以下の"Java not found"エラーが出て全く起動しなくなってしまいました。

単純に、JDKのパス設定が間違っているだけなように見えますが、Android Studioそのものが起動しないので、設定ファイルを直接編集する必要がありそうでした。

調べてみると http://stackoverflow.com/questions/16636146/using-android-studio-with-java-1-7 に、まさにその通りな回答がありました。以下、メモです。

Android Studioの"Info.plist"というファイルを編集します。(brew caskでインストールしているので以下のロケーションになりますが、インストーラでインストールしていると/Applications下になります。)
$ vim /opt/homebrew-cask/Caskroom/android-studio/0.8.9/Android\ Studio.app/Contents/Info.plist
エラーの問題となっていたのは以下の箇所でした。

私の環境では1.6ではなく1.7がインストールされていたので、以下のように変更しました。

これで、正しく起動出来ました。

November 30, 2014

Laravel SocialiteでFacebookとTwitterの認証をしてみた

Laravel 5は現在開発中です。正式リリース版では、当記事の内容と異なる可能性があります。

Laravel 5では、Laravel Socialiteを別途インストールすることで、Facebook,Twitter,Google,GitHub等と連携した認証(OAuth,OAuth2)を、簡単に実装できるようになります。
https://github.com/laravel/socialite

細かな書き方などは、以下がとても参考になりました。
https://laracasts.com/series/whats-new-in-laravel-5/episodes/9

以下、Facebookを用いた認証の簡単なサンプルです。

まず、予めFacebook側で適当にアプリを作成して、発行されたキーをプロジェクトルートの .env と config/services.php に設定しておきます。尚、.envはバージョン管理せずに、各環境でこっそりと作成するべきファイルです。

.env
FACEBOOK_CLIENT_ID=xxxxxxxxxx
FACEBOOK_CLIENT_SECRET=yyyyyyyyyy
config/services.php
// 以下を追記
'facebook' => [
    'client_id' => getenv('FACEBOOK_CLIENT_ID'),
    'client_secret' => getenv('FACEBOOK_CLIENT_SECRET'),
    'redirect' => url('facebook/callback'),
],
.envで設定した値はgetenv()で取得できます。CLIからの実行時でも取得できます。この機能は"PHP dotenv"を用いているので、同様のことは他のフレームワーク等でも簡単にできます。
https://github.com/vlucas/phpdotenv

Laravel Socialiteをインストールします。
$ composer require laravel/socialite:~2.0@dev
config/app.phpのprovidersにLaravel Socialiteのプロバイダを追記します。
'Laravel\Socialite\SocialiteServiceProvider',
routes.phpに、Facebook認証用のコントローラを追加します。以下、一例。
$router->controllers([
    'facebook' => 'FacebookController',
]);
app/Http/Controllers/FacebookController.php を作成します。
<?php namespace App\Http\Controllers;

use Laravel\Socialite\Contracts\Factory as Socialite;

class FacebookController extends Controller {

    /**
     * @var Socialite
     */
    protected $socialite;

    public function __construct(Socialite $socialite)
    {
        $this->socialite = $socialite;
    }

    public function getLogin()
    {
        return $this->socialite->driver('facebook')->redirect();
    }

    public function getCallback()
    {
        $user = $this->socialite->driver('facebook')->user();

        dd($user);
    }
}
以下の流れで、ユーザ情報が正しく表示されれば成功です。

* facebook/loginにアクセスしてFacebook側にリダイレクトされる。
* Facebook側で認証してfacebook/callbackにリダイレクトされる。
* ユーザ情報が表示される。

また、以下の形で、scopesの指定も出来ました。
return $this->socialite->driver('facebook')->scopes(['user_friends'])->redirect();
同じ流れで、Twitterの認証もうまくいきました。その他、GithubProviderやGoogleProviderも用意されているので、これらも同じような方法で上手くいくのかなと思います。
https://github.com/laravel/socialite/tree/master/src/Two

標準では用意されていないサービスでも、これらを参考にして簡単に自前できそうですね。

Laravel 5のイベントリスナのアノテーションを試してみた

Laravel 5は現在開発中です。正式リリース版では、当記事の内容と異なる可能性があります。

Laravel 5では、ルーティングだけでなく、イベントリスナもアノテーションで定義できるようになります。

細かな書き方などは、以下がとても参考になりました。
http://mattstauffer.co/blog/laravel-5.0-event-annotations

以下、サンプルです。

適当な場所に適当なクラスを作成します。
<?php namespace App\EventListeners;

use Illuminate\Contracts\Logging\Log;

class Foo
{
    protected $log;

    /**
     * @param  Log $log
     */
    public function __construct(Log $log)
    {
        $this->log = $log;
    }

    /**
     * @Hears("bar.baz")
     *
     * @param  mixed $params
     */
    public function qux($params)
    {
        $this->log->debug(print_r($params, true));
    }
}
$this->log->debug() の箇所は、Laravel 5のContractsという仕組みを用いてみましたが、ファサードを用いて\Log::debug()でも同じです。

ポイントは @Hears("bar.baz") の箇所で、従来の書き方だと以下になります。
\Event::listen('bar.baz', 'App\EventListeners\Foo@qux');
次に app/Providers/EventServiceProvider.php を編集して protected $scan をオーバーライドします。値は配列で、アノテーションをスキャンしたいクラス名を書き並べます。
protected $scan = [
    '\App\EventListeners\Foo',
];
php artisan event:scan コマンドでスキャンします。
$ php artisan event:scan
Events scanned!
storage/framework/events.scanned.php がそれっぽい内容になっていれば正しくスキャンされています。
<?php 

$events->listen(array (
  0 => 'bar.baz',
), 'App\EventListeners\Foo@qux');
また、ルーティングのアノテーションと同様に app/Providers/EventServiceProvider.php の protected $scanWhenLocal をオーバーライドしてtrueを設定すると、環境値が"local"の場合に限って、リクエストの度に自動スキャンしてくれるようになりました。
protected $scanWhenLocal = true;
適当な場所でイベントをfire()してみます。
\Event::fire('bar.baz', [['xxx', 'yyy']]);
ログにパラメータが書き出されていれば成功です。
storage/logs/laravel-{yyyy-mm-dd}.log
[2014-11-30 00:10:52] local.DEBUG: Array
(
    [0] => xxx
    [1] => yyy
)
ルーティングのアノテーションと同様、デプロイ時のスキャン忘れとかには注意が必要なのかなと思いました。

November 25, 2014

Laravel 5のルーティングのアノテーションを試してみた

Laravel 5は現在開発中です。正式リリース版では、当記事の内容と異なる可能性があります。

Laravel 4ではルーティングをroutes.phpで定義しますが、Laravel 5からはコントローラのアノテーションでも定義できるようになります。

細かな書き方などは、以下がとても参考になりました。
http://mattstauffer.co/blog/laravel-5.0-route-annotations

以下、簡単に試してみました。

コントローラを適当に作成します。
$ php artisan make:controller FooController --plain
メソッドとアノテーションを適当に書いてみます。
app/Http/Controllers/FooController.php
<?php namespace App\Http\Controllers;

use App\Http\Controllers\Controller;

class FooController extends Controller {

    /**
     * @Get("foo/{id}", as="foo.get")
     */
    public function getExample($id)
    {
        //
    }

    /**
     * @Post("foo/{id}", as="foo.post")
     */
    public function postExample($id)
    {
        //
    }
}
app/Providers/RouteServiceProvider.php を編集して protected $scan をオーバーライドします。値は配列で、アノテーションをスキャンしたいコントローラ名を書き並べます。
class RouteServiceProvider extends ServiceProvider {

    protected $scan = [
        '\App\Http\Controllers\FooController',
    ];
php artisan route:scan コマンドでスキャンします。
$ php artisan route:scan
Routes scanned!
php artisan route:list コマンドで確認してみます。
$ php artisan route:list
+--------+-------------------+----------+------------------------------------------------+------------+
| Domain | URI               | Name     | Action                                         | Middleware |
+--------+-------------------+----------+------------------------------------------------+------------+
|        | GET|HEAD foo/{id} | foo.get  | App\Http\Controllers\FooController@getExample  |            |
|        | POST foo/{id}     | foo.post | App\Http\Controllers\FooController@postExample |            |
+--------+-------------------+----------+------------------------------------------------+------------+
正しくスキャンされたようです。尚、スキャン内容は storage/framework/routes.scanned.php に保存されていました。

routes.php で同様の定義をするなら以下です。
$router->get('foo/{id}', ['as' => 'foo.get', 'uses' => 'FooController@getExample']);
$router->post('foo/{id}', ['as' => 'foo.post', 'uses' => 'FooController@postExample']);
また、app/Providers/RouteServiceProvider.php の protected $scanWhenLocal をオーバーライドしてtrueを設定すると、環境値が"local"の場合に限って、リクエストの度に自動スキャンしてくれるようになりました。
protected $scanWhenLocal = true;

routes.phpはどうしても肥大化しやすく、その点は解消しそうな気がします。参考記事を見ると @Where で正規表現によるマッチングにも対応しているようですね。更に、アノテーションはメソッドに対してだけでなく、コントローラに対しても書けるようで
@Resource
@Controller
等も記載されています。

ただ、routes.phpに書くと、before/afterフィルタ(Laravel 5ではMiddlewareという仕組みに置き換わります。)が一目瞭然で、その辺りで好き嫌いが別れそうな印象を受けました。また、アノテーション式だと、デプロイ時のスキャン忘れや、DB値を用いた動的なルーティングを定義する際などに注意が必要かもしれません。

最後に、Laravel 5ではルーティングのキャッシュが可能です。
$ php artisan route:cache
これは、routes.php式にもアノテーション式にも対応していました。キャッシュ内容は(少なくとも今現在は) config/cache.phpのdriver値に関わらず storage/framework/routes.php に保存されるようです。

November 24, 2014

Laravel 5のartisan schedule:runコマンドで定期実行するタスクをPHP側で管理する

Laravel 5は現在開発中です。正式リリース版では、当記事の内容と異なる可能性があります。

Laravel 5では、定期実行されるタスク(要はcron)を、PHP側で一元的に管理できるようになるみたいです。正確には、1 cron設定でNタスクを管理できる。といった感じでしょうか。

尚、この機能は、以下のライブラリに依存しているようです。
https://github.com/mtdowling/cron-expression

細かな書き方などは、以下がとても参考になりました。
http://laravel-news.com/2014/11/laravel-5-scheduler/
http://mattstauffer.co/blog/laravel-5.0-event-scheduling

以下、簡単なサンプルです。

適当なコマンドクラスを作ります。内容は、実行された時刻を標準出力するだけです。
$ php artisan make:console ScheduleExampleCommand --command=schedule:example
app/Console/Commands/ScheduleExampleCommand.php
<?php namespace App\Console\Commands;

use Illuminate\Console\Command;

class ScheduleExampleCommand extends Command {

    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'schedule:example';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command schedule:example';

    public function fire()
    {
        echo sprintf('%s fired at %s', $this->name, date('Y-m-d H:i:s'));
    }
}
app/Console/Kernel.php の protected $commands に、作成したコマンドクラスを登録します。
<?php namespace App\Console;

use Exception;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel {

    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        'App\Console\Commands\ScheduleExampleCommand',
    ];
同様に、app/Console/Kernel.php のschedule()メソッドでスケジューリングします。
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('schedule:example')
            ->cron('*/1 * * * * *')
            ->sendOutputTo(storage_path('logs/schedule.example.log'));
    }
cron()メソッドに、お馴染みの書式で実行間隔を指定しています。今回は、1分毎です。5分毎とかならcron()ではなくeveryFiveMinutes()とかでOKです。

参考: https://github.com/laravel/framework/blob/master/src/Illuminate/Console/Scheduling/Event.php

また、sendOutputTo()メソッドで、標準出力の保存先を指定しています。実行結果メールの送信先を指定するemailOutputTo()とかもあるみたいです。

php artisan schedule:run コマンドで、正しく実行されるか確認してみます。
$ php artisan schedule:run
Running scheduled command: /usr/bin/php5 artisan schedule:example > /home/vagrant/Code/example/storage/logs/schedule.example.log 2>&1 &
storage/logs/schedule.example.log が正しく出力されていれば成功です。どうも、今現在は追記式ではないようですね。実行の度に同一のログファイル名を出力先に指定すれば、毎回、新たなログファイルが生成されてしまいます。この辺は、そのうち追記式もできるようになる気がする。(根拠は無い。)

最後に php artisan schedule:run コマンドをcron設定してみます。Homesteadで確認しています。
$ sudo vim /etc/cron.d/schedule-example

# 以下を記述
*/1 * * * * vagrant /usr/bin/php /home/vagrant/Code/example/artisan schedule:run
storage/logs/schedule.example.log が毎分正しく更新されれば成功です。
schedule:example fired at 2014-11-23 16:43:21
cron設定は数が増えてくると、いつどのようなタスクが実行されているか把握するのが面倒な時がありますが、この方法なら一目瞭然に管理できそうですね。