二度忘れた事を三度忘れないようにする

しがないフリーランスIT系エンジニア

PhalconのRoutingとViewの関係

独自にルーティングを定義する際に、公式docを参考(なので大部分を割愛)に以下のように定義しました。

<?php

use Phalcon\Mvc\Router;

$router = new Router(false);

$router->add( "/", [
"namespace"  => 'Appname\Controllers',
"controller" => "index",
"action"     => "route"
]);

return $router;

ここで「IndexController.php」の「routeAction」が参照する末端のViewは「app/views/index/route.volt」です。
しかしながら、上記ルーティングの「"controller" => "index",」で指定している「index」を「Index」(頭文字を大文字)とすると、参照しにいくViewは「app/views/Index/route.volt」となりました。
実行されるコントローラは大文字小文字関係なく「IndexController.php」が実行されるが、参照しにいくViewはルーティング時の文字列に影響する模様。「"action" => "route"」は大文字小文字区別されるのでちょっと注意が必要。

これはPHPのクラス名は大文字小文字の区別しなくても動作する仕様からくるものだと思われる。

Phalcon3 + PHP7 導入手順

気づいたらPhalcon3がリリースされていたので、PHP7と一緒に試してみました。

・環境
CentOS7(AWS)
PHP 7.0.10
Phalcon 3.0.1
nginx 1.8

$ sudo yum install epel-release centos-release-scl-rh
$ sudo yum install https://centos7.iuscommunity.org/ius-release.rpm
$ sudo yum install rh-nginx18 php70u-devel php70u-mysqlnd php70u-fpm gcc libtool pcre-devel php70u-opcache php70u-json
$ curl -s https://packagecloud.io/install/repositories/phalcon/stable/script.rpm.sh | sudo bash
$ sudo yum install php70u-phalcon
$ php -m
[PHP Modules]
phalcon

公式doc通りではコンパイルエラーが発生し解決できず、試行錯誤しているとphalconブログなるものがあって、そこのレポジトリを利用。なお、公式docでインストールしているパッケージだけでは読み込まれないのでphp70u-jsonを追加でインストールする必要があります。

blog.phalconphp.com

phalcon - Repositories | packagecloud

インストールは以上で完了なのであとはnginxとphp-fpmの設定を各々の環境に合わせてセットしてください。

Unicornをsystemdで管理する

Rails初心者が典型的なハマり方をしたと思うので戒めの意を込めて。

前提
CentOS 7系
・SCL版ruby2.3
・SCL版nginx1.8

nginxはscl版でも標準でsystemctlコマンドで制御できるので特に問題なし。

$ sudo systemctl enable(start) rh-nginx18-nginx

unicronは自分でserviceファイルを記述する必要があるが、sclのrubyを使っているので普通にかくと環境変数がことなることでエラーになります。

ということでserviceファイルは以下みたいな感じで記述

[Unit]
Description=Unicorn Server

[Service]
WorkingDirectory=/var/www/html/
SyslogIdentifier=unicorn
PIDFile=/var/www/html/tmp/pids/unicorn.pid
User=nginx
Restart=always

ExecStart=/usr/bin/scl enable rh-nodejs4 rh-ruby23 -- unicorn_rails -c config/unicorn.rb -E development -D
ExecStop=/usr/bin/kill -QUIT $MAINPID
ExecReload=/bin/kill -USR2 $MAINPID

しかし、ちゃんと起動しない。コマンド直打ちでは問題ないことはわかっているので、ユーザ周りを睨んで調査していると、gemで入れたアレコレがみつからないことがわかった。(経緯のメモを忘れた)

原因 「bundle install」でパスを未指定

これは完全にbundleの理解不足でした。パスを未指定で実行していたことで、カレントユーザのホームディレクトリにインストールされるため、systemdで実行した際に「色々ネーヨ」ってなってました。なので、パスを指定してインストールした以後は「bundle exec」をつけて各種コマンドを実行することでbundle installしたgem群の読み込みをよしなにしてくれた。

ということでserviceファイルのExecStartのコマンドを次に修正することで無事解決

ExecStart=/usr/bin/scl enable rh-nodejs4 rh-ruby23 -- bundle exec unicorn_rails -c config/unicorn.rb -E development -D

CarrierWaveを利用したModelの挙動について

幅を広げるのにRoRを勉強し直すのに簡単なアプリを作ってみたらはまったので、メモ。

・とりあえず
プロフィール的なページを構成するのに、メインとなるプロフィールテーブルとhas_oneで2つ別テーブルを関連づけしてました。画像は特定のディレクトリに保存する形をとって、ファイル名をプロフィールテーブルに保存をしようとした。

・そのまえに
RoRのhas_oneで更新をかけると、子テーブルのほうに続々と不要レコードがたまってきたので更新のタイミングで直前のレコードを削除するように、プロフィールモデルをいじってました。

ざっくり)after_initialize時に子テーブルのidを取得→after_commit時に削除

ネットで調べるとhas_manyであれば別の方法があるっぽいけど、うまく動作しなかったのでこうしました。

参考:
has_many側の不要なやつはdeleteして更新したい - Oh! My! Enter! 〜バッチを起動しようと勢いよくキーを叩いたら、それはシフトキーだった〜


・本題
プロフィール編集フォームにて、画像以外の項目で編集しても問題なかったのに画像をアップロードしようとするとエラーが出た。原因は上記のinitialize時のid取得でエラーが出ていたが、画像の有無で挙動が変わっていた。

・結果?
とりあえず「mount_uploader :image, ImageUploader」としたカラムを更新する時は再度initializeが発生する。(おそらく新しくインスタンスを生成しているのではと思う

なお、SQLの実行内容をみていると次のようになっていた。

# 画像無し
Started PATCH "/my_page" for 127.0.0.1 at 2016-08-22 02:48:31 +0000
Processing by UserProfilesController#update as HTML
  Parameters: {"utf8"=>"", -略- "commit"=>"Update User profile"}
  UserProfile Load (0.2ms)  SELECT  -略- LIMIT 1
# ここらへんでafter_initialize動作しているっぽい
  UseService Load (0.2ms)  SELECT  -略- LIMIT 1 # has_oneその1
  UseLang Load (0.3ms)  SELECT  -略- LIMIT 1  # has_oneその2
# ここらへんでafter_initializeが終わっているぽい
BEGIN
  SQL (0.3ms)  has_oneその1をアップデート(ここで古い情報がゴミレコードになる
  SQL (0.2ms)  has_oneその2をアップデート(ここで古い情報がゴミレコードになる
  UserProfile Exists (0.2ms)  親テーブルの重複チェック?
  UserProfile Exists (0.2ms)  親テーブルの重複チェック?
  SQL (0.2ms)  UPDATE 親テーブルのUserProfileで画像以外のカラムを編集
  SQL (0.2ms)  INSERT has_oneその1に更新情報を格納
  SQL (0.2ms)  INSERT has_oneその2に更新情報を格納
COMMIT
# after_commitが動作
  UseService Load (0.2ms)  SELECT  has_oneその1の古い情報を取得
BEGIN
  SQL (0.3ms)  DELETE has_oneその1の古い情報を削除
COMMIT
  UseLang Load (0.2ms)  SELECT  has_oneその2の古い情報を取得
BEGIN
  SQL (0.3ms)  DELETE has_oneその2の古い情報を削除
COMMIT
# after_commitが終了
Redirected to http://++++
Completed 302 Found in 29ms (ActiveRecord: 9.8ms)
# 画像有り
Started PATCH "/profile" for 127.0.0.1 at 2016-08-22 02:49:29 +0000
Processing by UserProfilesController#update as HTML
  Parameters: {"utf8"=>"", -略- "commit"=>"Update User profile"}
  UserProfile Load (0.2ms)  SELECT  -略- LIMIT 1
# ここらへんでafter_initialize動作しているっぽい
  UseService Load (0.2ms)  SELECT  -略- LIMIT 1 # has_oneその1
  UseLang Load (0.3ms)  SELECT  -略- LIMIT 1  # has_oneその2
# ここらへんでafter_initializeが終わっているぽい
BEGIN
  SQL (0.3ms)  has_oneその1をアップデート(ここで古い情報がゴミレコードになる
  SQL (0.2ms)  has_oneその2をアップデート(ここで古い情報がゴミレコードになる
  UserProfile Exists (0.2ms)  親テーブルの重複チェック?
  UserProfile Exists (0.2ms)  親テーブルの重複チェック?
  UserProfile Load (0.2ms)  SELECT  -略- LIMIT 1
# ここで再びafter_initializeが動作している?
  UseService Load (0.2ms)  SELECT  -略- LIMIT 1 # この時点ではuse_serviceはnil
  UseLang Load (0.2ms)  SELECT  -略- LIMIT 1 # この時点ではuse_langはnil
# ここで再び動作していたafter_initializeが終わっている?
  SQL (0.2ms)  UPDATE 親テーブルのUserProfileに画像ファイルのファイル名を格納
  SQL (0.2ms)  INSERT has_oneその1に更新情報を格納
  SQL (0.2ms)  INSERT has_oneその2に更新情報を格納
COMMIT
# after_commitが動作
  UseService Load (0.2ms)  SELECT  has_oneその1の古い情報を取得
BEGIN
  SQL (0.3ms)  DELETE has_oneその1の古い情報を削除
COMMIT
  UseLang Load (0.2ms)  SELECT  has_oneその2の古い情報を取得
BEGIN
  SQL (0.3ms)  DELETE has_oneその2の古い情報を削除
COMMIT
# after_commitが終了
Redirected to http://++++
Completed 302 Found in 40ms (ActiveRecord: 10.5ms)

パーフェクトRoR買おうかな。。。

スクリプト言語の文法比較(海外サイト引用)

個人的には何々言語が優れてるからやるべきだ!みたいな考えは無く、一長一短であったりするので雑食です。

で、PHPメインでやってきて昔rubyをかじってそれっきりになっていたので、久しぶりに触り直してたとき、PHPではこうやってたやつをrubyでは・・・みたいなのばかり発生して、とりあえず文法がしりたいので調べたらやっぱりあったので、ブックマーク。別サイト発見次第追記予定

Scripting Languages I: Node.js, PHP, Python, Ruby (Sheet One) - Hyperpolyglot

細かい動作とかを確認する場合は本とかネットのドキュメントみるけど、とりあえず文法だけであればこういうやつのほうが使い勝手が良いです。

SoftwareCollections(SCL)について

CentOS6 + node.js5系 を導入する手順 - 二度忘れた事を三度忘れないようにする

以前の記事で少し調べたSCLについて、もう少しだけ調べたのでメモ。

RH中の人のスライドから

www.slideshare.net

独自にrpmを作ったりリポジトリ管理するのは大きな手間なので、RHユーザはOSサポートより短いものの公式にサポートされるモノを利用できるのは大きいですね。CentOSも同等のレポジトリが用意されているので、利用していこう。

なお、記事作成段階のCentOSで利用する方法

$ yum install centos-release-scl

これだけでSCL用リポジトリが追加、有効になります。あとは利用したいパッケージをインストールして「scl enable 任意のパッケージ '任意のコマンド'」で利用する。

以前は古いdevtoolsetを利用していたのでwgetしてましたが、サポート中のモノであればCentOSデフォルトのリポジトリにSCLがあるのでそれをインストールできます。(現時点ではdevtoolset3,4を入れれます

SCLだと若干使い勝手が変わりますが、NginxやPHP-FPM等はchkconfig、systemdのサービスに登録されるのでモノによっては大差なく利用することができます。

なお、SCLでは一般的なディレクトリ構成ではなく、SCL用のディレクトリ構造になる。

  • コレクション:/opt/rh/コレクション名
  • 設定ファイル:/etc/opt/rh/コレクション名
  • ログファイル:/var/opt/rh/コレクション名

例1)rh-ruby23をインストールした場合
* コレクション:/opt/rh/rh-ruby23
* rubyコマンド:/opt/rh/rh-ruby23/root/usr/bin/ruby
* 設定ファイル:/etc/opt/rh/rh-ruby23 (デフォルトで幾つかディレクトリはあるが、特にファイルは無い
* ログファイル:/var/opt/rh/rh-ruby23 (デフォルトで幾つかディレクトリはあるが、特にファイルは無い

例2)rh-nginx18をインストールした場合
* コレクション:/opt/rh/rh-nginx18
* nginxコマンド:/opt/rh/rh-nginx18/root/usr/sbin/nginx
* 設定ファイル:/etc/opt/rh/rh-nginx18/nginx/nginx.conf
* ログファイル:/var/opt/rh/rh-nginx18/log/nginx/access.log (nginx.confに記載されている
* serviceファイル:/usr/lib/systemd/system/rh-nginx18-nginx.service

CentOS6 + node.js5系 を導入する手順

先日CentOS6系(RHEL6系)にnode.js5.1以上を入れようとしたらコンパイルに失敗したので、それを解決する手順。

まず、原因はCentOS6系に入っているg++が古いことに起因しているので、新しいモノをいれてやります。 ただし、ここで既存のモノと入れ替えてしまうと別のところに影響が出てしまうので、個別にいれてやります。

今回はSoftwareCollectionsを利用した方法を採用。

root# cd  /etc/yum.repos.d/
root# wget http://people.centos.org/tru/devtools-2/devtools-2.repo
root# yum install devtoolset-2-gcc devtoolset-2-binutils devtoolset-2-gcc-c++ devtoolset-2-gcc-gfortran

wgetしてくる開発用リポジトリは色々あるので、この限りではないですが、centosドメインで提供しているのでこれにしました。 yumでインストールできるので依存関係ももちろんやってくれます。

ここからnode.jsですが、nodebrewを利用したので、そこは公式を参照して「nodebrew -v」が効くようにしてください。 あとは次のコマンドで無事とおるはず。

user# nodebrew install v5.3.0

シングルクオートでかこったコマンドに対してdevtoolset-2でインストールしたモノが適用されます。次のような感じになるので既存環境にも影響しないはず。

user# g++ -v // 4.4.x
user# scl enable devtoolset-2 'g++ -v' // 4.8.x
user# g++ -v // 4.4.x

毎回シングルクオートでくくるのは面倒で環境を整える間だけ有効にする場合、次のようにシェルを起動してやるのが便利と思います。

user# g++ -v // 4.4.x
user# scl enable devtoolset-2 zsh
user# g++ -v // 4.8.x