ひよっこエンジニアの雑多な日記

とあるプログラミングスクールで働くひよっこエンジニアが覚えたことや悲しみを記すブログ

Rails 5.1のform_withを使ってうまくredirectできないあなたへ

かなり気まぐれですがネタができたらコンスタントにブログを投稿しようという気持ちになり始めました。
今年一年で50投稿を目指して頑張ります。

概要

Rails5.1 + sorceryを使って認証周りをサクッと作ろうとしたのですが、5.1から出てきたform_withに微妙にはめられました。
結構初歩的っぽいですが僕と同じような気持ちになる人がいるかもしれないので備忘録。
というかRailsの4.x系とか5.0系やってきた人が試しに5.1使ってみよってなったら割とハマるんじゃないかと勝手に思ったり…
※ ブログの内容的にはsorceryはもはや関与しません

事象

以下みたいな感じでログインフォームを作ってました。

<%= form_with url: sign_in_path, method: :post do %>
  <div class="field">
    <%= label_tag :email %><br />
    <%= text_field_tag :email %>
  </div>
  <div class="field">
    <%= label_tag :password %><br />
    <%= password_field_tag :password %>
  </div>
  <div class="actions">
    <%= submit_tag 'Login' %>
  </div>
<% end %>

これでログインできるか確認するためにLoginボタンを押すと

ログイン成功してるはずなのに画面遷移しない・・・・!!!

いやいやと思ってLoginボタンを連打してみるも状況は変わらず。
しかし画面再読み込みしたらログインできてる。

ログ見たら

Started POST "/sign_in" for 127.0.0.1 at 2017-07-08 23:03:12 +0900
Processing by UserSessionsController#create as JS

なんかJSをレンダリングしようとしてますやん!
そりゃ画面遷移しないわけだ。

解決策

ちゃんとform_withの仕様を把握してから使えばよかったのですが、解決策としてはなんてことない感じでした。
解決したコードがこちら

<%= form_with url: sign_in_path, method: :post, local: true do %>
  <div class="field">
    <%= label_tag :email %><br />
    <%= text_field_tag :email %>
  </div>
  <div class="field">
    <%= label_tag :password %><br />
    <%= password_field_tag :password %>
  </div>
  <div class="actions">
    <%= submit_tag 'Login' %>
  </div>
<% end %>

ほぼ変わってない感じですが、form_withlocal: trueオプションをつけてあげてます!
これでHTMLをレンダリングしてくれるので意図した動きになります!

どうもform_withはデフォルトで生成されるformタグにdata-remoteがついちゃうっぽいです。
local: trueを設定することでこれをつかないようにできるとのこと。

よくよく考えたらscaffoldで生成された_form.html.erbみるとform_withlocal: trueがついてんだよなあ
まあしかしこれで大人の階段をまた一つ登ることができました

余談

scaffoldで生成されるerbファイルのform_withのブロック変数がformになってるんですね!
いままではfがブロック変数でしたが、やはり可読性を考えると一文字よりも意味のある単語の方がよいんですかねえ

CircleCIでelasticsearchにkuromojiをインストールして起動させる

CircleCIで自動テストをしたいもののelasticsearchの処理を含んだ処理周りがうまくテストされなくて結構ハマったので備忘録。

やりたいこと

アプリケーションの機能として簡単な全文検索をelasticsearchを使って実現していたので、CircleCIにもelasticsearchを導入して全文検索用のindexを作成をアプリケーションのテストコードで行なっていた。
CircleCIを使ってGitHubでプルリクエストを上げた瞬間に自動でテストが走らせるCI環境を構築して、全テストをパスさせたい!

問題点

しかし、elasticsearchを導入してpluginとしてkuromojiを導入する必要があり、それを実現するのに四苦八苦。
elasticsearchを動かすだけならできるのだが、kuromojiを導入する方法がわからない…

ひとまずここみたら全てが解決しました。

解決法

circle.ymldependenciesに以下を追記してあげるだけでOK

dependencies:
  post:
    - wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.3.2.tar.gz
    - tar -xvf elasticsearch-5.3.2.tar.gz
    - elasticsearch-5.3.2/bin/elasticsearch-plugin install analysis-kuromoji
    - elasticsearch-5.3.2/bin/elasticsearch: {background: true}
    - sleep 10 && wget --waitretry=5 --retry-connrefused -v http://127.0.0.1:9200/

これ書いておくとelasticsearchをwgetでダウンロードして、解凍して、kuromojiインストールして、elasticsearchを起動してくれる。

毎回wget走るの重くなりそうでいややなーとなる場合は以下みたいな感じに書き換える。

dependencies:
  cache_directories:
    - elasticsearch-5.3.2
  post:
    - if [[ ! -e elasticsearch-5.3.2 ]]; then wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.3.2.tar.gz && tar -xvf elasticsearch-5.3.2.tar.gz && elasticsearch-5.3.2/bin/elasticsearch-plugin install analysis-kuromoji; fi
    - elasticsearch-5.3.2/bin/elasticsearch: {background: true}
    - sleep 10 && wget --waitretry=5 --retry-connrefused -v http://127.0.0.1:9200/

cache_directoriesを使うとディレクトリをキャッシュできるようなので、wgetして解凍したディレクトリをキャッシュしてくれるようになる。
! -e elasticsearch-5.3.2のところでディレクトリの存在確認をしてくれるのでキャッシュされてなかったらwget、解凍、インストールしてくれるし、キャッシュされてたらこの処理を飛ばしてくれるようになるので無駄なオーバーヘッドなくなるぽい。

これでelasticsearchをCircleCI上でも使えるようになってめでたしめでたし。

ちなみに

machine:
  services:
    - elasticsearch

最初、これでelasticsearch立ち上がるやん!と喜んでいたのですが、この記述をしているとwgetで落としてきたelasticsearchとは別物の子が動いちゃうので、いくらkuromojiをインストールしてもpluginがインストールされていないことになってテストが全く通らなくて禿げ上がりそうでした。
この記述に気づかず1時間ぐらい消費したのは愚かでした…

CentOS7系でhttpアクセスを許可する

毎度ConoHaでサーバー構築する際に忘れてしまうので備忘録

操作

httpを許可する

$ firewall-cmd --add-service=http --zone=public --permanent

httpsを許可する

$ firewall-cmd --add-service=https --zone=public --permanent

ファイアウォールをリロード

$ firewall-cmd --reload

ファイアウォールが正しく設定されているか以下のコマンドで確認

$ firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: eth0
  sources:
  services: dhcpv6-client http https ssh
  ports:
  protocols:
  masquerade: no
  forward-ports:
  sourceports:
  icmp-blocks:
  rich rules:

serviceのあたりにhttpとhttpsが追加されていればOK

CentOS7で毎度環境構築するたびに忘れてて、検索するのがだるいのでブログ化
あとserviceコマンドに慣れすぎててsystemctlもよくど忘れする
あとmysqlいれたつもりになってるとmariadbが入ってて焦るときがある(ほぼmysqlと一緒だけど…)

【Rails】enumで特定の値を選択肢から省きたい

きむらです。またもやenum関連ですがちょっとなるほどと思ったことがあったので備忘録。

enumに設定している値を全て取り出す

例えばセレクトボックスに選択肢としてenumに設定している値を出す場合などによく使うとおもいます。

# blogというモデルに公開範囲(非公開、公開、一部に公開)のようなステータスがある想定
class Blog < ActiveRecord::Base
  enum status: { unpublish: 0, publish: 1, part_publish: 2 }
end

# enumに設定したstatusの状態をすべて取得する
Blog.statuses
=> {"unpublish"=>0, "publish"=>1, "part_publish"=>2}

てな感じでモデル.enumを設定した属性の複数系と書くことでenumに設定した値を取得することができます!
これを使えばブログの公開範囲選択セレクトボックスを作成するのが簡単になったりします!

enumに設定している特定の値だけを取得したい

便利とはいえ特定の状態だけを選択させたくなるときもあります。
例えば一部に公開(part_publish)を特定の箇所では選択肢に入らないようにしたいとか

そんなときはsliceexceptを使うと幸せになれます!

sliceを使ってpart_publishを省く

Blog.statuses.slice(:unpublish, :publish)
=> {"unpublish"=>0, "publish"=>1}

sliceを用いると引数に指定した値だけを取り出すことができます。
特定の値を省くというより、特定の値だけを取り出すという感じですね!

exceptを使ってpart_publishを省く

Blog.statuses.except(:part_publish)
=> {"unpublish"=>0, "publish"=>1}

exceptを用いると引数に指定した値は取得しないようになります!
省くという意味合いではexceptの方が正しそうですね。

といった感じで簡単にenumに設定した特定の値だけを取得できるようになります!
これらを使ってクラスメソッドなんか作ってあげるとスマートっぽい気がしますね!

class Blog < ActiveRecord::Base
  enum status: { unpublish: 0, publish: 1, part_publish: 2 }

  def self.restrict_statuses
    statuses.except(:part_publish)
  end
end

Blog.restrict_statuses
=> {"unpublish"=>0, "publish"=>1}

slice、exceptは何者か

この二つはActiveSupportのメソッドのようです!
enumを設定して設定内容を取得したもののクラスが以下にになります!

Blog.statuses.class
=> ActiveSupport::HashWithIndifferentAccess

つまりBlog.statusesで取得できるものはハッシュってことになります。
RailsだとハッシュはActiveSupportパイセンの力でちょいちょい強化されていて、その強化された力を用いることで割と簡単にハッシュ操作ができたりします…。sliceexceptはその力の一部であるということです…。

ほんとActiveSupportActiveRecordあたりはよしなにいろんなことをやってくれるので便利すぎるなあという気持ちになります。

最近RailsのよしなにしてくれるところにあぐらかいてRuby力がなくなってきている(そもそもそんなにない)ので、その辺鍛え直さないとなあ…。

2017年が始まったので今年の目標を掲げてみる

お久しぶりです。きむらです。
めちゃめちゃブログサボってました…もはや存在忘れそうになってました…笑

2017年は気持ちを改めてブログ書きます!目標は3日で1記事!
というわけで2017年最初の記事は今年の目標を綴っていきたいと思います!(戒めのためにも)

2017年の個人テーマ

  • コミュニケーション
  • 興味関心
  • 意思決定

2017年に掲げる個人テーマは上記3点です! それぞれ少し深堀します。

コミュニケーション

これは若干コミュ障気味の自分を変えていくために意識したいなあと…。

仲のいい友達や職場の人とは大体喋れるんですけど、ちょっと苦手だなあと感じる人や微妙な距離感の人、初対面の人と向き合うと途端に喋れなくなる(もはや逃げ出したくなる)という状態を解消しなければ…と思っています…。
何かの本でコミュニケーションは人生を左右する云々のことが書いてあって、そうだよなあと思いつつコミュニケーションをないがしろにしていた感はあります(反省)

お正月休みにKindleでコミュニケーションに関するコミックエッセイを読んで、コミュニケーションを楽にする方法みたいなものを学んだ感があるので、それを実践していきたい所存です。

興味関心

いろんなことに興味関心を持つようにしたいです!

というのも自分が興味関心あることが今ぱっとでないという完全に無趣味無関心人間になってしまっていたという状態を解消したいです…。

無関心状態があることによって人と話したりしても、そんなん言われても興味ないんだよなあとか、人自体に興味関心がわかないんだよなあみたいな、ある意味中二病とも言える病に20半ばの今でもかかっているという事態を解消するというのがこのテーマを掲げた一番の理由ですかね…
このテーマはコミュニケーションにもつながりますしね…

意思決定

これは仕事、プライベート共にですね…。

仕事でもプライベートでも色々考えすぎて意思決定まで時間がかかりすぎたり、決定することを人に任せてしまったりということが多々あったので、もっと自分で何かを決定するということをしていかねばと…。
もはやご飯いくところも完全に人任せばっかりだったし…

なのでちょっとずつ自分で何かを決めていける、決めるまでのスピードを早められるようにするということをやっていきたいと思います!

おわりに

個人的に今年一年は自分自身の人生がどうなるかというのが色々な意味で占われる一年になるのではないかなあと思っています。
そんな一年で自分をきちんと見つめ直して、本当に興味あることや今後どのようになっていきたいかを見つけて豊かな人生を構築する足がけにしていきたいと思います!

そして今年こそ彼女をば…!!!笑

【Nginx】apple-touch-iconに攻め込まれる場合の対処

2ヶ月ぶりくらいの更新です…
毎度お世話になっております。きむらです…

最近色々とやばいことが重なりすぎて人生が楽しくなってきています。(白目)

さて今日はちょっとした記事ではありますがタイトルの通りの設定を備忘録代わりに書いときます!

状況

現在Nginx+Unicornで運用しているRailsアプリケーションがapple-touch-iconに攻められまくり、apple-touch-iconをサーバに配置していないのでRoutingエラーを弾き出しまくっていました…
そしてイケてないエラーハンドリングをしてしまっているのでアクセスされるたびにアラートメールが飛んでくる…
一時間に10通は送信されてくるので煩わしさが半端じゃない!!(エラーハンドリング改善しろって話なんですけどね…はい…)
Nginxでそのアクセスをさばいていなかったので、Unicornまでアクセスが到着してしまっているという状況でした…

解決策

一番はapple-touch-iconを設置してしまうことなのですが、用意しているわけではなかったのでNginxパイセンにさばいてもらうことにしました

root /アプリまでのパス/current/public;

location / {
     ルートパスの設定
}

location ~ ^/(apple-touch-icon*) {
     キャッシュの設定とか
}

ひとまずapple-touch-iconなんとかへのアクセスがあったら/アプリまでのパス/current/public配下を見せるように設定を記述しました。
これでapple-touch-iconなんとかを配置していなくてもNginxパイセンが404をはじき返してくれて、Unicornさんが怒りのアラートを出すことがなくなりました!一安心!

とりあえず最近手掛けてるアプリの作りやばいし、色々やばいし、色々頑張んなきゃいけないしでつらたん
今、誰かに優しくされたら惚れてまいそう(真剣)

mysqlでメールアドレスにマスクをかけるよ

今回は2週間ぶりくらいの更新!
ちょっとずつ間隔を短くしたい!

今回は件名の通りmysqlでメールアドレスにマスクをかけます。
hogehoge@example.com -> xxxxxxx-1@example.com
上みたいな感じにします!

シチュエーションとしては

  • 本番データをステージングに突っ込みたいけど個人情報やし、うかつに突っ込めない…
  • そんなときはメールアドレスにマスクをかけちゃお! みたいな感じです

兎にも角にも手順

1. 本番環境からdumpとる
2. ローカルにdumpを突っ込む(DBは作成してからNE)
3. マスクかけるupdate文を流す

update 'テーブル名' set 'emailのカラム' = replace('emailのカラム',left('emailのカラム',instr('emailのカラム','@')-1),concat('xxxxxxx_',id));

4. ローカルからdumpとる
5. scpでステージングに送る
6. ステージングでDBにdump突っ込む
7. 優勝

きょうのはんせいてん

インフラ触るときは1字1句の誤字脱字のチェック、
なにか質問するときはまず自分で確認できるところはないか考えてから
ちゃんと足りないことがないか確認してからアクションする

備考

postgresqlの場合はこうする

update 'テーブル名' set 'emailのカラム' = replace('emailのカラム',substring('emailのカラム', 1, strpos('emailのカラム','@')-1),concat('xxxxxxx_',id));