グロースエンジニアのブログ

プログラミングとウェブ解析をやっています。Googleアナリティクス好きなRuby on Rails エンジニアです!

factory_bot 再入門

Railsを始めてはや数年、最初のころに factory_girl の勉強をして、それ以来必要に合わせてちょっと調べて終わりって感じで過ごしてきた。

最近Railsを始めた人に、「Railsチュートリアルとかeveryday railsとかやって、開発してるプロダクト見ると書き方が違ってる」って話を聞き、そう言えばその辺の知識って古いまま(と言っても2,3年前?)更新されてないな...と思って読み直すと結構知らなかったことが多かった。

なので、ざっとまとめてみる。

まとめる内容は全部ここに書いてるので、こちらを見るとよいかと!

github.com

attributes_for

これも地味に知らなかった。

# Returns a hash of attributes that can be used to build a User instance
attrs = attributes_for(:user)

Aliases

Factory に alias が付けれる

factory :user, aliases: [:author, :commenter] do
  first_name    "John"
  last_name     "Doe"
  date_of_birth { 18.years.ago }
end

factory :post do
  author
  # instead of
  # association :author, factory: :user
  title "How to read a book effectively"
  body  "There are five steps involved."
end

factory :comment do
  commenter
  # instead of
  # association :commenter, factory: :user
  body "Great article!"
end

はい、知りませんでした...

ポリモーフィックとか使ってるのをちょいちょい見るので、これ使うとわかりやすくなるかな〜とか思ったり。

Transient Attributes

ちょいちょい見て、わかってるようで分かってなかった。 

この例はわかりやすくて、理解できた。

factory :user do
  transient do
    rockstar true
    upcased  false
  end

  name  { "John Doe#{" - Rockstar" if rockstar}" }
  email { "#{name.downcase}@example.com" }

  after(:create) do |user, evaluator|
    user.name.upcase! if evaluator.upcased
  end
end

create(:user, upcased: true).name
#=> "JOHN DOE - ROCKSTAR"

Inheritance

いつも trait を使ってたけど、こういうのもあるのか。一回で書けるって意味ではいいのかもな。

factory :post do
  title "A title"

  factory :approved_post do
    approved true
  end
end

approved_post = create(:approved_post)
approved_post.title    # => "A title"
approved_post.approved # => true
factory :post do
  title "A title"
end

factory :approved_post, parent: :post do
  approved true
end

Associations

alias を使えばいいけど、こういう書き方もある。

factory :post do
  # ...
  association :author, factory: :user, last_name: "Writely"
end

Sequences

こうやって別個に定義できるってのは知らなかったな。

# Defines a new sequence
FactoryBot.define do
  sequence :email do |n|
    "person#{n}@example.com"
  end
end

generate :email
# => "person1@example.com"

generate :email
# => "person2@example.com"
factory :invite do
  invitee { generate(:email) }
end
factory :user do
  email # Same as `email { generate(:email) }`
end

んで、aliasも定義できる

factory :user do
  sequence(:email, 1000, aliases: [:sender, :receiver]) { |n| "person#{n}@example.com" }
end

# will increase value counter for :email which is shared by :sender and :receiver
generate(:sender)

Linting Factories

Lint ってよくあるけど、factory_bot でもあるって知らなかった。

「うるせー!」 って言いたくなることもあるけど(笑)

FactoryBot.lint

まとめ

これ以外にもいろいろ紹介されてたけど、基本的なところはこんなところかなと!

gemも定期的に見て、機能とか確認していかないとダメだなというのが今回の気付き!

github.com

【mastodon】bundle install でエラーが出たのでメモしておく

mastodon がちょっと前に流行ってコード読もう読もうと思ってそのままにしていたので、時間取れるときに読もうと思って bundle install したらエラーになったのでメモ。

mastodon のバージョンは 2.3.2 

Failed to locate protobuf

current directory:
/mastodon/vendor/bundle/ruby/2.5.0/gems/cld3-3.2.2/ext/cld3
.rbenv/versions/2.5.0/bin/ruby -r ./siteconf20180326-69724-1yq4plx.rb extconf.rb
Failed to locate protobuf

To see why this extension failed to compile, please check the mkmf.log which can be found here:

/mastodon/vendor/bundle/ruby/2.5.0/extensions/x86_64-darwin-16/2.5.0-static/cld3-3.2.2/mkmf.log

extconf failed, exit code 1

Gem files will remain installed in
/mastodon/vendor/bundle/ruby/2.5.0/gems/cld3-3.2.2 for inspection.
Results logged to
/mastodon/vendor/bundle/ruby/2.5.0/extensions/x86_64-darwin-16/2.5.0-static/cld3-3.2.2/gem_make.out

An error occurred while installing cld3 (3.2.2), and Bundler cannot continue.
Make sure that `gem install cld3 -v '3.2.2'` succeeds before bundling.

`gem install cld3 -v '3.2.2'` をやってみたけどダメで、調べたら protobuf なるものが必要みたいで、 `brew install protobuf` をやったら通った!

 つづいてこちら

ERROR: could not find idn library!

current directory:
/mastodon/vendor/bundle/ruby/2.5.0/gems/idn-ruby-0.1.0/ext
/.rbenv/versions/2.5.0/bin/ruby -r ./siteconf20180326-5783-ruj5ss.rb extconf.rb
checking for -lidn... no
ERROR: could not find idn library!

  Please install the GNU IDN library or alternatively specify at least one
  of the following options if the library can only be found in a non-standard
  location:
    --with-idn-dir=/path/to/non/standard/location
        or
    --with-idn-lib=/path/to/non/standard/location/lib
    --with-idn-include=/path/to/non/standard/location/include

*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
	--with-opt-dir
	--without-opt-dir
	--with-opt-include
	--without-opt-include=${opt-dir}/include
	--with-opt-lib
	--without-opt-lib=${opt-dir}/lib
	--with-make-prog
	--without-make-prog
	--srcdir=.
	--curdir
	--ruby=.rbenv/versions/2.5.0/bin/$(RUBY_BASE_NAME)
	--with-idn-dir
	--without-idn-dir
	--with-idn-include
	--without-idn-include=${idn-dir}/include
	--with-idn-lib
	--without-idn-lib=${idn-dir}/lib
	--with-idnlib
	--without-idnlib

To see why this extension failed to compile, please check the mkmf.log which can be found here:

/mastodon/vendor/bundle/ruby/2.5.0/extensions/x86_64-darwin-16/2.5.0-static/idn-ruby-0.1.0/mkmf.log

extconf failed, exit code 1

Gem files will remain installed in
/mastodon/vendor/bundle/ruby/2.5.0/gems/idn-ruby-0.1.0 for inspection.
Results logged to
/mastodon/vendor/bundle/ruby/2.5.0/extensions/x86_64-darwin-16/2.5.0-static/idn-ruby-0.1.0/gem_make.out

An error occurred while installing idn-ruby (0.1.0), and Bundler cannot continue.
Make sure that `gem install idn-ruby -v '0.1.0'` succeeds before bundling.

これも brew install libidn したら通った。

 

第57回 Ginza.rbに行ってきた!

今回は【ディスカッションプラットフォーム Discourse のソースを読もう!】というテーマでした。

github.com

 

今回も忘れないうちにメモしたことをまとめます。

 

Discourseとは?

オープンソースとして公開されている掲示板やチャットが使えるディスカッションプラットフォームで、現場感のあるコードとのこと!

フロントは Ember で作られている。

スピードを気にしていて、○○族な人が作ってるらしい。

勉強会メモ

メモした内容をざっと記録していく!

lib にいろいろ詰め込んてる

アプリとは切り離して動くコードがここにある。

lib/freedom_patches ってのがあって、これを自分たちのアプリでも作って、公開できるやつは公開していくのはいい方法。

discourse/lib/freedom_patches at master · discourse/discourse · GitHub

コントローラーで require している

スピードに気をつけているので、必要に合わせてコントローラーなどで require している

discourse/app/controllers at master · discourse/discourse · GitHub

モデルに置けないものを services に置いてる

処理系のメソッドをここにまとめて定義している感じ

discourse/app/services at master · discourse/discourse · GitHub

環境変数のデフォルト値を上げてパフォーマンスを上げてる

discourse/bench.rb at master · discourse/discourse · GitHub

pluck, present?, blank? を早くしてる

discourse/fast_pluck.rb at master · discourse/discourse · GitHub

discourse/performance_fixes.rb at master · discourse/discourse · GitHub

その他

- Gemfile も Discource 自身が作っているものがあり、スピードを気にして使っている

 

- 参考にするのはどのサイトが良い?

-> 過去の歴史を見るという意味で readmine

GitHub - redmine/redmine: Mirror of redmine code source - Official SVN repository is at https://svn.redmine.org/redmine - contact: @jbbarth or jeanbaptiste.barth (at) gmail (dot) com

 

-> mastodon

GitHub - tootsuite/mastodon: Your self-hosted, globally interconnected microblogging community

 

-> discource は現場感があっていい

GitHub - discourse/discourse: A platform for community discussion. Free, open, simple.

 

TracePoint がキテるらしい

まとめ

頑張って話を聞き逃さないようにメモ取ってたけど、理解しようとして考えてて取りきれてない部分が結構ある気がする...

でも、今回も自分の知らないことがたくさん聞けて学ぶことも多かった。

少しでも内容を理解して自分の力にできるように、今後も参加していこうと思います!

第56回 Ginza.rbに行ってきた!

ここ数ヶ月続けて Ginza.rb に参加しています。

自分とのレベルが違いすぎて圧倒されることが多いんですが、このままだと本当にただ参加しているだけになってしまうので、振り返りもかねて学んだことを記録して行こうと思います。

今回のテーマ

【第56回 もうすぐやってくる!Rails5.2の細かいところも見ておこう 】ということで、資料はこちら!(公開してもいいということだったので大丈夫なはず!)

Rails 5.2

内容については上記資料を見た方がいいです。

各ページの青いところはリンクになっていて関連するPRなどに飛べるので分かりやすいと思います。(さすが、あの方の資料!)

以下、自分用のメモ。
知らなかった機能などはざっとどんな機能か調べてリンクを貼ってます。

(間違いなどあればご指摘いただけるとうれしいです!)

Action Cable

- hostとかportとかを設定できるようになった

使ったことないけど、今後機会があれば思い出せるようにメモ。

Action Mailer

- Mailer ごとに delivery job を設定できる

`delivery_later` したときに呼ばれるjobを自由に設定できるってことかな?

rails/message_delivery_test.rb at 2b35826389005bdb0af85a4ebb1f0ec213174d13 · rails/rails · GitHub


- email preview にlocaleが選べるようになった

preview 機能自体を知らなかった...。

ActionMailer Preview のススメ - Qiita

RailsのAction Mailer Previewsについて | 日々雑記

Action Pack

- cache key と cache version(updated_at)を分けて管理し、 cache keyを再利用可能に

Rails の version up のときに、load default にするとあれするので気を付けてね!とのこと。

ローカルで試すには設定が必要っぽい。

キャッシュは、デフォルトではproduction環境でのみ有効になります。ローカルでキャッシュを使ってみたい場合は、対応するconfig/environments/*.rbファイルでconfig.action_controller.perform_cachingtrueに設定します。

Rails のキャッシュ: 概要 | Rails ガイド

- eager_load = true の場合、サーバー起動時に各コントローラーのactionを読み込むようになった

- encrypted cookies に AES-256-GCM を使用

より堅牢なものになった
Rails側で良いようにしてくれる(自動で書き換える)
勝手に古いcookies を新しいものに書き換える

- protect_from_forgery がデフォルトになった

- signed/encrypted cookiesに有効期限を知っていできる

- key の rotation ができるようになった

# ここで新しいものを指定
Rails.application.config.action_dispatch.signed_cookie_digest = "SHA256"

# ここで古いのを指定
Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
  cookies.rotate :signed, digest: "SHA1"
end

- System test に headless chrome driver, headless firefox driver が入った

- デフォルトの mime type が追加された

- header が変わった
以下の3つが追加された


  "X-Download-Options" => "noopen",
  "X-Permitted-Cross-Domain-Policies" => "none",
  "Referrer-Policy" => "strict-origin-when-cross-origin"

Action View

- srcset を指定できるようになった

- 画像の alt の自動生成を削除

- form_with に id 属性を追加するようになった
capybara のテストなどで必要ということで追加された 

Active Job

jobを実行する際にオブジェクトをシリアライズ、デシリアライズしてる。
それを自分で定義できるようになった

Active Model

- length validatorにProc, Symbolが渡せるようになった
これにより、条件を指定して最大文字数とかを調整できるようになった

- Active Record にあった attributes API が Active Model 配下に移動
Active Record のものを使うのは問題ない
Active Model のものは public ではない

(Rails で public api ってのは docs にあるかどうか)

Active Record

- Descending Indexが指定できるようになった(MySQL 8.0.1以降)

- sqlite3 で boolean を t/f から 1/0 に修正
t/f で保存していた場合、手動で変更する必要がある

- order と pluck に生 SQL はダメ
Arel.sqlメソッドでラップしないとだめ

- fixtures でまとめてクエリーを実行するようになった

- SQLが発行された場所が log に出るようになった
デフォルトは開発環境のみ

- MySQL の bulk true があったけど、 PostgreSQL でもできるようになった

- PostgreSQL の foreign tables をサポート

- Errorクラスが増えてる

https://y-yagi.github.io/presen_rails_5_2_part2/#/47

- その他、 Active Record はこれを見ると良い
http://kamipo.github.io/talks/20180215-tdtech/#/title

Atcive Support

- Date#prev_occurring、Date#next_occurringメソッドが追加された
前の曜日、次の曜日を取得するメソッド

- ActiveSupport::Cache::Store に write_multi メソッドを追加
Redis adapterだとMSETを使うようになるので、一個一個書き込むよりはやい

- ActiveSupport::Testing::TimeHelpersをincludeしている場合、テストの後処理で自動でtravel_backが呼ばれるようになった
Rspec はどうするのかわからない

Railties

- rails new 時の Gemfile に bootsnap を追加

--skip-bootsnap でスキップできる

その後の飲み会

今回は知った人が多かったので飲み会にも参加!

自分の開発経験にはない話がいっぱい出てきて、とても勉強になった。前に一回参加したときも感じたけど、こういう中での話にも知らないことがいっぱい出てくるので聞いてるだけで楽しい!

また参加します!

2017年を振り返って

今年は一年マルっとフリーランス プログラマーとして活動した年だった。

周りの人には言っているが、リサーチをやっていたときには全く考えていない働き方だし、こんなに楽しく仕事ができるようになったのも想像してなかったので、今の気持ちを整理したいと思う。

フリーになりたてのころ

正確には、去年(2016年)の10月からフリーになった。

当初はリサーチ業界から転職してプログラマーとしての仕事は2年くらいで不安しかなかった。

  • こんなんで仕事できるの?
  • コード理解できるかな?
  • コード書けるかな?

この時期は、まだプログラマーなりたてだし...って自分に言い訳をしてたのもあった。

一年を振り返った今の気持ち

この一年で合計3社さんとお仕事させてもらって、不安は少しずつ減ってきた。

それぞれの会社さんで実装面で相談にのってもらったり、知識不足のところは教えてもらったり、たくさんの人に助けてもらって、それまで「俺にできるのか?」って思ってたことでも「何とかできそう!」って思えるようになってきた。

今でも不安な気持ちはあるし、アウトプット出せてないときは本当に申し訳なく思う。だからこそ、もっと勉強しないといけないと思うし、期待されて、その期待以上のものを出すプログラマーになりたいなと思う。

いつからこんなに勉強することが楽しくなったのか、自分でも不思議なくらい本を読んだりコードを書くようになったのも事実で、まだまだ楽しく仕事できるな〜って感じている。

(それぞれの会社でお世話になった方々、ありがとうございました。)

反省点

性格的に怖がりで失敗したくないので、今ある知識の中で何とかしようとする部分が多かった。

でも、一回経験しておくと二回目以降に安心して手が動くという経験を何度もしたので、新しく勉強したものを「何でそれがいいのか?」という部分をちゃんと理解しつつ、実際のプロダクトのコードで使っていくようにした方がよかった。

来年やること

フロントとインフラ

あれこれ他の言語に手を出さず、自分で意図的に Rails だけやってきたのでここは強みだと思う反面、他の言語とかフロント、インフラ周りになるとなかなかアウトプット出せないなって感じた...

フロントと最低限のインフラ知識は必要だなと思うので、この辺りは自プロダクトも含めて埋めていきたい。

RubyRails

そして、 Rails 周りのインプットを増やせば増やすほど、こんな書き方できるのか!こうやって model を分けるのか!とか新しい発見が多くて、もっと知っていい感じのコード書けるようになりたいと思った。

なので、本やサイトから RubyRails 周りのインプット・アウトプットは今後も続けつつ、OSSのコードを読んでいろんな書き方を学びつつ、OSS貢献もやっていきたい。

また、人が多いところが苦手で勉強会とかにあまり参加していなかったので、来年は参加する機会を増やしていこうと思う。

ブログ

今年は全く書いてなかった。

こんなこと書いてもな〜...って思うこともあったけど、今でも昔の記事読むと、しょーもな!って思うことで悩んだりしてたみたいなので、作業ログ的に記録を残しておくと、「この当時はこんなことで悩んどったんだな」とかあとあと振り返りがしやすいかなと思う。

自分の記録を残すって意味でも、もう少し何かしら書いていこうと思う。

自プロダクト

自分のプロダクトとして Growth Support を作った。いいプロダクトになると思っているので開発を進めてさらにいいものにしていきたい。

また、くだらないサービスもどんどん作って公開していきたいと思う。その過程で知識不足なフロントとインフラを埋めていければいいと思っている。

まとめ

そんなこんなで、今年はとても楽しい一年だった。

やることもたくさんあるし、知りたいこともいっぱいある。まだまだプログラマーとして楽しい日々が送れそうなので、来年はもっと楽しい一年にしたいと思う。

webpack2 : チュートリアルをやってみた!

最近よく聞く webpack !

いろんなプロジェクトで使われてて裏でいい感じに動いていて勉強しないとと思って数ヶ月...やりました!

バージョンが 2 になってたみたいで、チュートリアルやったら動かなくて困った...

とりあえず以下を参考に動くところまでは持っていった!

 

webpack.js.org

で、初心者なのでここからどうしよう...と思って見つけたのがこのチュートリアル!なのでこれをやってみました!

Getting Started with webpack 2 – Thinking in Code

今の状態としては、webpack の Getting Started をやって終わっている状態です!

- webpackのバージョンは 2.3.2

yarnを使う

これも聞いたことがある!パッケージマネージャらしい!

Yarn is a package manager for your code.

yarnpkg.com

以下を実行すると yarn が使えるようになるっぽい。

brew install yarn
yarn add --dev webpack webpack-dev-server@2

 

基本的には記事通りにやって行けば大丈夫だと思うけど、動かなかったところをメモしていく!

webpack-dev-server

これも当たり前なんかな?普通にターミナルに入力して動かなくて、以下のコマンドでインストール後に動いた。

npm install -g webpack-dev-server

 

extract-text-webpack-plugin

記事内では beta 版が使われているようだったけど、今は大丈夫っぽい!

npm install --save-dev extract-text-webpack-plugin

github.com

まとめ

とりあえず動くようになったけど、実際にコード書いていかないと分からないな(笑)

これからいじっていく予定!

【Rails5】devise でログイン後ページをユーザー意図に合わせて変更する

前回の記事でログイン後ページのリダイレクトを設定しました。

ログアウトした状態でログイン後の特定ページにいった場合、ログインページに遷移し、ログインするとその設定したページにリダイレクトされます。

しかし、その場合ユーザーとしてはログインするとそのログイン後の特定ページにリダイレクトされたいと思います。

今回はその機能を実装していきます。

ログイン前のページを保存する

以下のページで解説がありますので、そのとおりに実装していきます。

How To: Redirect back to current page after sign in, sign out, sign up, update · plataformatec/devise Wiki · GitHub

まず app/controllers/application_controller.rb に以下を追記します。

class ApplicationController < ActionController::Base
before_action :store_current_location, unless: :devise_controller?
before_action :authenticate_user!

...

private

def store_current_location
store_location_for(:user, request.url)
end
end

これでログイン前のページが保存されます。

ログイン後のリダイレクトを修正

次に以下のようにしてログイン後のリダイレクトを先ほどの保存したログイン前ページを使うように修正します。

  def after_sign_in_path_for(resource)
stored_location_for(resource) || mypage_root_path
end

ログイン不要ページを除く

この設定でほぼOKなんですが、ログインが不要なページからログインページに遷移した場合もこの機能が働きます。

例えば、トップからログインページに遷移した場合、ログイン後はトップに戻ります。しかし、ここではログイン後のトップ(上記例では mypage_root_path)に遷移させたいと思います。

なので、その設定を行います。なお、ここではログイン不要のページ(トップページ)を home_controller で設定しているとします。

class ApplicationController < ActionController::Base
before_action :store_current_location, unless: :devise_or_home_controller?
before_action :authenticate_user!

...

private

...

def devise_or_home_controller?
devise_controller? || controller_name == 'home'
end
end

これで home_controller は保存されなくなり、意図通り mypage_root_path に遷移します。 controller_name == 'home' の部分はもう少しやりようがある気がしてますが、ひとまず、これで動きます。

それ以外でいい方法があれば教えてください!

ということで今回はここまで。

 

devise 関連記事はこちら

【Rails5】devise でログイン機能を実装する

【Rails5】devise を日本語化する

【Rails5】devise でログイン後、ログアウト後のページを設定する