はじめての自作gem 「Hello, Tama!」を出力してみる

(2021/10/5追記)
ありがたいことに今もこの記事を読んでくださる方がいるのですが、2021年現在では少し古い情報が含まれていたり、内容的にそもそもあまり意味のなさそうな記述が含まれていたりするため、この記事を読まれる方は以下のポストにも目を通してくださると良いかと思います。

[新しいgemをつくる](https://github.com/shioimm/til/blob/master/ruby/rubygems/__new_gem.md)

こちらは2021年7月時点で新しいgemを作成する際、自分用に書いたメモです。
内容的に指摘がありましたらぜひPRなど下さい:pray:

## はじめに
こんにちは！Tama.rbから @fuqdaさんの圧力を受け、はじめてのアドベントカレンダーを書くことになりました、Webエンジニア一年生のしおいです。

最近は「まだまだ弱い自分が、コミュニティに貢献できるまで成長するためにはどうすれば良いのだろう」…と悩んだりなどしております。
こちらに対して、Asakusa.rb（Tamaじゃない）にて激つよRubyistの方から**「簡単なgemを作ってみたりして、知識の隙間を埋めていくと良いよ」**とのアドバイスをいただきました。

すでに詳細な記事もありますが、せっかくなのでこちらで公開をさせていただきます。

## お題
**「Hello, Tama!」と出力するだけの簡単なgemを作ってRubyGemsにリリースしてみる**

## 1. 準備
① [GitHub](https://github.com)で[新しいリポジトリ](https://github.com/new)を作る
② [RubyGems](https://rubygems.org)にアカウントを登録
③ gemをupdate（以下を参照）

```console
# gemを最新版へupdate
$ gem update --system
# bundlerも同じく
$ gem update bundler
```
## 2. 雛形を作成する
gemの雛形は、コマンドで簡単に作成できます。

```console
# hello_tamaという名前のgemの雛形を作る
$ bundle gem hello_tama
```
この後、ターミナル上でいろんなことを訊かれます。

```
Do you want to generate tests with your gem?
Type 'rspec' or 'minitest' to generate those test files now and in the future. rspec/minitest/(none)
# gemにテストファイルを追加しますか？
# （追加する場合はrspecかminitestと入力）
```
今回はテストを使用しないため、何も入力せずにそのままEnter。

```
Do you want to license your code permissively under the MIT license?
This means that any other developer or company will be legally allowed to use your code for free as long as they admit you created it. You can read more about the MIT license at https://choosealicense.com/licenses/mit. y/(n)
# MITライセンスにしますか？
```
こちらは`y`を入力。
MITライセンスの内容は[こちら](https://choosealicense.com/licenses/mit/)で確認できます。

```
Do you want to include a code of conduct in gems you generate?
Codes of conduct can increase contributions to your project by contributors who prefer collaborative, safe spaces. You can read more about the code of conduct at contributor-covenant.org. Having a code of conduct means agreeing to the responsibility of enforcing it, so be sure that you are prepared to do that. Be sure that your email address is specified as a contact in the generated code of conduct so that people know who to contact in case of a violation. For suggestions about how to enforce codes of conduct, see https://bit.ly/coc-enforcement. y/(n)
# code of conduct に関するファイルも一緒に作成しますか？
```
`code of conduct` = 行動規範
`y`にした場合、CODE_OF_CONDUCT.mdというファイルが追加されます。
ファイルの内容はこちらの記事で翻訳を確認できます。
 [Contributor Code of Conduct](https://qiita.com/nstoym/items/ca711287117074a810aa)

**これらの質問に答えると、次のファイルが生成されます。**

```
      create  hello_tama/Gemfile
      create  hello_tama/lib/hello_tama.rb
      create  hello_tama/lib/hello_tama/version.rb
      create  hello_tama/hello_tama.gemspec
      create  hello_tama/Rakefile
      create  hello_tama/README.md
      create  hello_tama/bin/console
      create  hello_tama/bin/setup
      create  hello_tama/.gitignore
      create  hello_tama/LICENSE.txt
      create  hello_tama/CODE_OF_CONDUCT.md
```
## 3. gemspecを設定
`hello_tama`ディレクトリの中に`hello_tama.gemspec`というファイルができています。
こちらにGemの設定を書き込んでいきます。

ファイルを開くとこんな感じ。

```ruby:hello_tama.gemspec
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "hello_tama/version"

Gem::Specification.new do |spec|
  # gemの名前
  spec.name          = "hello_tama"
　　 # gemのバージョン　lib/hello_tama/version.rb と言うファイルで管理されている
  spec.version       = HelloTama::VERSION
  #  $ git configで取得可能なuser.name 
  spec.authors       = ["tamataro"]
  #  $ git configで取得可能なuser. email 
  spec.email         = ["tama@example.com"]

  # 以下は`TODO`の文字が入っているとgemをbuildする際にエラーが出てしまうため、内容を適宜書き換えます。
  # gemの説明（要約）
  spec.summary       = %q{TODO: Write a short summary, because RubyGems requires one.}
  # gemの説明
  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."
  # ライセンス
  spec.license       = "MIT"

  # 以下はプライベートgemを誤ってrubygems.orgに登録してしまい、他人にインストールされてしまうことがないように制御するための設定。
  # 今回はRubyOrgに登録したいため、削除します。

　　　　# --　ここから　--
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
  # to allow pushing to a single host or delete this section to allow pushing to any host.
  if spec.respond_to?(:metadata)
    spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"

    spec.metadata["homepage_uri"] = spec.homepage
    spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."    spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
  else
    raise "RubyGems 2.0 or newer is required to protect against " \
      "public gem pushes."
  end
　　　　# --ここまで--

　　　　# 以下はその他の詳細な設定

  # Specify which files should be added to the gem when it is released.
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
  # gemパッケージに含まれる対象となるファイルのリスト
  spec.files         = Dir.chdir(File.expand_path('..', __FILE__)) do
    `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
  end

　　　　# 実行可能なコマンド名を格納しているディレクトリの設定
  spec.bindir        = "exe"

  # gemパッケージに含まれる実行可能コマンドとしてのファイルのリスト
  spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }

　　　　# ルートとなるパスの設定
  spec.require_paths = ["lib"]

　　　　# 依存gemがある場合はここに追加
  # （デフォルトでは開発環境用に`bundler`と`rake`が指定されています）
  spec.add_development_dependency "bundler", "~> 1.17"
  spec.add_development_dependency "rake", "~> 10.0"
end
```

編集後のファイルはこんな感じです。

```ruby:hello_tama.gemspec
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "hello_tama/version"

Gem::Specification.new do |spec|
  spec.name          = "hello_tama"
  spec.version       = HelloTama::VERSION
  spec.authors       = ["tamataro"]
  spec.email         = ["tama@example.com"]

  spec.summary       = %q{Puts 'Hello, Tama!'}
  spec.description   = %q{Puts 'Hello, Tama!'}
  spec.homepage      = ""
  spec.license       = "MIT"
  spec.files         = Dir.chdir(File.expand_path('..', __FILE__)) do
    `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
  end
  spec.bindir        = "exe"
  spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
  spec.require_paths = ["lib"]

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


## 4. 実際のコードを記述
実際のコードは`lib/hello_tama.rb`の中に記述していきます。
今回は`Hello, Tama!`を出力するだけのコードなので、こんな感じ。

```ruby:lib/hello_tama.rb
require "hello_tama/version"

module HelloTama
  class Error < StandardError; end
  # ここから下に記述
    def greets
      puts 'Hello, Tama!'
    end
  end
end
```

## 5. Bundle Install
作ったgemのディレクトリに移動して、`bundle install`してみます。

```console
$ cd hello_tama/
$ bundle
Fetching gem metadata from https://rubygems.org/.
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies...
Fetching rake 10.5.0
Installing rake 10.5.0
Using bundler 1.17.1
Using hello_tama 0.1.0 from source at `.` # ←注目！
Bundle complete! 3 Gemfile dependencies, 3 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
```
成功しました！

## 6. ちゃんと動くか確認
```console
$ irb
2.5.3 :001 > require 'hello_tama'
 => true 
2.5.3 :002 > HelloTama.greets
Hello, Tama!
 => nil 
```
動いた！

## 7.　RubyGemsにリリースするための準備

Gitにここまでの変更を保存し、あらかじめ作っておいたGitHubのリポジトリにpushします。

```console
$ git init
$ git add -A
$ git commit -m "Create new gem"
$ git remote add origin git@github.com:アカウント名/hello_tama.git
$ git push origin master
```

続いて[RubyGems](https://rubygems.org)に登録する準備です。
SignInし、自分のプロフィールページにアクセスしたら`Edit Profile`をクリック。
少し下の方へスクロールすると、API Keyとログイン用のコマンドの記載があります。
![スクリーンショット 2018-11-28 20.32.47.png](https://qiita-image-store.s3.amazonaws.com/0/286075/bb8ae310-35df-9c12-a22a-74bfd7bdb85b.png)
`Your API key is`以下に記載されているAPI keyを、`~/.gem/credentials`ファイルの中で指定します。
<font color="Red">**（API keyが記述されたファイルをGitHubにpushするなど、第三者の目に触れる場所に置かないように注意！）**</font>

```~/.gem/credentials
:rubygems_api_key: [自分のAPI key]
```


さらに`curl`コマンドをターミナルで実行して、RubyGemsにログインします。

```console
$ curl -u アカウント名 https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials;  ~/.gem/credentials 
```
この時RubyGems.orgに登録しているパスワードを確認されます。
<font color="Red">入力したパスワードが間違っていた場合もエラー表示等は出ないため、</font>きちんとログインできているか次のコマンドで確認します。

```console
$ cat ~/.gem/credentials

# ログインできていない場合
HTTP Basic: Access denied.

# ログインできている場合
---
:rubygems_api_key: [自分のAPI key]
```


## 8. ついにRubyGemsにリリース
gemをビルドします。

```console
$ rake build
...
```

エラーが出ていなければ、続いてgemをリリースします。

```console
$ rake release
...
Pushed hello_tama 0.1.0 to rubygems.org
```
無事リリースに成功しました！
![スクリーンショット 2018-11-30 19.15.11.png](https://qiita-image-store.s3.amazonaws.com/0/286075/fe920f1d-86a2-f998-5c31-2b1a9a6ac144.png)
動作確認してみる…

```console
$ gem install hello_tama
Fetching: hello_tama-0.1.0.gem (100%)
Successfully installed hello_tama-0.1.0
1 gem installed

$ irb
2.5.3 :001 > require 'hello_tama'
 => true 
2.5.3 :00２ > HelloTama.greets
Hello, Tama!
 => nil 
```
動きましたー！🙌

## 最後に
Gemの雛形を作るためのコマンドが用意されているため、初心者でも簡単にGemを作って公開することができました！
たくさんの方に使っていただけるGemを作ることはRubyエンジニアのロマンですが、最初の一歩はこんなところから、自分だけのお役立ちGemを作るところからはじめてみるのも良さそうです。

## 余談
この記事を作成するにあたって大変参考にさせていただいた記事、同じTama.rb参加メンバーの方の執筆記事であった…（8割書き終えた後に気づいた）

## 参考

- [hello_tama](https://github.com/shioimm/hello_tama)

**更なる詳細はこちらをどうぞ**

- [パーフェクトRuby](https://www.amazon.co.jp/%E6%94%B9%E8%A8%822%E7%89%88-%E3%83%91%E3%83%BC%E3%83%95%E3%82%A7%E3%82%AF%E3%83%88Ruby-Ruby%E3%82%B5%E3%83%9D%E3%83%BC%E3%82%BF%E3%83%BC%E3%82%BA/dp/4774189774)
- [はじめての Ruby gem 作成が死ぬほど簡単で笑えた件 (失敗談もあるよ)](https://qiita.com/YumaInaura/items/90a3d02342486a62da43)
- [RubyGemsの作り方](https://qiita.com/chamao/items/9813e18d82b51c99422c)
- [Contributor Code of Conduct](https://qiita.com/nstoym/items/ca711287117074a810aa)
- [rubygemsに登録するときにはspec.metadata['allowed_push_host']あたりを消しておこう](https://qiita.com/tossh/items/08e7165e730dbc1a0e2e)