hotoolong's blog

プログラムのことやエンジニアリングに関することを記事にしています。

CIでbundle install するときには --clean をつけると無駄にキャッシュされない

タイトルどおりなのですが、 目からウロコだったので、メモとして残しておきます。

Circle CI 2.0 でstore_cache、 restore_cache を使って、Bundler の gem をキャッシュしていたので、 とても参考になります。

ローカル環境でも --clean を付けてもいいのかもしれないですが、 ブランチの切り替え時に毎回インストールしないといけなかったりが多発するようであれば 時々claenしてあげるのがいいのではないでしょうか。

blog.yuyat.jp

MySQLのgroup_concatをつかって複数の項目を1行に並べる

1対多のテーブル構成を多になっているテーブルの情報をgroup_concatを使って表示したくなるケースがあったのですが、 単純に1カラムのデータだけを表示する場合はそのまま使えばいいのですが、 複数を一気に選択することができず、副問合せして取得しました。

usersテーブル、user_optionsテーブルがあるとして 普通にgroup_concatを使ってオプションの名前を登録順に取得するケースは以下になります。

select
 users.id,
 group_concat(distinct user_options.name order by user_options.created_at separator ', ')
from
 users
left outer join user_options on user_options.user_id = users.id

これに対してuser_optionsの複数の項目(nameと登録日)を追加したい場合は joinしてるテーブルを副問合せしてしまって以下のようにしました。

select
 users.id,
 group_concat(distinct user_options.name_created_at order by user_options.created_at separator ', ')
from
 users
left outer join (
  select
   id,
   concat(id, '(', date_format(convert_tz(created_at, '+0:00','+9:00'), '%Y-%m-%d'), ')') as name_created_at,
   created_at
  from
   user_options
)
user_options on user_options.user_id = users.id

このようにすることで,区切りで名称(登録日)の情報が1行で表示されるようになります。 力技なのであまり参考にならないかもしれないですが、表示させるのに困ったときには使ってみて下さい。

Capybaraでselect時に同じ名称のvalueがありエラーになってしまう

RSpecでCapybaraのテストをしてたのですが、

select '東京都', from: '都道府県'

と記載している箇所で

Capybara::Ambiguous:
   Ambiguous match, found 2 elements matching visible option "東京都" within #<Capybara::Node::Element tag="select" path="/html/body/div/div/div[3]/div[2]/div/div/form/div[8]/select">

のようなエラーが出てしまいました。

FactoryBotの記載方法が悪かったようで、同じ東京都が2つできてしまったのですが、 改善方法としてはいくつかあります。

1点目としては
特に同じ名称でどちらも使っても問題ない場合

select '東京都', from: '都道府県', match: :first

とspecを修正することで免れることができます。 これはシンプルですね。

2点目としては FactoryBotで同じ名称を作りたく無いケース

FactoryBot.define do
    factory :prefecture do
      id 13
      sequence(:name) { |n| "東京都-#{n}" }

とsequenceを利用するとnにインクリメントされた数値が代入されまwす。 このように記載すると上手く行きますね。

本質的な問題として rails_spec.rb に固定させるデータを beforeで作ってしまうというのもいいのかもしれませんが、 今回はそれ以外の対処方法を考えてみました。

imagemagickのエラーでRSpecが落ちる

RSpecに画像アップロードのテストが含まれているのですが、 軒並みバリデーションエラーになってました。

        ActiveRecord::RecordInvalid: バリデーションに失敗しました。 プロフィール画像MiniMagickがファイルを処理できませんでした。画像を確認してください。エラメッセー...279.
        identify: no decode delegate for this image format `JPEG' @ error/constitute.c/ReadImage/509.

あまり見に覚えがなかったのですが、最近brewで痛い目に合ってるので、Updated Formulaeの巻き添えを食ったのかとおもい

brew upgrade imagemagick

しておきました。

brew info imagemagick
imagemagick: stable 7.0.7-28 (bottled), HEAD
Tools and libraries to manipulate images in many formats
https://www.imagemagick.org/
/usr/local/Cellar/imagemagick/7.0.5-4 (1,522 files, 22.6MB)
  Poured from bottle on 2017-04-03 at 12:58:14
/usr/local/Cellar/imagemagick/7.0.7-28 (1,526 files, 23.2MB) *
  Poured from bottle on 2018-03-30 at 20:43:27
From: https://github.com/Homebrew/homebrew-core/blob/master/Formula/imagemagick.rb

再度 spec を実行するとうまくいきました。怖いですね。。

awscli再インストール時のエラー対応

前回の記事でbrewpythonを入れ直したのが影響なのか、awsコマンドが使えなくなりました。

/usr/local/bin/aws: /usr/local/opt/python/bin/python2.7: bad interpreter: No such file or directory

というエラーが出ていました。pipでコマンド確認したところ。pipのパスが通ってなかったので 再インストールしてみました。

sudo easy_install pip
sudo easy_install --upgrade pip

awscliもインストールし直しました。

sudo pip install awscli

すると変なエラーが

    DEPRECATION: Uninstalling a distutils installed project (six) has been deprecated and will be removed in a future version. This is due to the fact that uninstalling a distutils project will only parti
ally uninstall the project.
    Uninstalling six-1.4.1:
Exception:
Traceback (most recent call last):
  File "/Library/Python/2.7/site-packages/pip-9.0.2-py2.7.egg/pip/basecommand.py", line 215, in main
    status = self.run(options, args)
  File "/Library/Python/2.7/site-packages/pip-9.0.2-py2.7.egg/pip/commands/install.py", line 342, in run
    prefix=options.prefix_path,
  File "/Library/Python/2.7/site-packages/pip-9.0.2-py2.7.egg/pip/req/req_set.py", line 778, in install
    requirement.uninstall(auto_confirm=True)
  File "/Library/Python/2.7/site-packages/pip-9.0.2-py2.7.egg/pip/req/req_install.py", line 754, in uninstall
    paths_to_remove.remove(auto_confirm)
  File "/Library/Python/2.7/site-packages/pip-9.0.2-py2.7.egg/pip/req/req_uninstall.py", line 115, in remove
    renames(path, new_path)
  File "/Library/Python/2.7/site-packages/pip-9.0.2-py2.7.egg/pip/utils/__init__.py", line 267, in renames
    shutil.move(old, new)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/shutil.py", line 302, in move
    copy2(src, real_dst)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/shutil.py", line 131, in copy2
    copystat(src, dst)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/shutil.py", line 103, in copystat
    os.chflags(dst, st.st_flags)
OSError: [Errno 1] Operation not permitted: '/tmp/pip-iN62ln-uninstall/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/six-1.4.1-py2.7.egg-info'

sixがインストール済みだったのでエラーになったようです。

sudo pip install awscli --upgrade --ignore-installed six

としてsixを無視すると再インストールできました。 無事awsコマンドも使えてました。

MySQLでユーザ定義変数とcountを組み合わせて失敗した

MySQLでユーザ定義変数を使っていたときのお話です。

ユーザ情報に紐づくアカウント数に合わせて出力する形式を変更して解析しようとしてたのですが、 MySQLのユーザー定義変数をcase文で判断させようとしたところ挙動がおかしくなってしまいました。

ユーザ情報はusersテーブルに 紐づくアカウント情報はaccountsテーブルに登録してるとして、 ユーザ情報毎にアカウント情報の数を出力させて 5件以上アカウント情報が登録されているユーザは!!で強調するように出力しようとしたときに 一旦ユーザ定義変数に格納してcase文で条件分岐しようとして以下のようなSQL文を作成していました。

select
   u.id ,
   u.name,
  @b:=count(a.id),
  case @cnt:=count(a.id)
    when @cnt >= 5 then concat('!! ', @cnt, ' !!')
    else @cnt
  end
from users u
inner join accounts a on a.user_id = d.id
group by u.id;

出力はしようとしたcount(a.id)の値は何も表示されずに出力されてしまいました。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 9.4 ユーザー定義変数

には

SELECT ステートメントでは、それぞれの選択式は、クライアントに送信されるときにのみ評価されます。つまり、HAVING、GROUP BY、または ORDER BY 句では、選択式リストで値を割り当てられた変数を参照しても、予想どおりには機能しないということです。

と記載されているので、countで式評価したものを変数に入れるというのは難しそうです。 面倒ですが、 count(a.id)を何度も記載して出力結果を表示することで代用することにしました。 count(a.id) のような短さであればいいのですが、すごく長くなってしまった場合は一時変数に入れたくなりますね。