search
LoginSignup
20

More than 5 years have passed since last update.

posted at

Dive into Rubygems

Dive into Rubygems

by pocke
1 / 47

Dive into Rubygems

2017/8/24(木) Rails Developers Meetup #4
https://rails-developers-meetup.connpass.com/event/62792/


自己紹介

pocke.png

桑原 仁雄 (pocke)


[宣伝] SideCI

弊社ではSideCIというサービスを作っています。

170821153556.png

GitHub で Pull-Request を投げると、それに対して自動でレビュー


[宣伝] アビシニアン

SideCIではアビシニアンという便利UIで、ツールの指摘を無視することが可能

例: ツールの誤検知を無視したい時

170821172327.png

無視する理由を書いてclose
170821172407.png


本題: Dive into Rubygems


Agenda

  • 何故 Gem に飛び込むのか
    • Gem のコードを読み書きする機会がある
  • Gem の歩き方
    • Gemの読み書き
    • Gemの作り方

  • RuboCop の話はしません
  • Rails の話はしません

何故 Gem に飛び込むのか


Ruby on Rails で作られたアプリケーションは、沢山の Gem の上に成り立っている。

# rails new ホヤホヤでも71個
$ rails new hogehoge
$ cd hogehoge
$ bundle exec gem list | wc -l 
71

# sideci のレポジトリでは251個
$ cd sideci
$ bundle exec gem list | wc -l 
251

沢山のGemに依存している分、Gemのコードを読み書きする機会も多い


Gem のコードを読む機会


Gem のコードを読む機会

Gemのコードを読むことで、素早く問題解決をすることが出来ることがある


例: アプリケーションの挙動が怪しい時

  • 自分の書いたコードが悪いのかも知れない
    • この場合は自分のコードを読めば良い
  • Gem のバグかも知れない
  • Gem の使い方が悪いのかも知れない
    • この場合はGemのコードを読んだほうがいいかも知れない

Gem のコードを読んだ実例

docker-api gem を使って、プライベートなdocker repositoryの検索をしたかった。


こんな感じのコードが既にあったので、

# 認証をする
Docker.authenticate!(username: name, password: pass)
# docker pull をする
Docker::Image.create(fromImage: image_name)

こんな感じに行けるのでは!

# 認証をする
Docker.authenticate!(username: name, password: pass)
# 検索をする
Docker::Image.search(term: image_name)

と思ったけど、何故か public なレポジトリしか検索できない。


原因

しばらく混乱していたけど、docker-api のコードを読んだら分かった。

Docker::Image.create の定義 (GitHub)

    def create(opts = {}, creds = nil, conn = Docker.connection, &block)
      credentials = creds.nil? ? Docker.creds : creds.to_json
      headers = credentials && Docker::Util.build_auth_header(credentials) || {}

Docker::Image.search の定義 (GitHub)

    def search(query = {}, connection = Docker.connection)
      body = connection.get('/images/search', query)
      hashes = Docker::Util.parse_json(body) || []

.search の方はクレデンシャルが使われていなかった……!

Gem のコードを眺めることで問題が解決できた


Gem のコードを書く機会


Gem のコードを書く機会

Gem のコードを読む機会も多いけど、書く機会も多い。

  • 踏んだバグを直す時
  • 機能追加をする時
    • Monkey Patch ではなく Gem を修正する
      • 多くの人がそのコードの恩恵を受けることが出来る
      • Gem のアップデートをしやすくなる
  • Gem を作りたい時

Gem の歩き方


Gem の歩き方

8割ぐらいのGemでは通用する方法を紹介します

  • clone
  • ディレクトリ構成
  • 依存関係
  • インストール
  • Gemの作り方

Gem を clone する


Gem を clone する

  • コードを読む際、手元にコードがあると便利
    • Gem としてインストールはされているけど、Git管理下にあるコードも持っていると更に便利
      • grep しやすい
      • すぐに手を加えられる
      • 変更履歴とかも追える

amatsuda/gem-src

  • gem install した時に git clone が走る
    • 何も考えなくても手元にコードがあって便利
  • motemen/ghq と組み合わせると更に便利
    • Go like なレポジトリ管理ツール
    • Go を使っていなくても便利

詳しくは
http://koic.hatenablog.com/entry/2016/10/07/000000
https://www.slideshare.net/koic/ghq-gemsrc-andmore?ref=http://koic.hatenablog.com/entry/2016/10/07/000000

DEMO


Gemのディレクトリ構成


Gemのディレクトリ構成

  • lib/
  • test / spec
  • exe / bin

lib/

  • メインのコードが置かれている
  • ここがrequireする際のエントリーポイント
    • require 'foo' すると、各Gemのlib/foo から最初に見つかったものが require される
    • そのため、基本的にlib/直下にはGEMNAME.rbGEMNAME/しか置かれていない
    • $LOAD_PATH について調べるといいと思う
  • また、それ以下のディレクトリ構成はクラス名の階層と対応する。
    • 例: foo gem の Foo::BarBaz::Hoge class は、lib/foo/bar_baz/hoge.rbに定義されている(ことが多い)
      • "慣習"なので、必ずしもこうなっているとは限らない

spec/, test/

  • テストコードが書かれている。Rails と同じですね
  • test の場合は minitest, spec の場合は RSpec のことが多い
  • ディレクトリ構造はlib/とだいたい同じ
  • テストコードを読むことで Gem の使い方が分かることもあるので、意外と読む機会がある
    • テストコードは動くexample

exe / bin

  • Gem をインストールした際に一緒にインストールされる実行ファイルが置いてある
  • 例) RuboCop であれば、bin/rubocopにコマンドの定義が置いてある。 See. bin/rubocop
  • 最近作られた Gem だとexeに置かれていることが多い

Gemの依存関係


Gemの依存関係

主に登場人物は3人。

  • GEMNAME.gemspec
  • Gemfile
  • Gemfile.lock

gemspec

Gemの情報が色々書いてあるファイル。

  • gemの名前
  • author
  • license
  • etc...

依存するGemの情報もここに書く

# gem install した時に、一緒にインストールされるgem
s.add_runtime_dependency('rainbow', '>= 2.2.2', '< 3.0')
# gem の開発時のみに使用されるgem。Railsで言うところのdevelopment groupみたいなやつ。
s.add_development_dependency('rspec', '~> 3.6.0')

Gemfile

  • Rails application では定番
  • Gemの開発においては殆ど使われないことも多い
  • Gem のディレクトリ直下で bundle install した時に見られるのはこいつ
  • そのため、ここに開発時にのみ使うGemを書くこともある(add_development_dependencyと同じ)
    • Gemfileに書いておけば、gem installされた時には使われない

Gemfile.lock

  • Rails application ではだいたいVCSにcommitされている
    • 何故ならば、Gemのバージョンを固定してアプリケーションを運用したいから
  • Gem ではVCSにcommitすることは避けられている
    • 何故ならば、多くのアプリケーション(==多くのGemのバージョン)で動いてほしいから

Gem をインストールする


Gem をインストールする

Gem をインストールする為のrake taskが用意されている。
これを使って手を加えたGemをローカルにインストールすることが出来る。

# Gem のコードを編集
$ bundle install
$ bundle exec rake install:local

8割ぐらいのGemではこの方法が使える


Gem をインストールする(manually)

  • rake task は bundler によって提供されているものなので、bundler を使って作られていない gem などでは使えないこともある。
  • そんな時は、gem buildする。
$ gem build GEMNAME.gemspec
# gem がビルドされ、GEMNAME-VERSION.gem ファイルができる
$ gem install --local GEMNAME-VERSION.gem

tmpgem の紹介

Gem を一時的に install するための gem

  • デバッグ用にbinding.pryを仕込んだGemを、インストールしたままにする事故を防げる
  • Gem の開発と、Gemの使用を分離できる

DEMO


Gem を作る


Gem を作る

基本的な流れ

  1. bundle gem GEMNAME
  2. Edit GEMFNAME.gemspec
  3. Write gem code
  4. bundle exec rake install:local
  5. bundle exec rake release

時間があったら、DEMOしながらやる


1. bundle gem

  • gem の雛形を作る。
  • rails new みたいな。
$ bundle gem foo
Creating gem 'foo'...
      create  foo/Gemfile
      create  foo/lib/foo.rb
      create  foo/lib/foo/version.rb
      create  foo/foo.gemspec
      create  foo/Rakefile
      create  foo/README.md
      create  foo/bin/console
      create  foo/bin/setup
      create  foo/.gitignore
Initializing git repo in /path/to/foo

2. Edit gemspec

bundle gemしただけでは、gemをビルドすることは出来ない。

  • descriptionなどからTODOを消す
    • => description, summary, homepage を書く
  • if spec.respond_to?(:metadata)... の部分をなんとかする
    • rubygems.org に公開するなら、バッサリ消す
    • 公開しないなら、ホスト名を指定する

3. Write gem code


4. install

  • bundle exec rake install:local すると、gem installしたものと同じように扱える
  • install したらデバッグしよう

5. release

  • gem が出来たら、rubygems.org にpublishしよう

まとめ


まとめ

  • Railsアプリケーションを作る上でも、Gemのコードを読み書きすることは結構ある
  • このスライドはTips集なので、なにかあったら読み返して欲しい

ご清聴ありがとうございました。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
20