Gemの作り方：ActionMailerSandboxというGemを初めて作ってみたので、作り方をまとめた


Gemを初めて作ってRubyGemsで公開したので、作業メモと感想をまとめてみた。

- [actionmailer-sandbox](https://rubygems.org/gems/actionmailer-sandbox)

作ったGemはなんてことはないもので、ActionMailerのメール送信先を強制的に変更するもの。開発時に飛ばすメールを全部自分の手元に飛ばすために作った。

# Gemの作り方

## Gem作成コマンド

RailsにもRailsプラグインがあり、そちらでもGemのスケルトンを作れるみたいだけど、Raisl用だろうと普通のGemだろうと、bundleコマンドで作るのが良いと思う。

```
$ bundle gem newgem -t
```

なお、Railsプラグインの作り方は以下のコマンド。作成される物の構成は大きく変わらない。

```
$ ./bin/rails plugin new newplugin
```

## Gemの初期設定

`hoge.gemspec`というファイルがあり、それがGemの設定ファイル。

```newgem.gemspec
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'newgem/version'

Gem::Specification.new do |spec|
  spec.name          = "newgem"
  spec.version       = Splug::VERSION
  spec.authors       = ["zaru@sakuraba"]
  spec.email         = ["user@example.com"]

  spec.summary       = %q{TODO: Write a short summary, because Rubygems requires one.}
  spec.description   = %q{TODO: Write a longer description or delete this line.}
  spec.homepage      = "TODO: Put your gem's website or public repo URL here."

  # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
  # delete this section to allow pushing this gem to any host.
  if spec.respond_to?(:metadata)
    spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
  else
    raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
  end

  spec.files         = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
  spec.bindir        = "exe"
  spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
  spec.require_paths = ["lib"]

  spec.add_development_dependency "bundler", "~> 1.10"
  spec.add_development_dependency "rake", "~> 10.0"
  spec.add_development_dependency "rspec"
end
```

TODOと書かれている所を埋めていくと良い。具体的には以下の3つ。homepageにはGitHubのリポジトリを指定しておけばOK。全て英語で書く。

- spec.summary
- spec.description
- spec.homepage

もし今から作ろうとしているGemは非公開、プライベートなGemだ！っていう場合は、`spec.metadata['allowed_push_host']`にpushしてもよいホストを指定しておこう。誤操作で公開してしまうことを防ぐことが出来る。そうじゃなくて、全世界に公開だ！という場合は、`if spec.respond_to?`の下りから全部消しても構わない。

## Gemのデバッグ方法

普通にRspecでテストが書けるので、普通にテストすれば良い。

```
$ bundle exec rspec
```

### Gemを組み込んで動作テストしたい

作ったGemをとりあえずテスト的に導入したいときがあります。その場合は、すでに編集したファイルをコミット済みの上で、以下のコマンドを実行するとローカルにインストールされます。

```
$ bundle exec rake install
```

使って見るには`require`で呼び出せばOKです。なぜかpry環境下だと読みこむことが出来なかったので、irbで試しています。

```
$ irb
> require `newgem`
```

## GemをRubyGemsに公開する

動作テストも終わり、いよいよ公開したいということになりました。まずは、RubyGems.orgにアクセスしユーザ登録をします。簡単です。そして、[マイページ](https://rubygems.org/profile/edit)にアクセスをして、設定ファイルのダウンロードコマンドをゲットします。

```
$ curl -u zaru https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials; chmod 0600 ~/.gem/credentials
```

こんなやつ。

そしたらいよいよリリースする。

```
$ bundle exec rake release
```

### RubyGemsのGemを更新したい

何らかのバグ解消や新機能の追加でRubyGemsに公開したGemを更新したいという時には、Gemのバージョンを上げる必要がある。

```lib/splug/version.rb
module Splug
  VERSION = "0.1.0" # ここのバージョンを変更する
end
```

あとはコミットしてプッシュしてからの`rake release`コマンドで完了。簡単ですね。

## Gem独自の設定ファイルを作りたい

Gem自身にはデフォルトの設定があり、利用者側がそれを上書きできるような設定ファイルが欲しい。よくあるやつです。こんな時にもActiveSupportが強力な味方になってくれる。

```lib/newgem/config.rb
require 'active_support/configurable'
module Newgem
  def self.configure(&block)
    yield @config ||= Newgem::Configuration.new
  end

  def self.config
    @config
  end

  class Configuration
    include ActiveSupport::Configurable
    config_accessor :hoge_configure
  end

  configure do |config|
    config.hoge_configure = "hoge"
  end
end
```

`active_support/configurable`を使うことで簡単に設定ファイルの下準備が可能になる。この例だと`hoge_configure`という設定キーを作り、Gem上ではデフォルト`hoge`が指定されている。要はブロックを渡して中身を上書きする感じ。

```lib/newgem.rb
require "newgem/version"
require "newgem/config"

module Newgem
  class Newgem
    def example
      p Newgem.config.hoge_configure
    end
  end
end
```

Gem内部から設定値を使用するには`Newgem.config.hoge`といった形で呼び出すことが出来る。

### 利用者側

Rails環境下の例になるが、こんな感じで上書き設定が可能になる。

```Rails.root/initializers/newgem.rb
Newgem.configure do |config|
  config.hoge_configure = 'piyo'
end
```


# Gemを作ってみて

今回作ったのはRailsに付属しているActionMailerのサンドボックス化するGemで、中身は単純にモンキーパッチでしかなく、モンキーパッチならGemじゃなくて良いじゃんとか思ったんだけど、プロジェクト作るたびにモンキーパッチ書いてたら面倒だなーと思ってGemにしてみた。この作法がGem界隈で正しいのかは知らない。

でもGemにして公開したことで同僚に簡単に勧められるようになったし良かったと思っている。Gem作成の第一歩としては、とても小さくて踏みやすい一歩。作ってみると思っていた以上に簡単だったし、公開自体もなんのことはない感じ。

無駄にGemを作る意味はないけど、なんらかの目的や役割が果たせるのなら、ガンガン作っていっていくと良いような気がした。