Help us understand the problem. What is going on with this article?

プライベートgemの作り方

More than 3 years have passed since last update.

プロダクトが大きくなるにつれて、既存のコードの一部を使いまわしたいというケースはよくあるでしょう。
OSSとして公開してしまうのが一番なのですが、様々な事情から公開できないこともあると思います。
そんな場合に、同じコードを複数のリポジトリにコピーするのは当然良くありません。
git submodule等を使う手はありますが、rubyの場合はrubygemsのエコシステムを使ってプライベートなgemを作成し、組み込むことができます。

gemの雛形を作る

gemを作成したことがない人も多いと思いますが、驚くほど簡単です。
とりあえずbundlerがインストールされているとして(普通はしていると思いますが)

bundle gem my_rubygem
Creating gem 'my_rubygem'...
      create  my_rubygem/Gemfile
      create  my_rubygem/.gitignore
      create  my_rubygem/lib/my_rubygem.rb
      create  my_rubygem/lib/my_rubygem/version.rb
      create  my_rubygem/my_rubygem.gemspec
      create  my_rubygem/Rakefile
      create  my_rubygem/README.md
      create  my_rubygem/bin/console
      create  my_rubygem/bin/setup
      create  my_rubygem/.travis.yml
      create  my_rubygem/.rspec
      create  my_rubygem/spec/spec_helper.rb
      create  my_rubygem/spec/my_rubygem_spec.rb
Initializing git repo in /path/to/my_rubygem

これで雛形一式ができます。
このままgithubのプライベートリポジトリにしてしまいましょう。
(やり方は省略します)

依存するgemを書く

gemの中で他のgemを利用することもよくあります。
普段の開発ではGemfileを編集していると思いますが、gemを作る場合は.gemspecを編集します。

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

Gem::Specification.new do |spec|
  spec.name          = "my_rubygem"
  spec.version       = MyRubygem::VERSION
  spec.authors       = ["nysalor"]
  spec.email         = ["nysalor@larus.org"]

  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. 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'"
  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 do |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.13"
  spec.add_development_dependency "rake", "~> 10.0"
  spec.add_development_dependency "rspec", "~> 3.0"
end

こんな感じの雛形ができているので、依存するgemを下の方に追記します。

my_rubygem.gemspec
  gem.add_runtime_dependency "nokogiri"

これで依存関係に追加されます。
開発時に使うだけのものは、add_development_dependencyに追加します。

my_rubygem.gemspec
  spec.add_development_dependency "webmock", "~> 2.3"

また、TODOと書かれているところを直さないとgem installできません。
summaryとdescriptionには簡単に説明を書いておきます。
allowed_push_hostには部内のgemserverのアドレスか、または存在しないアドレスを書いておくと、誤ってrubygems.orgに公開してしまうことを防げます。

開発

後は黙々と開発しましょう。
コードはlib/以下に書いていきます。
lib/my_rubygem/version.rbにバージョンを書いておくと、このgemをbundle installした場合にバージョン管理することができます。
gemの作り方については検索するなり「パーフェクトRuby」のような書籍なりを読んだ方が良いですが、lib/my_rubygem/base.rbに基底クラスを書き、それを継承したクラスを書いて行くというのがよくあるパターンかと思います。
ここで作ったクラスについては、bundle installしたプロジェクト内で通常のクラスとして利用できます。

初期設定

TIPSとして、プロジェクトごとの設定項目を作る方法を書いておきます。
よくgemの初期設定で出てくる記法に以下のようなものがあります。

MyRubygem.configure do |config|
  config.access_token = 'hogehoge'
end

これを実現するには、以下のように書きます。

lib/my_rubygem.rb
require "my_rubygem/version"
require "my_rubygem/configuration"
require "my_rubygem/base"

module MyRubygem
  class << self
    def configure
      yield configuration
    end

    def configuration
      @configuration ||= MyRubygem::Configuration.new
    end
  end
end
lib/my_rubygem/configuration.rb
class MyRubygem::Configuration
  attr_accessor :access_token
end

試す

開発中に試すなら、とりあえずローカルからインストールしてみるのが簡単ですね。
組み込む側のプロジェクトのGemfileに以下のように書きます。

Gemfile
gem 'my_rubygem', :path => '/path/to/my_rubygem

これでbundle installすればインストールされます。

プロダクトコードへの組み込み

一通り完成したら、githubのプライベートリポジトリにpushします。
そして組み込む側のプロジェクトのGemfileに以下のように書きます。

Gemfile
gem 'my_rubygem', git: 'https://github.com/nysalor/my_rubygem.git'

この状態でgem installすると、githubのIDとパスワードを問い合わせてきます。
それでも良いのですが、継続的デリバリーしたいですよね。
そのためにはまずgithubのtokenをpersonal access tokenから取得します。
デプロイスクリプトにtokenを直接書いてしまっても良いですが、それを避けるのであれば環境変数BUNDLE_GITHUB__COMを使います。

BUNDLE_GITHUB__COM=[github token]:x-oauth-basic bundle install

または

export BUNDLE_GITHUB__COM=[github token]:x-oauth-basic
bundle install

アンダースコアが2つ続くのに注意。

privateリポジトリに置いたgemをGemfileに含めるも参照。

なお、Gemfileに git: 'git://github.com...'github: 'nysalor/private-repo' と書いてもtokenは利用されません。 git: 'https://github.com...' のようにhttpsと明記しないと、通常のパスワード認証になります。

おわりに

gemを作るのは意外と簡単で、再利用もしやすいのでおすすめです。
部内で普及すれば、PRを出して貰って育てていくこともできると思います。

nysalor
Rubyで仕事しています。宗派はEmacs教矢印キー派です。
http://blog.larus.jp/
r-n-i
二つのアプリ・サービス、CODE、Mycomment を開発・運営しています。ユーザーに対してはポイントが貯まる家計簿・ポイントが貯まる調査アンケートを提供し、顧客企業に対してはインターネットを活用したマーケティングリサーチ、販売促進プロモーションを提供しています。
https://r-n-i.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away