Railsアプリの育て方という発表をしました #railsdm

Rails Developers Meetup 2018 Day 1で「Railsアプリの育て方」という発表をしました。

railsdm.github.io

発表資料

余談

本当は4月から放送されるシュタインズゲートゼロみたいな流れにしたかったけど、ちょっと上手く話の流れを作れなかったので、今回は普通に作りました。

資料を作るのは難しい。

Step-to-Rails-Expert.rb#17 に参加した #StepRailsEx

ToDo アプリを題材にして、参加者で相互レビューする勉強会。

step-to-rails-expert-rb.connpass.com

コード持ち込んだ

今までサボっていたので、今回は課題を解いて持ち込んでみた。

github.com

Ruby/Rails のアップグレードなどが含まれている関係で、変更量が多くなってしまった。

実際の機能に関わるコミットは後半のやつ。

  • Bootstrap で少しデザイン調整
  • タスクを完了する機能を実装
  • サインアップ後に確認メールを送信する機能
  • 本番環境で実際にメールが届くように設定

機能に関する diff は sinsoku/expert-todo@c57cff2...9af64b3 になる。

レビューの練習できる会

仕事以外でレビューする機会/される機会は少ないので、 StepRailsEx は良い機会になるなーと思って、できるだけ参加してる。

あと、ToDoアプリって大した機能は無いのに各参加者で実装が違っていて、その違いについて話すのも楽しい。

Rails を勉強してる人は参考になるかも?

Rails 5.2.0 rc1 + System Test の実例ってあまり多くないだろうし、何かしら参考になるかもしれない。

あと、仕事で GitHub を使っていない人にとっては、プルリクでのレビューの雰囲気とか参考になるかも。

次回の日程

たぶん、4月後半の月曜になると思う。

主催の @ebihara99999 さんと @two_sann さんをフォローしていれば、情報が流れてくるはず 👀

AWS EC2 上に Rails の開発環境を構築する

Rails アプリの開発環境じゃなくて、 rails/rails にプルリクを送るための開発環境を作る方法です。

rails-dev-box について

Rails には Contributing to Ruby on Rails guide というドキュメントがあり、 誰でも簡単に開発環境を作ることができます。

5.1.1 The Easy Way

The easiest and recommended way to get a development environment ready to hack is to use the rails-dev-box.

「The Easy Way」の名前通り、 vagrant up するだけで簡単に開発環境が構築できてしまいます。

問題点

私の使っている PC は MacBook なので、ちょっと VagrantRails を動かすのはスペック的にツラいです。そこで、AWS EC2 上に開発環境を構築してみました。

Vagrant + Ansible

開発環境は Vagrant(+vagrantup-aws) と Ansible を使いました。

$ vagrant up --provider=aws
$ vagrant ssh
ubuntu@xxx: ~$ cd ~/src/rails
ubuntu@xxx: ~$ bundle exec rake
...

のような感じで AWS EC2 にお金さえ払えば、誰でも簡単に Rails の開発環境が使えるようになります。

Vagrantfile と playbook.yml は以下の通りです。*1

Vagrantfile

# frozen_string_literal: true

Vagrant.configure("2") do |config|
  config.vm.box = "dummy"

  config.vm.provider :aws do |aws, override|
    aws.access_key_id = ENV["AWS_ACCESS_KEY_ID"]
    aws.secret_access_key = ENV["AWS_SECRET_ACCESS_KEY"]
    aws.region = ENV["AWS_DEFAULT_REGION"]

    # Ubuntu Server 16.04 LTS (HVM), SSD Volume Type
    aws.ami = "ami-48630c2e"

    # m3.medium  $0.096 / hour
    # m3.large   $0.193 / hour
    # m3.xlarge  $0.385 / hour
    # m3.2xlarge $0.77  / hour
    aws.instance_type = "m3.large"

    aws.keypair_name = ENV["AWS_KEYPAIR_NAME"]
    aws.security_groups = ["rails-dev"]
    aws.tags = { Name: "rails-dev" }

    override.ssh.username = "ubuntu"
    override.ssh.private_key_path = "~/.ssh/#{ENV["AWS_KEYPAIR_NAME"]}.pem"
  end

  config.vm.provision "ansible" do |ansible|
    ansible.playbook = "playbook.yml"
  end
end

playbook.yml

- name: rails
  hosts: all
  gather_facts: no

  vars:
    bootstrap_path: /tmp/bootstrap.sh
    rails_path: ~/src/rails

  pre_tasks:
    - name: install python-simplejson
      raw: sudo apt-get -y install python-simplejson
      register: output
      changed_when: output.stdout.find("1 newly installed") != -1

  tasks:
    - name: Download bootstrap.sh
      get_url:
        url: https://raw.githubusercontent.com/rails/rails-dev-box/master/bootstrap.sh
        dest: "{{ bootstrap_path }}"

    - name: Replace user from vagrant to ubuntu
      replace:
        path: "{{ bootstrap_path }}"
        regexp: "vagrant"
        replace: "ubuntu"

    - name: Install packages
      become: yes
      apt:
        name: "{{ item }}"
      with_items:
        - libreadline-dev

    - command: which ruby
      register: which_ruby
      failed_when: which_ruby.rc not in [0, 1]
      changed_when: no

    - name: Bootstrap for Rails
      become: yes
      command: "bash {{ bootstrap_path }}"
      when: which_ruby.rc == 1

    - name: git clone rails/rails
      git:
        repo: "https://github.com/rails/rails.git"
        dest: "{{ rails_path }}"

    - name: bundle install
      bundler:
        chdir: "{{ rails_path }}"
        extra_args: "--jobs=4"

注意点

  • Ubuntu 16.04 だと Python 3 とかの問題で pre_tasks 無いと動かない。よく分かっていない
  • aws.security_groups は group ID じゃなくて、 Name を使う必要がある。

  • ActiveStorage のテストは一部テストが落ちる。

ActiveStorage のテスト

このブログ読んで、ImageMagick に詳しい人いたらコメントやプルリクを頂けると凄く助かります。

ubuntu: ~/src/rails/activestorage $ bundle exec rake
...
(中略)
Error:
ActiveStorage::Previewer::PDFPreviewerTest#test_previewing_a_PDF_document:
MiniMagick::Invalid: `identify /tmp/mini_magick20180306-5674-pg12ad` failed with error:
identify: no decode delegate for this image format `' @ error/constitute.c/ReadImage/501.

みたいなのでテスト落ちて、よく分からなかった。

まとめ

AWS EC2 を使うことで、Rails の全件テストを 80 円/1 時間 で実行できるようになりました。

あとは Rails に送るパッチを完成させて、英語のコミットログを用意するだけです!*2

*1:実際のファイルは sinsoku/dotfiles に置いてあります

*2:Ruby より英語の方が圧倒的に大変

面接で現年収(前職の年収)を聞かれるのが嫌い

転職活動で面接に行くと、だいたいどの会社も現年収を聞いてくる。

この文化が本当に嫌いなので、その気持ちをブログにまとめてみた。

最近の Twitter のツイートまとめ

たぶん人事が考えていること

生活レベルを維持できる金額を提示したい

生活する上で、最低限の必要な年収はあると思います。 特に結婚していて奥さんや子供がいると、気になるところです。

これを人事が気にして現年収を聞きたくなる気持ちも分かるのですが、それは 希望年収 を聞けば事足ります。現年収を聞く必要はありません。

現年収より高い金額を提示したい

できれば現年収より高い金額を提示したいというケース。

そんな「お気持ち」で年収が簡単に上がる評価基準の会社は微妙。

他社の評価を参考にしたい

他社で評価が高い人であれば、おそらく仕事もできるし、高い年収を提示しても良いはずというケース。

自分で相手の技術力を測ろうとせず、他社の評価を無条件で信じるその姿勢が 嫌い です。 短い面接で相手を正確に評価することが難しいと重々承知していますが、それでも自分で評価しようとする姿勢は持っていて欲しいと思っています。

そもそも、業種や会社規模の異なる他社の評価を参考にするのもどうかと思います。

採用したい

採用するために、現年収より高い金額を提示しておきたいケース。

そんなに採用したい人であれば、現年収に関係なく上限の金額を提示すれば良い。 中途半端に「現年収 + 50万」みたいなの提示して、他社のオファー金額に競り負けたら後悔する。

少しでも安い金額で採用したい

現年収と同じ、もしくは微増の提示年収なら承諾される可能性が高いと考えて聞くケース。

この気持ちは少し分かるけど、エンジニアを安く採用したいと思っている企業は 嫌い です。

まとめ

現年収を聞く理由は下記の2つくらいしか思い浮かびません。

  • エンジニアを安く採用したい
  • 自社で評価せず、他社の評価を使って楽したい

給与テーブルや評価基準を明確にしている企業様は現年収を聞かないようにして頂けると、「この企業は評価基準が明確なのか」と分かるので聞かないようにして頂ければ幸いです。

この記事が人事の目に止まって、現年収を聞く文化が消えて無くなって欲しい。

Rails 5.2 の Credentials で diff を復号化してから比較する

先日、こんなブログを書きました。

sinsoku.hatenablog.com

この gem の機能に「diff を平文で表示できる」というものがあるけど、 gem 無くても実現出来ちゃったので紹介する。

1. 復号化するスクリプトを用意する

bin/decrypt を用意する。

#!/usr/bin/env ruby

system("rails r 'puts Rails.application.encrypted(\"#{ARGV[0]}\").read'")

実行権限をつけておく。

$ chmod +x bin/decrypt

2. Git の設定をする

まず、 .gitattributes を用意する。

$ echo config/credentials.yml.enc diff=rails_credentials >> .gitattributes

次に、 git config を設定する。

$ git config diff.rails_credentials.textconv bin/decrypt

3. credentials を変更し、git diff を実行する

$ rails credentials:edit
( change something )
$ git diff
Running via Spring preloader in process 42724
Running via Spring preloader in process 42756
diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc
index 1d31b4b..e701793 100644
--- a/config/credentials.yml.enc
+++ b/config/credentials.yml.enc
@@ -4,3 +4,5 @@
 
 # Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
 secret_key_base: 76804339ec1fcd2243da42662528f60dd5044ccb7f9f2479aa10dca307d12f706517ea22f013170f0dec98c8d6f022a62f54c0f031b88587608f272ce9d18c08
+
+foo: hello

こういう Tips みたいなのって rails/rails にプルリク投げた方が良いものなんだろうか。 Issue じゃないしなぁ...。

Rails の Issue とプルリクを毎日読むと勉強になる

最近やっているけど、これ良い勉強になっているのでブログで紹介する。

読むようになったきっかけ

先月、永和システムマネジメントさんのOSSパッチ会に参加しました。

agile.esm.co.jp

この会の懇親会では Rails の Issue やプルリクの話題が多く出ました。 ただ、私が知らない話題もいくつか出ていて、もっと Rails の更新内容を知りたいと思いました。

その結果、酔った勢いで rails/railsWatching にしてみました。

毎日だけど、雑に読む

Rails は活発に開発されているため、Issue(プルリク)は毎日たくさん増えます。 しかし、これを隅々まで目を通すのはとても大変です。

なので、雑に目を通しています。

  • 興味のないやつは読み飛ばす
    • 自分が使用していない機能(ActionCable, ActiveStorageなど)の Issue やプルリク
  • 英語の長文は Google 翻訳を使って、概要だけ読む
  • 忙しいときはタイトルと description だけ読む
    • コメントを全部読むと時間がかかる

大変だと続かないので、まずは出来る範囲で読んでいます。

雑に Issue やプルリクに目を通すだけであれば、意外と短時間で済みます。

Issue(プルリク) の感想を Twitter に投稿する

  • 便利な機能のプルリクを読んだ
  • 自分の知らなかった Rails の挙動を知った

など、何かあったときは Twitter に投稿してます。

なんとなく、アウトプットしておいた方が記憶に残る気がする。

仕事に役立ちそうな情報は社内に共有する

業務中に Rails の Issue(プルリク) を読む事もあるので、役立ちそうな情報は積極的に社内 Slack に流します。

Rails の最新情報を社内に布教してる」ことを言い訳に、業務中でも Rails の Issue(プルリク)を読みやすくします。*1

2月前半のまとめ

ここ最近で Rails の Issue(プルリク) を読んでいて面白かったのをいくつか紹介する。

リダイレクトの 301, 302 の違い

github.com

  • routes.rbredirect メソッド: 301
  • コントローラの redirect_to メソッド: 302

ステータスコードが違う。

301リダイレクトにはいくつか問題があるので302 を使う方が良いとか、デフォルトの挙動を変えるのはキツいので temporary_redirect を導入するとかの話がされている。

英文が長いので、細かいところは自信ない。

accepts_nested_attributes_for で validates_uniqueness_of が効かない

github.com

一度解決したけど再発したみたい。

Rails 5.2.0.rc1 でも再現する。

p = Parent.create
#=> #<Parent id: 1, created_at: "2018-02-11 18:24:53", updated_at: "2018-02-11 18:24:53">
p.update_attributes :children_attributes => [{:name => "Kid"}, {:name => "Kid"}]
#=> true
Child.all.map(&:name)
#=> ["Kid", "Kid"]
c = Child.first
#=> #<Child id: 1, parent_id: 1, name: "Kid", created_at: "2018-02-11 18:24:59", updated_at: "2018-02-11 18:24:59">
c.save
#=> false
c.errors.full_messages
#=> ["Name has already been taken"]

並列テストのプルリク(WIP)

github.com

rails/rails##26703 で System Test のプルリクを投げた方の新作。

Rails 標準で対応されると便利そう。

has_many through で touch が効かない

github.com

touch が効かないという Issue。
コメントに dependent: :destroy で回避策する方法があるけど、それはどうなんだ...って感じがする。

あと、この Issue で through の対象レコードは削除されないことを知った。マジか。

class Post < ActiveRecord::Base
  has_many :taggings
  has_many :tags, through: :taggings
end

class Tagging < ActiveRecord::Base
  belongs_to :post
  belongs_to :tag
end

class Tag < ActiveRecord::Base
end


post = Post.create!
tag = Tag.create!

post.tags << tag
post.tags.count
#=> 1
post.tags.destroy_all

Tag.count
#=> 1

Tag も消えると思っていました。

Relation#pick の追加

github.com

DHHのプルリクで、 pick ってメソッドが追加されている。

Person.where(id: 1).pick(:name)
  # SELECT people.name FROM people WHERE id = 1 LIMIT 1
  # => 'David'

便利そうだけど、戻り値が文字列 or 配列ってのは気をつけて使わないと NoMethodError が出そう。

あと、このプルリクのテストは最初 fail していて、kamipo さんが「Failing the assert will be fixed by #30000 .」とコメントしていた。 それに対するDHHのコメントは「I just changed the test for now」だった。

「30000をマージして、rebaseにしないのかー」とプルリクを見ながら思った。 まぁ、pick のプルリクのためにマージするにしては 30000 は影響範囲が大きいって判断なのかもしれない。

has_many の関連と where! で誤った値が返ってくる

github.com

where! は内部用のメソッドだから where を使って」とコメントされて close されている。

気になったので Rails 5.2.0.rc1 で試したら再現した。

class User < ActiveRecord::Base
  has_many :tasks
end

class Task < ActiveRecord::Base
end


u = User.create!
2.times { u.tasks.create! }

u.tasks.where(user_id: u.id + 1).count
#=> 0
u.tasks.where!(user_id: u.id + 1).count
#=> 0
u.tasks.where!(user_id: u.id + 1).size
#=> 0
u.tasks.tap { |x| x.where!(user_id: u.id + 1) }.count
#=> 0
u.tasks.tap { |x| x.where!(user_id: u.id + 1) }.size
#=> 2

まとめ

Issue(プルリク)を読んでいると勉強になるので、是非 Rails エンジニアは読んでみると良いですよ。

そして、余裕のある方はプルリクを投げてみよう!*2

*1:まぁ業務中にOSS活動していても特に問題にならないけど

*2:私もまだコントリビュート無いので、2018年は頑張りたい

Rails 5.2 の Credentials を各環境でも使えるようにする gem を作りました

Rails 5.2 で追加される Credentials 機能を development, test, staging などで使えるようにする gem を作ったので紹介します。

github.com

Rails 5.2 の Credentials とは?

API token やパスワードなどの情報を暗号化し、リポジトリで管理できるようにする機能です。 暗号化に使用する鍵は config/master.key もしくは RAILS_MASTER_KEY 環境変数を使います。

今までの Rails では秘匿情報を環境変数で管理するのが主流でした。 しかし、この方法では管理する環境変数が多くなってしまったり、バージョン管理できません。

5.2 で導入される Credentials の機能を使うことで、秘匿情報をリポジトリで管理することができるようになり、サーバに設定する環境変数も1つで済むようになります。

Rails 5.1 で追加された encrypted secrets に似ている?

This will eventually replace Rails.application.secrets and the encrypted secrets introduced in Rails 5.1.

引用: https://github.com/rails/rails/blob/v5.2.0.rc1/activesupport/CHANGELOG.md

encrypted secrets は忘れてください。

Credentials の不満点

DHH曰く「productionしか秘匿情報は必要ない」とのこと。

It's only in production (and derivative environments, like exposed betas) where the secret actually needs to be secret.

引用: https://github.com/rails/rails/pull/30067

いや、staging 環境で使う API token とかがあるので他環境でも使いたい...(´・ω・)ッス

別の方が Environment-specific credentials という Issue を作っていますが、 Close されてるし、 Rails では対応されなそう。

各環境の秘匿情報を管理する gem を作った

そんな経緯で RailsEnvCredentials の gem を作りました。

各環境で別々の credentials.yml.enc を使うことができます。

基本的な使い方

Gemfile に gem を追加して、 bundle install してください。

group :development, :test do
  gem 'rails-env-credentials'
end

もし staging 環境で使う場合は group に :staging も追加する必要があります。

development 用の credentials を生成するには env_credentials:edit コマンドを使います。

$ rails env_credentials:edit -e development

development 環境用に credentials-development.yml.enc が生成されます。 鍵は config/master-development.keyRAILS_MASTER_KEY_DEVELOPMENT 環境変数を使います。

秘匿情報の diff を表示する

credentials.yml.enc は暗号化されているため、普通に git diff をしても差分は表示できません。

$ git diff config/credentials.yml.enc
diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc
index 1a69bf0..c0517de 100644
--- a/config/credentials.yml.enc
+++ b/config/credentials.yml.enc
@@ -1 +1 @@
-SN7yXV6C2NZSdp/ij84h6Tox7iJ6VLHWgnhbY7BFe5TTsLa6V9g1D/iI1H5WMCBEECcpL/z096spDpcjXdq1aS2w3Wz9GijvuCiJNA8LaiWzqN9FMDmPB8eJjff+qebG+mN4Wdmc1/LDWV0GvYYQWAzDuYv3Kyv7jrxh/6MQjm2h9+i1AlBj7ofnzppvz9m8yiM2kyoqk/5aFdNiUqCdvzqbxIAXf0qvL/GPezKVOgbLcKL+/ytIFDvQeFc+BJ9d/RHsvOfyy1hSJxAhstJsf9I2WlI8gWYBjNip6nZzEQUCuhJpFEd+tXTzTOt3ZlWHzodQA9W3E+BScDOUgAPM7eJ9eCCDHvSQrvgI9i9KfpuxFP51OTODqYkR1+kfW7g/ZupvAhwMAM504af7zFLgHxad5Q==--UjL1q8veV48t5/oe--pUi30wtwFBJlM5PIJI0U2A==
\ No newline at end of file
+i3rH3HDSUPAwal/Mi8SsFg+UqKfyudpjTxFpYrXxElOvQYo7wsIZ9v5NRe6mew/WnMNiM/B66jJM4KIf1voMRWrTJaGfkWc4ARLjgcgLNu4rj4+LHyZZ2LkkgqZpTHObtLHEH5E/7ZNQVpHJ1CwfMR/SwGgaDPJFVbM3uF3tGSzY0Fyg3jgeyiiU6Vuk+H6CUGFMxDsjnIj1oMrpACX42vDfpy5wZquyzycG6LpmJsFqofirmb9sT1n32mDoA6k138SDLdnUiNpftNGGK8q9+SaT8e5OzZ8CMSeMLD64psRYCL+qZip+ZX0zvdVmSkUaH/y71bkHTChhMNZzHcW69FBNgfirMmWchjePUlw1+LIjrAIQL9JQD5Wv3E5Yl6RRKX0LSsrtUqEmABz9Lxbq9uGh/HEsC2OnrLOGYFvvKYs53Y1lQVF29f3NKSdk--VINiczKtUJKG4hro--oRD2+JF7lbL/wO8d3dP0Mw==
\ No newline at end of file

これは Git の設定を活用することで表示できるようになります。

まず、下記の内容で .gitattributes を作成します。

config/credentials*.yml.enc diff=env_credentials

次に Git に復号化のコマンドを設定します。

$ git config diff.env_credentials.textconv 'rails env_credentials:show --file'

この状態で git diff を実行すると、復号化した内容で差分が表示されるようになります。

$ git diff config/credentials.yml.enc
diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc
index 1a69bf0..c0517de 100644
--- a/config/credentials.yml.enc
+++ b/config/credentials.yml.enc
@@ -4,3 +4,5 @@
 
 # Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
 secret_key_base: 358e4c7f994ffdba2a8994c00bf902303d1456d1f4d4e3d8e6c6c843628de5bc158f061fd1759b7a4a015395627ad9b34a4e8714167da0387695cb9906bbe1ba
+aws:
+  access_key_id: 123

まとめ

production 以外での秘匿情報の管理って需要ありそうだけど、Rails にプルリクを投げて英語で説得する自信は無かったので、とりあえず gem を作ってみた。

もし gem が結構使われて、需要ありそうってのが分かったらプルリクを投げるかもしれない。