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

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

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

PHPのpreg_matchについて

いろいろなところで利用されているpreg_matchについてはまったのでメモ。
今回はまったのはcodeigniterのクエリビルダで使われているところで、
nginxのエラーログから調査していたけど、ちゃんとした内容がみつからなかった。

・エラー内容
Warning: preg_match(): Compilation failed: regular expression is too large at offset XXXXX

なにやら最後の数字(恐らくByte)を超えるとpreg_matchが意図した動作をしないようで。
日本語情報のほとんどが「pcre.backtrack_limit」と「pcre.recursion_limit」の上限を上げると解消する、というもの。backtrackというのがどういった動作に影響する値かが良くわからなかったけど、今回の現象とは違うと思いつつ設定したけど案の定解決せず。

おそらくpcreのコンパイルオプションで「link size」という値をデフォルトの2から3以上にすると解決するのではないかというところまで調べたけど時間切れで諦めた。
そもそもで正規表現にかけてる文字数が多すぎるのが、誰が見てもイケてないという結論になったのでそこを修正した。

ということで今回のハマリで注意することは、

フレームワークでは結構使われているので、他人事ではない。

・上記warningが出た場合、マッチするはずであろうがなかろうがreturnは「0」になる。

・warningなので処理が続行されてしまうので、条件分岐では「x==0」のようにしない。
 (マッチしない場合も0、warningが出た場合も0なので意図した条件分岐にならない可能性が高い)

・使用上どうしても長い・大きい値を正規表現にかけないといけない場合はpcreをコンパイルし直す。

・pcreなのでpreg_match以外のpcre系関数でも同様の制限がかかる模様。

・特に他人が書いたコードでは要注意。

AWSでCentOSインスタンス作成メモ

どっかに書いてるかもしれないけど殴り書きメモ

AWSのWebコンソールで使えるcloud-initは実行ユーザはec2user。 ・マーケットプレースにあるCentOSにはec2userが居ない。 ・マーケットプレースにあるCentOSのcloud-init実行ユーザはcentos。 ・CentOS公式のAMI IDは一度適当にインスタンス立てると確認できた。(他に方法あるきがするけど・・・

何かあれば追記しよう

Phalcon 2 はまりどころメモ

あとで清書するよてい

phalcon1系から2になることで実装が純粋なC言語からZephirを挟んでいるようで、よく次のような事に巻き込まれる。

経験したことのあるタイミング:autoloder等で設定を読み込ませる、viewのsetvarする時
発生した事象1:「Parameter 'key' must be a string」的なエラー
発生した事象2:コンフィグ等、配列・連想配列で読み込ませた値が参照できない
原因:値を格納する際に可変変数を利用して実装しているため

可変変数使ってるの初めてみるんじゃないかって感じで理解するまで時間かかった。
連想配列の値を格納する際に、連想配列のkeyを可変変数で指定しており、そのkeyが数値だった場合、エラーがでたりkeyを指定して値が参照できなくなります。
可変変数で格納された連想配列はdumpとかすると次のような見えかたします。

(["0"] => 'aaa', ["1"] => '123')

ちょこっとしらべたところ、こういう形ではいっちゃうとforearchとかしないと中身とりだせない仕様っぽい? 実装箇所によってエラー出てくれるけど、場所によっては上記のような入りかたしちゃうこともあるので、注意。
英語できないからこっそりと修正待ち。