deviseのGemを動かしてみる

何をするの?

  • 公式のdeviseっていうgemを手元でリバースエンジニアリングして再利用してみる。
  • gemへの理解とOSSへの足がかりとして。

開発環境

  • deviseのgithubを手元に落としてきてvscodeのDevContainer環境で動かしてみる。

Get Started!!

deviseの用意するdevcontainerで実行環境を作ってみる。

  • git cloneしてみた。
  • .devcontainerっていうディレクトリがあることに気づき、調査するとvscodeでDockerの実行環境を構築できることが判明。
  • 早速環境を作ってみた。

Dev Containerとは

vscodeの拡張機能。
ディレクトリ内の`.devcontainer/devcontainer.json`を使ってDockerコンテナを起動する。
SSHで接続していれば、接続先のサーバーのDockerエンジンを利用してくれる。
普通のDockerとの違いがよくわからないが、こっちの方がシンプルな構成のように見える。

起動や停止はdocker composeではなくdocker psなどのdocker本体のコマンドが必要になる。

テストを実行してみる。

  1. サンプルとして1つテストファイルを選んでみた。
  2. テスト実行用のコマンドが用意されていたので使ってみた。
$ ./bin/test test/models/omniauthable_test.rb

==> Devise.orm = :active_record
Run options: --seed 35473

# Running:

.

Finished in 0.004172s, 239.6646 runs/s, 239.6646 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
  • しっかり動いてくれて感動。

新たにテストを追加してみる(Red)

  • なかなかできない・・・
  • 原因:rubyの知識が少なすぎる・・・
    • class, module, inlude???
  • でも、できた!
  • 解決策
    • moduleで名前空間を作り、classでメソッド定義し、利用時にインスタンスを生成する。
    • そして、インスタンスメソッドとして呼び出す。

deviseのコードにhello_japanメソッドを1つ仮実装してみる(Green)

  • 定型の文字列を返すだけだけど、それで良い。
  • ここではスピードは最重要。
  • 実際、メソッドをテストファイルで呼び出すのに苦労した。。
# lib/devise/japan_greeting.rb

# frozen_string_literal: true

module Devise
  class JapanGreeting
    def say_hello(name)
      return "もえさんこんにちは"
    end
  end
end

三角測量してみる

まずは以下のテストを三角測量として追加し、redになることを確認。

  test 'アリスをさん付けでこんにちはって言ってくれる' do
    assert_equal 'アリスさんこんにちは', Devise::JapanGreeting.new.say_hello('アリス')
  end
Failure:
ActiveRecordTest#test_アリスをさん付けでこんにちはって言ってくれる [/workspaces/devise/test/japan_greeting_test.rb:10]:
Expected: "アリスさんこんにちは"
  Actual: "もえさんこんにちは"
  • うん、いい感じ!

hello_japanメソッドを本実装してみる。

# frozen_string_literal: true

module Devise
  class JapanGreeting
    def say_hello(name)
      return "#{name}さんこんにちは"
    end
  end
end
# Running:

..

Finished in 0.005393s, 370.8839 runs/s, 370.8839 assertions/s.
2 runs, 2 assertions, 0 failures, 0 errors, 0 skips
  • 大成功!
  • これで、Deviseモジュールの中に定義した、JapanGreetingクラスに存在する、say_helloっていうインスタンスメソッドが機能してくれるようになった。

手元のrailsアプリでカスタムしたdeviseをgemとして読み込む

ここまでで、gemのカスタムは完了している。
でも、このGemをどうせなら使ってみたい。
ということで、githubで公開して自分のrailsプロジェクトの中でも使ってみようと思います。

まずはシンプルなrailsプロジェクトをDockerで作ってみる。

docker-compose.ymlを追加
touch docker-compose.yml
Dockerfileを追加
touch docker-compose.yml
FROM ruby:3.1.2

# 必要最低限のツールを入れる
RUN apt-get update -qq && apt-get install -y sqlite3 vim nodejs npm sudo

# 最新のnodeを入れる
RUN npm install n -g
RUN n stable

# yarnだけはバージョン指定
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update && apt-get install yarn

# railsをインストール
RUN gem install rails -v 7.1.3.2

# アプリケーションディレクトリを作成
RUN mkdir rails

# アプリケーションディレクトリを作業用ディレクトリに設定
WORKDIR /rails
# ADD Gemfile ./Gemfile
# ADD Gemfile.lock ./Gemfile.lock
# RUN bundle install
ADD . .
EXPOSE 3000
railsアプリの作成
rails _7.1.3.2_ new . --database=sqlite3
hello_japanメソッドが使えることを確認する。
docker compose exec rails rails test
README.mdに実行手順を追加

README.mdにはプロジェクトの節目とビルド・テストの実行手順のみシンプルに記述。

作業を終えて

このプロジェクトの目的である以下の項目は、最低限達成できたかなと思います。

  • 公式のdeviseっていうgemを手元でリバースエンジニアリングして再利用してみる。
  • gemへの理解とOSSへの足がかりとして。

合計で5時間くらいかかった気がしますが、初回にしては上出来ではないでしょうか。

ただ、もっとスムーズにやれたと思います。
はまったポイントと次回からの対策は以下。

deviseのgemにメソッドを追加できない

そもそも、どうやってメソッドを追加するのかよくわかっていませんでした。

Devise::JapanGreeting.new.say_hello()

こんな感じで、Deviseの名前空間の中にJapanGreetingクラスがあり、その中にsay_hello()メソッドがある。
JapanGreetingクラスをnewしてインスタンスを生成した後に、インスタンスメソッドとしてsay_hello()メソッドを呼ぶ。
こういう流れを理解していませんでした。
クラスメソッドなんてあんまり普段意識して使わないので良い勉強。

作ったファイルを読み込めない

メソッドを作ったものの、自作のテストファイルでそれを読み込むのにも苦労しました。
単に.rbファイルを作っただけじゃもちろん読み込まれませんからね。

devise.rbの中でoutloadしてやる必要がありました。
outloadの際も、moduleで名前空間が分かれていて、ファイルの階層と対応していました。
今回はdevise.rbと同じ名前空間、同じ階層にファイルを配置することでどうにか対応。

test_helper.rbを辿っていくと、deviseクラスを読み込んでいたのでそこからは自動で読み込んでくれていました。

RailsというかRubyの領分ですが、もっと勉強が必要です。

RailsプロジェクトをDockerで作れない

最後のはまりポイントはカスタムしたGemをRailsプロジェクトで検証する際に、そもそもRailsプロジェクトを作れないというものでした。
Dockerを使っていたのですが、久々にRailsプロジェクトを作ろうとしてDockerfileの書き方がおかしいのか全然ダメ。

結局、railsのgemとその他周辺ツール(nodeとか)が入ったコンテナでrails newすることでrailsプロジェクトをスタートできました。
GemfileやGemfile.lockはそこから設定しなおした感じですね。

railsプロジェクト立ち上げ後、カスタムしたdeviseのインストールや追加したメソッドの呼び出しは問題なくスムーズに完了できました。

以上、作業を終えてみて、他者の作ったコードにここまで向き合ったことは正直あまりなく、これまでは応急措置の連続でコーディングしていたなあと感じました。
コードを深く読み込むことなく、qiitaの記事なんかを参考にできることだけで実装を終えていました。

でも、今回の作業はそもそもdeviseのgemを手元で動かすところからドキュメントが見つからずに苦労しました。
初めてDev Containerも使ったので、そこもちょっと難しかった。

でも、全体的に、OSSっぽいことができたのでかなり勉強になりました。

残った課題

  1. Dev Containerを使ったGem環境の構築方法。(今は使うだけだけど、作れるようになりたい)
  2. より実用的なテストを書きたい(今はシンプルなテストのみ)
  3. そもそももっとコードを読めるようになりたい(正直、今回は必要な箇所しか読んでいない)

もっと「コードを読む」というスキルを上げていきたいですね。
生産性を飛躍的に上げるには、読む技術が必要です。