RailsではInteger型のカラムに0始まりの値は登録できない話
これだけ書くと至極当然の話なのだが、ハマったので自戒を込めてメモ。
DB(MySQL)でinteger型のカラムがあり、そのカラムにデータを登録する際に検証ルールを付けたかったが、何故か入力値がvalidationでうまく処理されない。
例) 入力値: 00001 検証時: 1になる <- なぜ!?
原因はその入力値に対応するDBテーブル上のカラムがinteger型だったため、Railsが勝手に数値に変換してvalidation処理をしていた。
この時はXXXコードのような数字のみの項目か何かで、「5桁ちょうどであること」というルールを付けようとしたが、 入力値のパターンが00001の場合、1に変換されて、結果1桁となり検証エラーになってしまった。
development.logのParametersには{ "code" => "00001" }のように出てるから気づかなかった。。
同僚に相談して指摘され、気が付きました。というか、今まで気にしたことが無かった。。
Rubyの%wを使うときは型に気を付けよう
Railsのvalidationルールにinclusionがあるが、含まれる値をinに指定する際には注意が必要。
Rails Guideでも使っているが、配列を表現するために %wを使う場合、中身は文字列になるので、数字を検証したい場合は要注意。
例えば %w(0 1 2)
と [0, 1, 2]
は別物なのである。
%wは文字列扱いになるので、["0", "1", "2"]
ということになる。
読み読むとrubydocにも「文字列の配列を簡単に表現できる」と書いてある。 https://docs.ruby-lang.org/ja/latest/doc/spec=2fliteral.html#percent
まとめ
%wの使い方をよく知っていなかっただけなので、Railsのinclusionバリデーションが悪いわけではないが、 「数字の0,1のみ受け付ける」という検証が出来ずにはまったので、メモとして残しておく。
collection_check_boxesで登録済みの値にチェックをつけて表示する方法を調べた
Railsで多対多のリレーションのモデルをチェックボックスで表現する際にcollection_check_boxes
と言うViewヘルパー関数を使うと便利。
しかし編集時など、登録した値は当然チェックがついた状態で表示されないと困るが、どういう仕組みかの説明が見当たらなかったので調べた。
前提
下記のようなリレーションとなっていることを前提とする。
◆リレーション post <--> author_post <--> author ◆model定義 class Post < ApplicationRecord has_many :author_posts has_many :authors, through: author_posts ... class Author < ApplicationRecord has_many :author_posts has_many :posts, through: :author_posts ... class AuthorPost < ApplicationRecord belongs_to :post belongs_to :author
viewにcollection_check_boxesを下記のように設置
= collection_check_boxes(:post, :author_ids, Author.all, :id, :name) do |b| = b.label { b.check_box + b.text } formオブジェクトを使う場合 = f.collection_check_boxes(:author_ids, Author.all, :id, :name) do |b| = b.label { b.check_box + b.text } # ※viewにAuthor.allを直接記述するのはよくない
:id
と:name
は下記のように展開される
<input type="checkbox" value="4" checked="checked" name="post[author_ids][]" id="post_author_ids_4"> value : authorのID id : post_{第2引数の文字列}_{authorのID}
各項目の意味
各引数の役割はそれぞれ下記の通り
:post
更新対象のオブジェクト(のシンボル名)。form_forでは省略可。
:author_ids
更新対象のオブジェクトが持つメソッド(のシンボル名)。チェックボックスの各チェックに関連付けられるID。登録や更新の際にparameterに含まれるので、StrongParameterの対応が必要。
# app/controller/posts_controller.rb def post_params params.require(:post).permit(:title, { author_ids: [] }) end # ここで定義しないと「Unpermitted parameter: author_ids」となって登録や更新がされない。
:id
chekcboxのinputタグにてid属性に割り振られる値。かつ、登録済みの値かどうかを比較する対象となる属性(後述)。authorが持つメソッドを指定。
:name
チェックボックスに表示されるinputタグのテキストとなる値。authorが持つメソッドを指定。
# 例えばauthorが下記メソッドを持つ場合、:nameのところで代替可能 def honorific name + '様' end
まとめ
今回分かってよかったのが、第2、4引数(form_forの場合は第1、3引数)であるauthor_ids
と:id
の関係。
post(正確には第1引数のオブジェクト)はauthor_ids
というメソッドを持っており、その結果と第4引数id
を比較し、合致する場合にチェックボックスにチェックが付いた状態で画面表示される。(第4引数のidは、第3引数のそれぞれのオブジェクトが持つid。今回ではAuthor.allのそれぞれのauthorのauthor.id)
例えばpostに紐付くauthorのidが1,3,4の場合、post.author_ids = [1,3,4]となり、 Author.allの各authorのidと比較し、合致する場合にcheck属性が付与される。
上記を理解しておけば、登録した値が編集画面でチェックされていない、という場合にも原因の切り分けがしやすくなるので、覚えておこう。
bashで配列の扱い方
bashのshellスクリプトで配列の定義方法と取り出し方についてメモ。
下記のコードで確認します。
#!/bin/bash declare -A FRUITS; FRUITS=(["apple"]="100" ["banana"]="120" ["orange"]="150"); for f in ${FRUITS[@]}; do echo ${f}; done declare -a ANIMAL; ANIMAL=("dog" "cat" "bird"); for a in ${ANIMAL[@]}; do echo ${a}; done
実行結果
150 100 120 dog cat bird
解説
配列の定義
declare
は変数の宣言をするコマンドです。オプションには主に下記があります。
option | 意味 |
---|---|
-a | 配列として定義 |
-A | 連想配列として定義 |
-i | 整数型として定義 |
-r | 参照のみ可能な変数として定義 |
今回は連想配列と配列について確認しました。 例では変数FRUITSとANIMALを定義し、それぞれ連想配列と配列として扱います。
配列からデータの取り出し方
for文を使って配列からデータを取り出します。 取り出し方はどちらも同じですが、連想配列の場合、以下のようにするとkeyの値が取れます。
declare -A FRUITS; FRUITS=(["apple"]="100" ["banana"]="120" ["orange"]="150"); for f in ${!FRUITS[@]}; do # <- ! (エクスクラメーションマーク)を付ける echo ${f}; done # 結果 orange apple banana
Rspecで undefined method `validate_uniqueness_of' が出たので対応した件
とても久しぶりにRailsでRspecを思い出しながら書いていたら、
NoMethodError: undefined method `validate_uniqueness_of' for ...
が出て困って対応したときのメモ。 結論から書くと、Railsでよくあるバージョンの問題でした。
環境
※抜粋 rails (5.0.0.1) rspec-rails (3.5.2) shoulda-matchers (2.8.0) <=今回の肝
is_expected.to ensure_length_of
とか is_exepcted.to validate_uniqueness_of
などはshouda-matchers が提供しているテストコードを簡潔に書くためのgemですが、
使用しているRspecのバージョンによっては動かないことがあるようです。
(2016/11/07現在のshoulda-matchers最新バージョンは3.1.1、Rspecは3.5.2)
そこでshoulda-matchersのバージョンをいろいろ変えて試したところ、2.8.0ならエラーがなくSpecが通りました。
# Gemfile に下のようにバージョンを指定して bundle install gem 'shoulda-matchers', '~> 2.8.0'
結構、はまりました。
FuelPHPのDB操作で「mysqli::mysqli(): (HY000/2002): そのようなファイルやディレクトリはありません」が出た件
FuelPHPのマイグレーションを実行しようとしたら、次のエラーが出て失敗したので、その時の対処法を残しておきます。
$ oil refine migrate caught exception Fuel\Core\Database_Exception: mysqli::mysqli(): (HY000/2002): そのようなファイルやディレクトリはありません
環境
vagrant上に環境構築 php : 5.6.25 fuelphp : 1.8 mysql : 5.6.33
対応
エラーメッセージで検索すると、mysql.sockがphp.iniに指定していないと起こる、とのことだったので、指定してみます。
php.iniの編集前(no valueになっている) $ php --ri mysqli mysqli.default_socket => no value => no value mysql.sockの場所を確認し、下のように編集 $ vim /home/vagrant/.phpenv/versions/5.6.25/etc/php.ini mysqli.default_socket = /var/lib/mysql/mysql.sock 改めて確認 $ php --ri mysqli mysqli.default_socket => /var/lib/mysql/mysql.sock => /var/lib/mysql/mysql.sock マイグレーションを実行 $ oil r migrate Performed migrations for app:default: 001_create_form
mysql.sockを指定してもダメな場合もあるようです、今回はうまくいったので良かったです。
他のハマりポイントとしては、fuel/app/config/db.php
にてmysqliとPDOで記述が若干異なるため、間違えると上手く動かないようです。
# PDOの場合 return array( 'default' => array( 'type' => 'mysqli', 'connection' => array( 'dsn' => 'mysql:host=localhost;dbname=fuel_dev' 'username' => 'username', 'password' => 'password', ), ), );
# mysqliの場合 return array( 'default' => array( 'type' => 'mysqli', 'connection' => array( 'hostname' => 'hostname', 'username' => 'username', 'password' => 'password', ), ), );
参考にさせていただきました!
psqlのバージョンをあげた話
CentOS6系でyumでインストールしたPostgreSQLに psql
で操作すると、以下のような警告が毎回出るので、対応した手順をメモします。
# psql -U postgres psql (8.4.20, server 9.3.10) WARNING: psql version 8.4, server version 9.3. Some psql features might not work. Type "help" for help.
PostgreSQLのバージョンは9.3.10
postgres=# SELECT version(); version ---------------------------------------------------------------------------------------------------------------- PostgreSQL 9.3.10 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16), 64-bit (1 row)
原因は、psqlの古いモジュールが使われているせいみたいなので、 rpmの古いモジュールをアンインストールします。
# rpm -qa | grep postgres postgresql93-9.3.10-1PGDG.rhel6.x86_64 postgresql93-server-9.3.10-1PGDG.rhel6.x86_64 postgresql-libs-8.4.20-4.el6_7.x86_64 postgresql93-libs-9.3.10-1PGDG.rhel6.x86_64 postgresql93-contrib-9.3.10-1PGDG.rhel6.x86_64 postgresql-devel-8.4.20-4.el6_7.x86_64 postgresql-8.4.20-4.el6_7.x86_64
元から入っていた(?)8.4.20系のモジュールを削除します。
# rpm -e postgresql-devel-8.4.20-4.el6_7.x86_64 # rpm -e postgresql-8.4.20-4.el6_7.x86_64 # rpm -e postgresql-libs-8.4.20-4.el6_7.x86_64
psqlコマンドを使うと、パスが無くなってしまっているのでエラーになりました。
# psql -bash: /usr/bin/psql: No such file or directory
9.3.10系のパスは /usr/pgsql-9.3/bin
なので、あとはどういう形でもいいのでパスを通せば完成です。
今回は下記一行を新規作成したファイルに追記しました。
# vim /etc/profile.d/psql93.sh export PATH=$PATH:/usr/pgsql-9.3/bin
警告が出なくなりました。よかった。
# psql -U postgres psql (9.3.10) Type "help" for help.
vagrantで作った環境に元から8.4.20系のpostgresモジュールが入ってたのかも知れません(未確認。。)