こんにちは、今年の4月に中途で入社しました下山です。
ベーシックでは大半のwebサービスがRuby on Railsで作られており、私自身、入社後約1ヶ月ちょっとはRailsをやっていたのですが、途中からPHPのサービスに移り、最近またRailsに関わるようになってきました。
前職ではPHPでシステム開発をしていため、Ruby、Railsは全くのド素人です。
おそらくまだ数十行しかコードを書いておりません・・・
今回はタイトルにもあるように、
とあるgemの仕様を調べようと思ったが結局作り方を調べることにした件について書いていきたいと思います。
とあるgemの仕様を調べようと思ったきっかけ
先日メンバーにコードレビューを依頼したところ、ページネーションを手軽に実装できる kaminari というgemのメソッドについてこのような質問をいただきました。

コードには.page(params[:page])と書いていたため、pageってどっちのこと?と思ったのですが、ここでは、 params[:page]の存在確認はしなくていいの? という意味でした。
ふむ・・・知らない・・・
ただ、引数に2以上の数字が入っていなければ普通に1ページ目を表示して欲しいよなぁ
てかOFFFSET 0でクエリ作ってくれるんじゃないの知らないけど、と思いつつクエリを確認してみました。
結果は・・・

ほら!!やっぱりだ!!!OFFFSET 0入ってる!!!!
と思ったのつかの間、何でOFFFSET 0が入るんだろう?という疑問にとらわれてしまいました・・・
kaminariの仕様を調べてみた
ということで、kaminariのソースを調べてみようと思ったのですが・・・
みなさんもうお忘れかもしれませんが、
私、
RubyもRailsもド素人でございます。
どのソースから追っていけば良いのかさっぱりわかりません!!!!!
もうこうなったら、gemの作り方から調べるしかない!
gemの作り方をさらっと調べてみた
そもそもgemって何?
普段何となく使っているが、多大な恩恵を受けているgemって何なんだろうとふと思ったので調べてみました。
The software package is called a “gem” which contains a packaged Ruby application or library.
引用 https://guides.rubygems.org/
ふむふむ、簡単にいうとライブラリのことですか。(←完全にlibraryという単語だけで判断していま・・・)
作り方
gemをインストールする際には、bundlerを利用することが大半かと思いますので、今回はbundlerを利用したgemの作り方を追っていきたいと思います。
bundlerのバージョンは1.16.6を使用しております。
まず、
# bundle gem [gem名]
$ bundle gem greet
を実行すると下記ファイルが生成されます。
create  greet/Gemfile
create  greet/lib/greet.rb
create  greet/lib/greet/version.rb
create  greet/greet.gemspec
create  greet/Rakefile
create  greet/README.md
create  greet/bin/console
create  greet/bin/setup
create  greet/.gitignore
これでgemの雛形が作られました。
続いて、
lib/[gem名].rbに処理を書いていきます。
今回は'Hello World!'と出力するようにします。
require "greet/version"
module Greet
  # Your code goes here...
  def self.greet
    puts 'Hello World!'
  end
end
実際に出力されるか試してみます。
# irbの起動
$ ruby greet/bin/console
irb(main):001:0> Greet.greet
Hello World!
無事出力されましたね。
lib/[gem名].rbファイルについて、
lib/foodie.rb:~This is the file that will be required by Bundler (or any similarly smart system) when our gem is loaded. ~
引用 https://bundler.io/v1.16/guides/creating_gem
と記載されているように、gemがロードされたらこのファイルが要求されます。
ということは、このファイルから見ていけばkaminariの仕様を追っていけそうですね!
gem名の命名規則については、こちらに推奨されてる規則がございます。
今回は、kaminariの仕様を調べるためでしたので、.gemspecや、一般公開の方法はまたの機会にしたいと思います!
改めてkaminariの仕様を調べてみる
lib/kaminari.rbの中身を見てみると、
# frozen_string_literal: true
require 'kaminari/core'
require 'kaminari/actionview'
require 'kaminari/activerecord'
と書いてあります。
なるほど、kaminari-core、kaminari-actionview、kaminari-activerecordのgemを呼び出しているようです。
ということは次は、
kaminari-core/lib/kaminari/core.rb
kaminari-actionview/lib/kaminari/actionview.rb
kaminari-activerecord/lib/kaminari/activerecord.rb
を見ていけばよさそうです。
このようにして順を追って見ていくと・・・
ついに下記ファイルに目的のものを見つけました!
kaminari-activerecord/lib/kaminari/activerecord/active_record_model_extension.rb
limit(per_page).offset(per_page * ((num = num.to_i - 1) < 0 ? 0 : num)).extending do
これだ!!
nil.to_iは0であるため、numにnilが入っていた場合、num.to_i - 1の式は必ずマイナスになる、そして後述の三項演算子により.offset(0)となる!!!
あーすっきり!!!
感想
調べるの大変だった・・・英語も苦手だし・・・作るの大変だったんだろうなぁ・・・
gemの構成など全く気にも留めずひたすら恩恵だけを受け甘い汁を吸っていた自分を殴ってやりたい。
調べるきっかけをくれたレビュワーに感謝!!!
今後はしっかりありがたみを感じつつgemを使っていこうと思いました!
そしていつか有用なgemを自分の手で・・・
参考リンク
kaminari
How to create a Ruby gem with Bundler
NAME YOUR GEM
