Laravel 4 + Sentry 2でデフォルトの外部キー名を変更する
前回の続きです。
前回のエントリでは、Sentryで使用するテーブル「users」「groups」「users_groups」「throttle」を、「my_users」「my_groups」「my_users_groups」「my_throttle」に変更しました。しかし、カラム名は変更しなかったので、「my_users_groups」「my_throttle」テーブルから「my_users」「my_groups」テーブルに対して張られる外部キーの名前がデフォルトの「user_id」「group_id」のままになっており、Eloquentの命名規約(モデル名 + id)とずれてしまっていました。そこで今回は外部キーの名前をそれぞれ「my_user_id」「my_group_id」に変更します。
手順
1. Userモデル、Groupモデルの外部キーの命名規約を変更する
「my_users_groups」テーブルから「my_users」「my_groups」テーブルに対して張られる外部キーの命名規約を変更するのは簡単です。SentryのEloquentでは、UserモデルのgroupsメソッドでGroupモデルに対する多対多のリレーションを、GroupモデルのusersメソッドでUserモデルに対する多対多のリレーションをそれぞれ定義しています。これらのメソッドをオーバーライドすることで外部キーの命名規約を変更できます。
app/models/MyUser.php
<?php class MyUser extends Cartalyst\Sentry\Users\Eloquent\User { protected $table = 'my_users'; public function groups() { return $this->belongsToMany(static::$groupModel, static::$userGroupsPivot, 'my_user_id', 'my_group_id'); } }
belongsToManyメソッドの第3引数、第4引数に関係するキー名を渡します。
app/models/MyGroup.php
<?php class MyGroup extends Cartalyst\Sentry\Groups\Eloquent\Group { protected $table = 'my_groups'; public function users() { return $this->belongsToMany(static::$userModel, static::$userGroupsPivot, 'my_group_id', 'my_user_id'); } }
こちらも同様です。
2. Throttleモデルの外部キーの命名規約をオーバーライドする
「my_throttle」テーブルから「my_users」テーブルに対して張られる外部キーの命名規約を変更するのは少し面倒です。vendor/cartalyst/sentry/src/Cartalyst/Sentry/Throttling/Eloquent/Provider.phpのfindByUserメソッド内で「user_id」という文字列がハードコーディングされているためです。
これを回避するために、app/start/global.phpなどに下記を追加し、findByUserメソッドをオーバーライドして、「user_id」を「my_user_id」に置換します*1。
app/start/global.php
<?php ... $app = app(); $app['sentry.throttle'] = $app->share(function ($app) { $model = $app['config']['cartalyst/sentry::throttling.model']; return new MyThrottleProvider($app['sentry.user'], $model); }); use Cartalyst\Sentry\Users\UserInterface; class MyThrottleProvider extends Cartalyst\Sentry\Throttling\Eloquent\Provider { public function findByUser(UserInterface $user, $ipAddress = null) { $model = $this->createModel(); $query = $model->where('my_user_id', '=', ($userId = $user->getId())); if ($ipAddress) { $query->where(function($query) use ($ipAddress) { $query->where('ip_address', '=', $ipAddress); $query->orWhere('ip_address', '=', NULL); }); } if ( ! $throttle = $query->first()) { $throttle = $this->createModel(); $throttle->my_user_id = $userId; if ($ipAddress) $throttle->ip_address = $ipAddress; $throttle->save(); } return $throttle; } }
あとは「my_users_groups」テーブルのときと同じように、EloquentのリレーションをオーバーライドすればOKです。
ThrottleモデルのuserメソッドでUserモデルに対する1対多のリレーションが定義されているので、MyThrottleモデルでuserメソッドをオーバーライドします。
app/models/MyThrottle.php
<?php class MyThrottle extends Cartalyst\Sentry\Throttling\Eloquent\Throttle { protected $table = 'my_throttle'; public function user() { return $this->belongsTo('MyUser', 'my_user_id'); } }
belongsToメソッドの第2引数に外部キー名を渡します。
3. マイグレーションを変更する
「my_users_groups」「my_throttle」テーブルのマイグレーションをエディタで開き、カラム名を「user_id」「group_id」から「my_user_id」「my_group_id」に変更します。
app/database/migrations/2013_12_19_091459_create_my_users_groups.php
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateMyUsersGroups extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('my_users_groups', function(Blueprint $table) { $table->integer('my_user_id')->unsigned(); $table->integer('my_group_id')->unsigned(); // We'll need to ensure that MySQL uses the InnoDB engine to // support the indexes, other engines aren't affected. $table->engine = 'InnoDB'; $table->primary(array('my_user_id', 'my_group_id')); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('my_users_groups'); } }
app/database/migrations/2013_12_19_091441_create_my_throttle.php
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateMyThrottle extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('my_throttle', function(Blueprint $table) { $table->increments('id'); $table->integer('my_user_id')->unsigned(); $table->string('ip_address')->nullable(); $table->integer('attempts')->default(0); $table->boolean('suspended')->default(0); $table->boolean('banned')->default(0); $table->timestamp('last_attempt_at')->nullable(); $table->timestamp('suspended_at')->nullable(); $table->timestamp('banned_at')->nullable(); // We'll need to ensure that MySQL uses the InnoDB engine to // support the indexes, other engines aren't affected. $table->engine = 'InnoDB'; $table->index('my_user_id'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('my_throttle'); } }
このあと上記のマイグレーションを実行すればOKです。
*1:もう少しスマートな方法はないものか。
Laravel 4 + Sentry 2でデフォルトのテーブル名を変更する
LaravelでSentryを使う際に、Sentryが使用するテーブル名を変更する方法についてまとめます。
Sentryはデフォルトでは「users」「groups」「throttle」「users_groups」の4つのテーブルを使用しますが、SentryのEloquentモデルを継承したモデルを作成することで、Sentry本体のソースコードを書き換えることなく使用するテーブル名を変更することができます。
前提
Sentry 2のインストール手順(https://cartalyst.com/manual/sentry/installation/laravel-4)を実行している前提です。
手順
1. SentryのEloquentモデルを継承したモデルを作成する
app/config/packages/cartalyst/sentry/config.phpを見るとわかるのですが、Sentryはデフォルトで下記のEloquentモデルを使用しています。
- Cartalyst\Sentry\Users\Eloquent\User
- Cartalyst\Sentry\Groups\Eloquent\Group
- Cartalyst\Sentry\Throttling\Eloquent\Throttle
これらのモデルを継承したモデルを作成し、tableプロパティをオーバーライドすることで、Sentryで使用するテーブル名を変更できます。
app/models/MyUser.php
<?php class MyUser extends Cartalyst\Sentry\Users\Eloquent\User { protected $table = 'my_users'; }
app/models/MyGroup.php
<?php class MyGroup extends Cartalyst\Sentry\Groups\Eloquent\Group { protected $table = 'my_groups'; }
app/models/MyThrottle.php
<?php class MyThrottle extends Cartalyst\Sentry\Throttling\Eloquent\Throttle { protected $table = 'my_throttle'; }
上記のモデルでは、「users」「groups」「throttle」テーブルの代わりに「my_users」「my_groups」「my_throttle」テーブルを使うように指定しています。
app/config/packages/cartalyst/sentry/config.phpを編集して、作成したモデルを使うように変更します。また、user_groups_pivot_tableの設定値を変更することで、「users_groups」テーブルの名前も変更できます。
diff --git a/app/config/packages/cartalyst/sentry/config.php b/app/config/packages/cartalyst/sentry/config.php index 3cd7151..d5e1603 100644 --- a/app/config/packages/cartalyst/sentry/config.php +++ b/app/config/packages/cartalyst/sentry/config.php @@ -95,7 +95,7 @@ return array( | */ - 'model' => 'Cartalyst\Sentry\Groups\Eloquent\Group', + 'model' => 'MyGroup', ), @@ -120,7 +120,7 @@ return array( | */ - 'model' => 'Cartalyst\Sentry\Users\Eloquent\User', + 'model' => 'MyUser', /* |-------------------------------------------------------------------------- @@ -149,7 +149,7 @@ return array( | */ - 'user_groups_pivot_table' => 'users_groups', + 'user_groups_pivot_table' => 'my_users_groups', /* |-------------------------------------------------------------------------- @@ -186,7 +186,7 @@ return array( | */ - 'model' => 'Cartalyst\Sentry\Throttling\Eloquent\Throttle', + 'model' => 'MyThrottle', /* |--------------------------------------------------------------------------
2. マイグレーションを作成する
このままだとデータベースにテーブルがないので、マイグレーションを作成します。
下記のコマンドを実行します。
$ php artisan migrate:make create_my_users --table=my_users --create $ php artisan migrate:make create_my_groups --table=my_groups --create $ php artisan migrate:make create_my_throttle --table=my_throttle --create $ php artisan migrate:make create_my_users_groups --table=my_users_groups --create
app/database/migrationsディレクトリにマイグレーションが生成されます。生成されたマイグレーションをエディタで開き、upメソッドの中身をvendor/cartalyst/sentry/src/migrationsディレクトリにあるSentryのマイグレーションのupメソッドの中身と置き換えます。
app/database/migrations/2013_12_19_091344_create_my_users.php
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateMyUsers extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('my_users', function(Blueprint $table) { $table->increments('id'); $table->string('email'); $table->string('password'); $table->text('permissions')->nullable(); $table->boolean('activated')->default(0); $table->string('activation_code')->nullable(); $table->timestamp('activated_at')->nullable(); $table->timestamp('last_login')->nullable(); $table->string('persist_code')->nullable(); $table->string('reset_password_code')->nullable(); $table->string('first_name')->nullable(); $table->string('last_name')->nullable(); $table->timestamps(); // We'll need to ensure that MySQL uses the InnoDB engine to // support the indexes, other engines aren't affected. $table->engine = 'InnoDB'; $table->unique('email'); $table->index('activation_code'); $table->index('reset_password_code'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('my_users'); } }
他の3つのマイグレーションファイルについても同様に編集します。
すでにSentryのマイグレーションを実行している場合にはロールバックします。
$ php artisan migrate:rollback Rolled back: 2012_12_06_225988_migration_cartalyst_sentry_install_throttle Rolled back: 2012_12_06_225945_migration_cartalyst_sentry_install_users_groups_pivot Rolled back: 2012_12_06_225929_migration_cartalyst_sentry_install_groups Rolled back: 2012_12_06_225921_migration_cartalyst_sentry_install_users
今回作成したマイグレーションを実行します。
$ php artisan migrate Migrated: 2013_12_19_091344_create_my_users Migrated: 2013_12_19_091422_create_my_groups Migrated: 2013_12_19_091441_create_my_throttle Migrated: 2013_12_19_091459_create_my_users_groups
これでデータベースに「my_users」「my_groups」「my_throttle」「my_users_groups」テーブルが作成されます。
このままだとロールバック時にclass not foundエラーが発生してしまうので、下記コマンドを実行します。
$ composer.phar dump-autoload Generating autoload files
これでロールバックもできるようになります。
CodeIgniterアプリをデプロイするときのCapistranoレシピ
CodeIgniterアプリをデプロイするときのCapistranoレシピを考えてみた。
deploy.rb
set :application, "my.domain.com" set :deploy_to, "/home/httpd/#{application}" set :repository, "http://path_to_myproject" set :scm, :subversion set :use_sudo, false role :web, "myhostname" after "deploy", "deploy:create_app_symlink" after "deploy:setup", "deploy:chown_log_dir" namespace :deploy do task :create_app_symlink, :roles => :web do run "rm -f #{deploy_to}/application; ln -s #{deploy_to}/current/application #{deploy_to}/application" run "rm -f #{deploy_to}/html; ln -s #{deploy_to}/current/html #{deploy_to}/html" run "rm -f #{deploy_to}/system; ln -s #{deploy_to}/current/system #{deploy_to}/system" end task :chown_log_dir, :roles => :web do run "sudo chown httpd #{deploy_to}/shared/log" end end
このレシピを使ってcap deploy:setup、cap deployを実行すると、デプロイ先に下記のディレクトリ構成でアプリケーションがデプロイされます。
$ tree -L 3 /home/httpd/my.domain.com/ /home/httpd/my.domain.com/ ├── application -> /home/httpd/my.domain.com/current/application ├── current -> /home/httpd/my.domain.com/releases/20130224145516 ├── html -> /home/httpd/my.domain.com/current/html ├── releases │ └── 20130224145516 │ ├── Capfile │ ├── REVISION │ ├── application │ ├── html │ ├── log -> /home/httpd/my.domain.com/shared/log │ ├── public │ ├── system │ └── tmp ├── shared │ ├── log │ ├── pids │ └── system └── system -> /home/httpd/my.domain.com/current/system 16 directories, 2 files
以下、補足です。
設定ファイルの置き場所について
CapfileをCodeIgniterのルートディレクトリ、deploy.rbをapplication/config/に置いています。
capifyコマンドの実行手順は下記のとおりです。
まずCodeIgniterのルートディレクトリで、application/を指定してcapifyコマンドを実行します。
$ capify application/ [add] writing 'application/Capfile' [add] writing 'application/config/deploy.rb' [done] capified!
実行後、Capfileをルートディレクトリに移動させます。
$ mv application/Capfile .
そして、Capfileを編集して、application/config/deploy.rbを読み込めるように変更します。
$ diff Capfile.org Capfile 4c4 < load 'config/deploy' # remove this line to skip loading any of the default tasks \ No newline at end of file --- > load 'application/config/deploy' # remove this line to skip loading any of the default tasks
application/config/に.phpファイルにまぎれて1つだけ.rbファイルがあって違和感がすごいですが、気にしないことにします。
CodeIgniterへのシンボリックリンクについて
create_app_symlinkタスクで、current/application、current/html、current/systemへのシンボリックリンクを作成しています。これはindex.phpをhtml/に配置して、ドキュメントルートとして/home/httpd/my.domain.com/html/を指定しているためです*1。
ドキュメントルートに直接/home/httpd/my.domain.com/current/html/を指定すればシンボリックリンクが不要になりますが、Capistranoを使う環境(productionサーバやstagingサーバ)と使わない環境(developサーバ)とでドキュメントルートを揃えておきたかったのでこうしました。
ログディレクトリについて
ログファイルは、CodeIgniterのデフォルトのログディレクトリ(application/logs/)ではなくcurrent/logに出力するようにしています。current/logはshared/log/へのシンボリックリンクで、デプロイ時にCapistranoが作成してくれます。ここにログを出力することで、すべての世代のアプリケーションでログファイルを共有可能になります。
config.phpでは、Capistranoを使わない環境も想定して、下記のようにAPPPATHからの相対パスを指定しています。
$ diff config.php.org config.php 194c194 < $config['log_path'] = ''; --- > $config['log_path'] = APPPATH . '../log/';
なお、上記レシピでは、deploy:setup実行時にshared/log/の所有者をApacheユーザに変更しています。
まとめ
CodeIgniterアプリをデプロイするときのCapistranoレシピを考えてみました。
まだlocalhostで検証しただけなので、実際に運用を始めるといろいろ改善点が出てくると思いますが、とりあえずこのレシピでCapistranoを使ってみようと思います。
*1:index.phpをhtml/に置いている理由は、ドキュメントルートにapplication/やsystem/があるのがセキュリティ上よろしくないためです。http://www.ci-guide.info/basic/install/
CodeIgniterでmemcachedに持続的接続する
CodeIgniterのmemcachedドライバを改造して、memcachedに持続的接続するというお話。
環境
CodeIgniterのバージョンは下記のとおりです。
CodeIgniter | 2.1.3 |
普通にmemcachedドライバを使う
普通にmemcachedドライバを使うと非持続的接続になります。
確認のため、memcachedに接続するだけの簡単なコントローラを書いてみます。
application/config/memcached.php
<?php $config['memcached'] = array( 'hostname' => '127.0.0.1', 'port' => 11211, 'weight' => 1 );
application/controllers/test.php
<?php class Test extends CI_Controller { public function index() { $this->load->driver('cache'); $this->cache->memcached->save('foo', 'bar', 10); echo $this->cache->memcached->get('foo'); } }
index.php/testに10回リクエストを送信し、netstatでWebサーバからmemcachedへのコネクションを確認してみます。
$ ab -n 10 -c 1 http://$HOSTNAME/index.php/test > /dev/null 2>&1; netstat | awk '{print $5,$6}' | grep 11211 localhost.11211 TIME_WAIT localhost.11211 TIME_WAIT localhost.11211 TIME_WAIT localhost.11211 TIME_WAIT localhost.11211 TIME_WAIT localhost.11211 TIME_WAIT localhost.11211 TIME_WAIT localhost.11211 TIME_WAIT localhost.11211 TIME_WAIT localhost.11211 TIME_WAIT
リクエストのたびにmemcachedに接続しているため、コネクションが10個張られています。
memcachedとの通信はすで終了しているので、コネクションの状態はTIME_WAIT(終了確認待ち)になっています。
何が問題?
TIME_WAIT状態のコネクションは一定時間経つと消えるため、通常はこれで問題ありません。
しかし、アクセス数が非常に多いサイトなどの場合、短時間にアクセスが集中することで、TIME_WAIT状態のコネクションが大量発生してポートを使い切ってしまい、それ以上新しいコネクションを張れない状態になってしまうことがあります。(結果、TIME_WAITが消えてポートに空きが出るまで待つことになり、memcachedのレスポンスタイムが急激に悪化します。)
持続的接続する
そこで、system/libraries/Cache/drivers/Cache_memcached.phpを改造します。
まず、165行目を下記のように変更します。
-$this->_memcached = new Memcached(); +$this->_memcached = new Memcached('memcached_pool');
次に、167〜187行目を下記のようにif文で囲みます。
-foreach ($this->_memcache_conf as $name => $cache_server) -{ - ... -} +if (!count($this->_memcached->getServerList())) +{ + foreach ($this->_memcache_conf as $name => $cache_server) + { + ... + } +}
最終形です。
<?php ... $this->_memcached = new Memcached('memcached_pool'); if (!count($this->_memcached->getServerList())) { foreach ($this->_memcache_conf as $name => $cache_server) { if ( ! array_key_exists('hostname', $cache_server)) { $cache_server['hostname'] = $this->_default_options['default_host']; } if ( ! array_key_exists('port', $cache_server)) { $cache_server['port'] = $this->_default_options['default_port']; } if ( ! array_key_exists('weight', $cache_server)) { $cache_server['weight'] = $this->_default_options['default_weight']; } $this->_memcached->addServer( $cache_server['hostname'], $cache_server['port'], $cache_server['weight'] ); } } ...
これでmemcachedに持続的接続することが可能になり、同じサーバプロセスが受け付けたリクエストであればコネクションを共有できるようになります。
つまり、memcachedへの接続に必要なポート数が高々サーバプロセス数になります。
確認しやすくするため、httpd.confに下記の1行を追記し、Apacheのサーバプロセス数を1に設定します。
ServerLimit 1
同じようにindex.php/testに10回リクエストを送信し、netstatしてみます。
$ ab -n 10 -c 1 http://$HOSTNAME/index.php/test > /dev/null 2>&1; netstat | awk '{print $5,$6}' | grep 11211 localhost.11211 ESTABLISHED
今度はコネクションが1個しか張られないようになっています。
コネクションを張りっぱなしにして再利用するため、コネクションの状態はESTABLISHED(接続確立)のままになっています。
Xdebug + VimでPHPのWebアプリをステップ実行してみた
PHPerの強力な味方var_dump。
しかし、あまりにもvar_dumpにばかり頼っていると、「えーマジvar_dump?」「var_dumpデバッグが許されるのは小学生までだよねー」などと罵られかねません。
そこで、今回は脱var_dumpデバッグを目指し、Xdebug + VimでPHPのWebアプリをステップ実行してみます。
debugger.vimのインストール
下記サイトからdebugger.zipをダウンロードします。
http://www.vim.org/scripts/script.php?script_id=1929
ダウンロードしたdebugger.zipを.vimディレクトリに展開します。
$ cd ~/.vim $ unzip ~/Downloads/debugger.zip
これでdebugger.vimのインストールは完了です。
debugger.vimが起動することを確認します。
まず、ファイル名の指定なしでVimを起動します。
$ vim
Vimが起動したら、fn + F5を押します。
waiting for a new connection on port 9000 for 5 seconds...
というメッセージが出力され9000ポートでListenが始まればインストールはOKです。
もしListenが始まらない場合は、VimのPythonインタフェースが有効になっていない可能性があります。
下記コマンドでPythonインタフェースが有効になっているか確認します。
$ vim --version | grep python +persistent_undo +postscript +printer -profile -python -python3 +quickfix
「+python」となっていれば有効になっています。
「-python」となっている場合は有効になっていないので、Vimを再インストールする必要があります。
下記コマンドでVimをアンインストール。
$ sudo port clean vim
そして「+python27」オプションをつけてVimを再インストール。
$ sudo port install vim +python27
インストールが終わるまで時間がかかるので、コーヒーでも飲みながら待っていてください。
インストールが終わったら、Pythonインタフェースが有効になっていることを確認します。
$ vim --version | grep python +python -python3 +quickfix +reltime -rightleft +ruby +scrollbind +signs リンク: /usr/bin/gcc-4.2 -L. -L/opt/local/lib -L/opt/local/lib -arch x86_64 -o vim -lncurses -liconv -lintl -framework Cocoa -L/opt/local/lib -fstack-protector -L/opt/local/lib/perl5/5.12.4/darwin-thread-multi-2level/CORE -lperl -lm -lutil -lc -L/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/config -lpython2.7 -framework CoreFoundation -u _PyMac_Error /opt/local/Library/Frameworks/Python.framework/Versions/2.7/Python -lruby -lobjc -L/opt/local/lib
今度はOKです。
再度Vimを起動してfn + F5を押してみてください。
今度はListenが始まるはずです。
Xdebugのインストール
Peclで一発で入ります。
$ sudo pecl install xdebug
インストールできたことを確認します。
$ pecl list Installed packages, channel pecl.php.net: ========================================= Package Version State xdebug 2.2.1 stable
[Zend] zend_extension=/usr/lib/php/extensions/no-debug-non-zts-20090626/xdebug.so [Xdebug] xdebug.default_enable = true xdebug.remote_enable = true xdebug.remote_host = localhost xdebug.remote_port = 9000 xdebug.remote_connect_back = false xdebug.remote_cookie_expire_time = 3600 xdebug.remote_handler = dbgp xdebug.remote_log = /var/tmp/xdebug.log
設定が有効になっていることを確認します。
$ php -r "phpinfo();" | grep -i xdebug
でXdebug関連の設定がずらずらと表示されれば、とりあえず設定はOKです。
ステップ実行
下記のサンプルプログラムをステップ実行してみます。
index.php
<?php $food = array( 'Japanease' => 'Sushi', 'Italian' => 'Pizza' ); $nation = 'Japanease'; echo "I love $nation[$food]. Because I am $nation.";
上記のindex.phpをApacheのドキュメントルートに配置してください。
Vimを起動して9000ポートでListenしている状態で、http://$HOSTNAME/index.php?XDEBUG_SESSION_STARTにリクエストを送信します。
URLに?XDEBUG_SESSION_STARTをつけないとApacheがXdebugを使用してくれないので注意してください。
リクエストを送信すると、デバッガが起動します。
fn + F3でステップ実行できます。
fn + F5でプログラムを最後まで実行すると、
ブラウザに「I love Sushi. Because I am Japanease.」と表示されます。
今度はブレークポイントを設定してみます。
Vimでindex.phpを開き、9行目にカーソルをあわせて「:Bp」押し、ブレークポイントを設定します。
index.phpを開いたままfn + F5を押してリクエストを送信すると、ブレークポイント付きでデバッガが起動します。
fn + F5で実行すると、ブレークポイントを設定した行でプログラムの実行が停止します。
ここで変数の値を参照してみます。
「,e」と入力すると、カーソルがWATCH_WINDOWに移動します。
「/*{{{1*/ => eval:」と聞かれるので、「$nation;」と入力しエンターを押すと、$nationの値を表示できます。
変数の値を書き換えることも可能です。
「,e」と入力しWATCH_WINDOWに移動したあと、今度は「$nation = 'Italian';」と入力します。
すると、$nationの値を書き換えることが可能です。
この状態でfn + F5を押してプログラムを実行すると、今度はブラウザに「I love Pizza. Because I am Italian.」と表示されます。
HerokuのCedar stackでパッケージの依存関係がキャッシュされる罠にはまってみた
タイトルのとおりです。
Cedar stackをよく使っている人は知っていて当然のことなのかもしれませんが、自分は見事に罠にはまりました。
node-twitterというライブラリを使ってTwitterのStreaming APIを使ったアプリを作るのに、最初以下のようにpackage.jsonを書いていました。
{ "name": "twitter-photo-montage" , "version": "0.0.1" , "private": true , "dependencies": { "express": "2.5.1" , "ejs": ">= 0.0.1" , "twitter": "0.1.17" , "socket.io": "0.8.7" } }
バージョン0.1.17はnpmでインストールできるtwitter-nodeの最新バージョンです。
しかし、バーション0.1.17にはStreaming APIにHTTPS接続するためのパッチが当たっておらず503エラーを返してしまっていたので、package.jsonを変更して、作者とは別の人がGitHubに公開しているパッチ版を使用することにしました。
{ "name": "twitter-photo-montage" , "version": "0.0.1" , "private": true , "dependencies": { "express": "2.5.1" , "ejs": ">= 0.0.1" , "twitter": "https://github.com/rocketlabsdev/node-twitter/tarball/master" , "socket.io": "0.8.7" } }
ところが、上記のpackage.jsonをpushしても503エラーが返ってくる事象が一向に改善しません。
おかしいと思い調べてみたところ、こんな記事を発見しました。
Use versioned github tarballs to update dependencies on heroku cedar stack — Gist
Cedar stackはパッケージの依存関係をキャッシュしていて、パッケージのバージョンが変わっていないとキャッシュしたものを使用するとのこと。
今回の場合、別の人が公開しているパッチ版が未パッチ版と同じバージョン0.1.17として公開されていたため、Cedar stack側が同じパッケージと見なして、キャッシュしたもの使っていたようです。
いったんtwitter-nodeのバーションを0.1.16に下げてpushしたあとで、再度パッチ版に変えてpushしたら、今度は無事パッケージが更新されました。
うーん。Herokuの日本語ドキュメントがほしい。
MinTTYの日本語まわりの設定まとめ
最近このエントリの影響を受けて、Cygwinを窓から投げ捨て、代わりにMinGW + MinTTYをインストールしました。
そのとき日本語まわりの設定で苦労したので、備忘録としてブログに残しておきます。
lsで日本語ファイル名を表示できるようにする
.profileをホームディレクトリに作成し、下記内容を記述します。
alias ls='ls --color=auto --show-control-chars' alias ll='ls -l' alias l='ls -CF'
MinTTYの起動時に~/.profileが読み込まれるように、下記のショートカットアイコンを作成しておくと便利です。
C:\MinGW\msys\1.0\bin\mintty.exe /bin/bash --login -i