gemを作ってみた！



## はじめに
この記事は「RUNTEQ Advent Calendar 2023」の25日目です🎂
現在プログラミングスクールで学習中のnishiharaと申します！

今回のアドベントカレンダーのテーマは「初めて学んだ技術」。
前々から個人でgemを作れると聞いていて今回初めて作ってみました！
gemの内容は登録したスニーカー👟をランダムで選んでくれるものです！
（クリスマスプレゼントがスニーカーで、それがきっかけでハマってしまった感じですかね😂）

:::note warn
今回の記事はプログラミング初学者が作成したものとなっています。
間違えや適切でない記述があるかもしれませんのでご了承ください。
気になる点がございましたらコメントなどで教えていただけると嬉しいです。
:::

## 作っていきます！

ターミナルを開き、
```
bundle gem [プロジェクト名]
```
で作れます。

今回プロジェクト名は「sneaker_selector」にしますので、
```
bundle gem sneaker_selector
```
でOKです!

**注意点として、すでに使われている名前は使用できないです。**
確認方法としては[RubyGems](https://rubygems.org)でgemを検索できます。




作成途中で出てくる選択肢では、CircleCIやGitHub Actionsのセットアップは行いません。
また、RuboCopも設定せず、MITライセンスとCODE_OF_CONDUCTは有効にしました。




```
$ bundle gem sneaker_selector
Creating gem 'sneaker_selector'...
Do you want to set up continuous integration for your gem? Supported services:
* CircleCI:       https://circleci.com/
* GitHub Actions: https://github.com/features/actions
* GitLab CI:      https://docs.gitlab.com/ee/ci/

Future `bundle gem` calls will use your choice. This setting can be changed anytime with `bundle config gem.ci`.
Enter a CI service. github/gitlab/circle/(none): n
MIT License enabled in config
Code of conduct enabled in config
Do you want to include a changelog?
A changelog is a file which contains a curated, chronologically ordered list of notable changes for each version of a project. To make it easier for users and contributors to see precisely what notable changes have been made between each release (or version) of the project. Whether consumers or developers, the end users of software are human beings who care about what's in the software. When the software changes, people want to know why and how. see https://keepachangelog.com y/(n): y
Changelog enabled in config
Do you want to add a code linter and formatter to your gem? Supported Linters:
* RuboCop:       https://rubocop.org
* Standard:      https://github.com/standardrb/standard

Future `bundle gem` calls will use your choice. This setting can be changed anytime with `bundle config gem.linter`.
Enter a linter. rubocop/standard/(none): n
Initializing git repo in /Users/nishihara/Projects/create_gem/sneaker_selector
      create  sneaker_selector/Gemfile
      create  sneaker_selector/lib/sneaker_selector.rb
      create  sneaker_selector/lib/sneaker_selector/version.rb
      create  sneaker_selector/sig/sneaker_selector.rbs
      create  sneaker_selector/sneaker_selector.gemspec
      create  sneaker_selector/Rakefile
      create  sneaker_selector/README.md
      create  sneaker_selector/bin/console
      create  sneaker_selector/bin/setup
      create  sneaker_selector/.gitignore
      create  sneaker_selector/.rspec
      create  sneaker_selector/spec/spec_helper.rb
      create  sneaker_selector/spec/sneaker_selector_spec.rb
      create  sneaker_selector/LICENSE.txt
      create  sneaker_selector/CODE_OF_CONDUCT.md
      create  sneaker_selector/CHANGELOG.md
Gem 'sneaker_selector' was successfully created. For more information on making a RubyGem visit https://bundler.io/guides/creating_gem.html
```
無事ディレクトリが作成されました。

階層を表示してくれるtreeコマンドが入ってなかったのでインストールします。
```
brew install tree
```

階層はこんな感じです。
```
$ tree
.
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Gemfile
├── Gemfile.lock
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin
│   ├── console
│   └── setup
├── lib
│   ├── sneaker_selector
│   │   └── version.rb
│   └── sneaker_selector.rb
├── pkg
│   └── sneaker_selector-0.1.0.gem
├── sig
│   └── sneaker_selector.rbs
├── sneaker_selector-0.1.0.gem
├── sneaker_selector.gemspec
└── spec
    ├── sneaker_selector_spec.rb
    └── spec_helper.rb

7 directories, 17 files
```

CHANGELOG.mdとは？ 
プロジェクトの各バージョンにおける変更点を時系列順にリストアップしたファイルです。
これにより、ユーザーやコントリビューターが、各リリース間の主要な変更点を正確に把握することが容易になります。



MITライセンスとは？

>このソフトウェアを誰でも無償で無制限に扱って良い。ただし、著作権表示および本許諾表示をソフトウェアのすべての複製または重要な部分に記載しなければならない。
>作者または著作権者は、ソフトウェアに関してなんら責任を負わない。
>>[by wikipedia](https://ja.wikipedia.org/wiki/MIT_License)


[CODE_OF_CONDUCT](https://www.contributor-covenant.org/ja/version/1/4/code-of-conduct/)
日本語訳で行動規範となります。
プロジェクトやコミュニティのルールみたいなものです。



## gemspecの修正

```sneakers_selector.gemspec
# 修正内容を記述します

# 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.}

# ホームページURLまたはGithubのリポジトリURLを記述します。
spec.homepage      = "TODO: Put your gem's website or public repo URL here."

# RubyGems.orgへのプッシュが許可されているホストを記述します(今回はコメントアウトします。)
spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"

# Githubの公開リポジトリURLを記述します。
spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."

# CHANGELOG.mdのURLを記述します。
spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
```




修正後のファイルはこのような感じです。
```sneakers_selector.gemspec

# frozen_string_literal: true

require_relative "lib/sneaker_selector/version"

Gem::Specification.new do |spec|
  spec.name = "sneaker_selector"
  spec.version = SneakerSelector::VERSION
  spec.authors = ["wahei628"]
  spec.email = ["wahei628@gmail.com"]

  spec.summary = "This gem will pick a random sneaker for you!"
  spec.description = "This gem will randomly select a sneaker for you. Find a new discovery or your favorite pair!"
  spec.homepage = "https://github.com/wahei628/sneaker_selector"
  spec.license = "MIT"
  spec.required_ruby_version = ">= 2.6.0"

  # spec.metadata["allowed_push_host"] = "http://mygemserver.com"

  spec.metadata["homepage_uri"] = spec.homepage
  spec.metadata["source_code_uri"] = "https://github.com/wahei628/sneaker_selector"
  spec.metadata["changelog_uri"] = "https://github.com/wahei628/sneaker_selector/CHANGELOG.md"

  # 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.
  spec.files = Dir.chdir(__dir__) do
    `git ls-files -z`.split("\x0").reject do |f|
      (File.expand_path(f) == __FILE__) ||
        f.start_with?(*%w[bin/ test/ spec/ features/ .git appveyor Gemfile])
    end
  end
  spec.bindir = "exe"
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
  spec.require_paths = ["lib"]

  # Uncomment to register a new dependency of your gem
  # spec.add_dependency "example-gem", "~> 1.0"

  # For more information and examples about making a new gem, check out our
  # guide at: https://bundler.io/guides/creating_gem.html
end

```


問題なければ
```
$ bundle install
```
を実行します。


## コードを書きます！

今回はランダムでスニーカの種類(商品名)を表示してくれるようにします。
またセットで商品ページのURLも持ってきてくれるようにします。

ファイルの作成
```
$ touch lib/sneaker_selector/select.rb
```

```lib/sneaker_selector/select.rb
module SneakerSelector
  SNEAKERS = {
    "Nike Air Force 1" => "https://www.nike.com/jp/t/%E3%83%8A%E3%82%A4%E3%82%AD-%E3%82%A8%E3%82%A2-%E3%83%95%E3%82%A9%E3%83%BC%E3%82%B9-1-07-%E3%83%A1%E3%83%B3%E3%82%BA%E3%82%B7%E3%83%A5%E3%83%BC%E3%82%BA-55GJLx/CW2288-111",
    "Nike Air Max 90" => "https://www.nike.com/jp/t/%E3%83%8A%E3%82%A4%E3%82%AD-%E3%82%A8%E3%82%A2-%E3%83%9E%E3%83%83%E3%82%AF%E3%82%B9-90-%E3%83%A1%E3%83%B3%E3%82%BA%E3%82%B7%E3%83%A5%E3%83%BC%E3%82%BA-1q2wtc/CN8490-002",
    "Nike Air Jordan 1 MID" => "https://www.nike.com/jp/t/%E3%82%A8%E3%82%A2-%E3%82%B8%E3%83%A7%E3%83%BC%E3%83%80%E3%83%B3-1-mid-%E3%83%A1%E3%83%B3%E3%82%BA%E3%82%B7%E3%83%A5%E3%83%BC%E3%82%BA-hWx1dL/DQ8426-106",
    "Adidas SUPERSTAR" => "https://shop.adidas.jp/products/EG4958/",
    "Adidas STAN SMITH" => "https://shop.adidas.jp/products/FX5500/",
    "Adidas SAMBAE" => "https://shop.adidas.jp/products/ID0440/",
    "New Balance 574" => "https://shop.newbalance.jp/shop/g/gCM996GR2",
    "New Balance 996" => "https://shop.newbalance.jp/shop/g/gU574SOR"
  }

  def self.random
    SNEAKERS.to_a.sample
  end

end
```
スニーカー名と商品URLを記述しました。

またランダムに返すrandomメソッドを記述します。

次にsneaker_selector/select.rbを読めるようにrequireします。
```lib/sneaker_selector.rb
require_relative "sneaker_selector/version"
require_relative "sneaker_selector/select"

module SneakerSelector
  class Error < StandardError; end
  # Your code goes here...
end

```
呼び出し元ファイルが存在するディレクトリからの相対パスを記載します。
これでOKですね。

## テストの作成
RSpecを使用します。

```
mkdir spec/sneakers_selector
touch spec/sneakers_selector/select_spec.rb
```

```spec/sneakers_selector/select_spec.rb
require 'sneaker_selector/select'

RSpec.describe SneakerSelector do
  describe ".random" do
    it "スニーカー名とURLが返る" do
      sneaker = SneakerSelectors.random
      expect(sneaker).to be_a(Array)
      expect(sneaker.size).to eq(2)
      expect(sneaker[0]).to be_a(String)
      expect(sneaker[1]).to start_with("http")
    end
  end
end
```

```
$ bundle exec rspec spec

FindSneakers
  .random
    スニーカー名とURLが返る

Finished in 0.0041 seconds (files took 0.15945 seconds to load)
1 example, 0 failures
```
通りましたね！

## 作成したgemの確認
rake buildコマンドでpkgディレクトリにgemをbuildします
buildが完了したらgem installをします。
```
$　　rake build
sneaker_selector 0.1.0 built to pkg/sneaker_selector-0.1.0.gem.
$ gem install pkg/sneaker_selector-0.1.0.gem
Successfully installed sneaker_selector-0.1.0
1 gem installed
```

いい感じですね！


irbを起動して確認
```
$ irb
irb(main):001:0> require 'sneaker_selector'
=> true
irb(main):002:0> SneakerSelector.random
=> ["New Balance 996", "https://shop.newbalance.jp/shop/g/gU574SOR"]
```
うまく取得できましたね！

最後に[RubyGems](https://rubygems.org/)に会員登録します。

終わったら下記のコマンドを打ちます。
ユーザー名とパスワードを聞かれました。
```
$ curl -u <ユーザの名前> https://rubygems.org/api/v1/api_key.yaml
```
を入力するとrubygems_api_keyが返ってきます。

```
$ gem push sneaker_selector-0.1.0.gem
```
RubyGemsで登録したユーザー名とパスワードを入力。(2回目)

下記のコマンドを打つことでgemが登録されているかが確認できます。

```
$ gem list -r sneaker_selector

*** REMOTE GEMS ***

sneaker_selector (0.1.0)
```
しっかりと登録されていますね！

[sneaker_selector](https://rubygems.org/gems/sneaker_selector)
[GitHub](https://github.com/wahei628/sneaker_selector?tab=readme-ov-file)


## 　終わりに
プログラミング学習してからなんとなく使っていたgem、作れてみて楽しかったです。
今回作ったものは必要最小限とも言えない感じですが愛着があり、色々とアップデートしていきたいです。
まずはREADMEをしっかりと書きます🙇
最後まで読んでいただいてありがとうございました！

## 引用

https://qiita.com/harashoo/items/1284fd5362c1a4f6c8a8

https://techblg.app/articles/howt-make-gem-and-publish/