Ruby
Gem
React

JavaScriptライブラリをGemにしてRubyに組み込む

More than 1 year has passed since last update.

JavaScriptライブラリのためのRubyGemを作ってみたので、作成手順を順を追いながらまとめます。JavaScriptライブラリのGem作成方法は日本語記事があまりなかったので、参考になれば幸いです。

JavaScriptライブラリのGemは、有名所ですとjquery-rails等です。

今回は、Gemを使うときに下記のようにAsset Pipelineのマニフェストファイルで宣言して読み込むタイプのものについて見ていきます。


app/assets/javascripts/application.js

//= require jquery



今回作ったGem

react_rails_modalというreact-rails上でモーダルを動かすためのGemを今回作りました。react-railsはRailsにReactを導入するためのものです。

使い方は簡単です。

まずreact-railsをインストールした上で、本Gemをインストールします。


Gemfile

gem 'react_rails_modal'


$ bundle install

次にRailsコマンドを実行します。

$ rails g react_rails_modal:install

これにより、下記がapplication.jsに追記され、マニフェストファイルにライブラリの読み込みが宣言されます。


app/assets/javascripts/application.js

//= require react_rails_modal


これでreact_rails_modalが読み込まれるようになったので、react-rails上でモーダルが使えるようになります。

モーダルの使い方に関してはreact-railsでモーダルを実装するにまとめしたのでそちらをご覧ください。

ここから、Gemの作成手順を追いながら、JavaScriptライブラリのためのGemの作成方法を見ていきます。


準備

まずGemとbundlerのアップデートをします。

$ gem update --system

# bundlerをインストールしていない場合はインストールします
$ gem install bundler

$ gem update bundler


ひな形の作成

Gemのひな形をテストありで作ります。

$ bundle gem react_rails_modal -t


gemspec

Gemの基本的な情報をgemspecにまとめます。


react_rails_modal.gemspec

# coding: utf-8

lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'react_rails_modal/version'

Gem::Specification.new do |spec|
spec.name = 'react_rails_modal'
spec.version = ReactRailsModal::VERSION
spec.summary = 'Accessible modal dialog component on react-rails'
spec.description = 'React_rails_modal creates modal dialog easily on react-rails'
spec.homepage = 'https://github.com/kiyodori/react_rails_modal'
spec.license = 'MIT'

spec.authors = ['kiyodori']

spec.add_development_dependency 'bundler', '>= 1.13'
spec.add_development_dependency 'rspec', '>= 3.0'
spec.add_development_dependency 'rails', '>= 3.2'
spec.add_development_dependency 'react-rails', '>= 1.7.0'

spec.files = Dir[
'lib/**/*',
'README.md',
'LICENSE.txt'
]

spec.require_paths = ['lib']
end


今回はreact-railsとセットで動くGemなので、 railsreact-railsadd_development_dependencyに追加します。

add_development_dependencyに開発に必要なGemを記載することで、Gemfileはこの中身を参照します。


Gemの本体の実装

それではGem本体を下記手順で実装していきます。


  1. Asset Pipelineのパスにreact_rails_modalファイルが読み込まれるよう設定する

  2. Asset Pipelineにreact_rails_modalファイルを追加するRailsコマンドを作成する。具体的には、マニフェストファイルに//= require react_rails_modalを追記するRailsコマンドを作成する

  3. 設定したパスにreact_rails_modalファイルを設置する

Gem本体の実装はlib/ディレクトリに行います。

── lib

├── react_rails_modal
│ └── railtie.rb
│ └── version.rb
└── react_rails_modal.rb

呼び出し側でGemが読み込まれるとlib/react_rails_modal.rbが呼ばれるようになっているので、lib/react_rails_modal.rblib/react_rails_modal/ディレクトリ配下のファイルをrequireします。


lib/react_rails_modal.rb

require 'react_rails_modal/version'

require 'react_rails_modal/railtie'


1. Asset Pipelineのパスにreact_rails_modalファイルが読み込まれるよう設定する

パスはRailsの起動時に設定されます。Rails::Railtieを使うことで、Railsの起動時にフックしてパスの追記を行います。


lib/react_rails_modal/railtie.rb

require 'rails'

module ReactRailsModal
class Railtie < ::Rails::Railtie
GEM_ROOT = Pathname.new('../../../').expand_path(__FILE__)

config.react_rails_modal = ActiveSupport::OrderedOptions.new

initializer 'react_rails_modal.set_variant' do |app|
app.config.assets.paths << GEM_ROOT.join('lib/assets/javascripts/').to_s if app.config.respond_to?(:assets)
end
end
end


Rails::Railtieを継承したクラスで、initializerマクロを用いて、Railsの起動プロセス時にconfig.assets.pathsにパスを追記します。

これにより、lib/assets/javascripts/がAsset Pipelineのパスに追加されます。

実際に本Gemを読み込んだアプリケーションでパスを確認してみると、次のようになっています。

$ rails console

[1] pry(main)> Rails.application.config.assets.paths
=> ["{other_path}", ..., "{project_path}/vendor/bundle/ruby/{ruby_version}/gems/react_rails_modal-{gem_version}/lib/assets/javascripts/"]

最終的にはlib/assets/javascripts/react_rails_modal.jsファイルを置いておけば、マニフェストファイルでreact_rails_modalファイルが宣言された時に読み込まれます。


2. Asset Pipelineにreact_rails_modalファイルを追加するrailsコマンドを作成する

Asset Pipelineにreact_rails_modalファイルを追加するために、下記Railsコマンドを準備しました。

$ rails g react_rails_modal:install

上記コマンドにより、下記がapplication.jsに追記されます。


app/assets/javascripts/application.js

//= require react_rails_modal


このコマンドは、Railsジェネレータを利用することで作成できます。

lib/generators/react_rails_modal/install_generator.rbというファイルを作成します。


lib/generators/react_rails_modal/install_generator.rb

module ReactRailsModal

module Generators
class InstallGenerator < ::Rails::Generators::Base
desc 'Install react_rails_modal'

def inject_react_rails_modal
require_react_rails_modal = "//= require react_rails_modal\n"

if manifest.exist?
manifest_contents = File.read(manifest)

if match = manifest_contents.match(/\/\/=\s+require_tree[^\n]*/)
inject_into_file manifest, require_react_rails_modal, { before: match[0] }
else
append_file manifest, require_react_rails_modal
end
else
create_file manifest, require_react_rails_modal
end
end

private

def manifest
Pathname.new(destination_root).join('app/assets/javascripts', 'application.js')
end
end
end
end


Rails::Generators::Baseを継承したクラスを作成します。ジェネレータが起動されると、ジェネレータ内で定義されているパブリックメソッドが定義順に実行されます。

今回はinject_react_rails_modalメソッドの中で、app/assets/javascripts/application.js"//= require react_rails_modal\n"を追記しています。


3. 設定したパスにreact_rails_modalファイルを設置する

ここまででAsset Pipelineに、パスを設定し、react_rails_modalファイルを追加できました。あとは設定したパスにreact_rails_modalファイルを置くだけです。

今回はreact_buildsディレクトリに、Reactでモーダルを実現するためのjsxファイルを置き、それをwebpackでjsファイルにコンパイルしています。

コンパイルされたjsファイルを、上記で設定したパスであるlib/assets/javascripts/以下にreact_rails_modal.jsとして設置しています。

ここはJavaScript関連の処理になるので、具体的な内容は省略します。

このコンパイル作業をRakeタスク化し、下記コマンドで簡単に呼び出せるようにします。

$ rake react_rails_modal:build

Rakeタスクの定義はRakefileに行います。


Rakefile

begin

require 'bundler/setup'
rescue LoadError
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
end

Bundler::GemHelper.install_tasks

namespace :react_rails_modal do
desc 'Build the JS bundles with Webpack'
task :build do
Dir.chdir("react_builds") do
`yarn run build`
end
end
end


Rakeタスクではyarn run buildコマンドを実行していて、その中でwebpackを用いて、Reactのjsxファイルをjsファイルにコンパイルしています。

このように、lib/assets/javascripts/以下にreact_rails_modal.jsを設置することで、Asset Pipelineから読み込むことができます。

以上で完成です。


まとめ

JavaScriptライブラリをGemにする手順を見てきました。

まとめると、下記手順になります。


  1. Gemのひな形をbundle gemコマンドで作成する

  2. gemspecにGemの基本的な情報をまとめる

  3. Gemの本体であるlib/react_rails_modal.rbで必要なファイルを読み込む

  4. Asset Pipelineのパスにreact_rails_modalファイルが読み込まれるよう設定する


    • パスの追加は、Rails::Railtieを使い、Railsの起動時にフックして設定する



  5. Asset Pipelineにreact_rails_modalファイルを追加するrailsコマンドを作成する


    • マニフェストファイルに//= require react_rails_modalを追記するRailsコマンドを作成する

    • RailsコマンドはRails::Generators::Baseを用いることで設定できる



  6. 設定したパスにreact_rails_modalファイルを設置する


    • Reactのjsxファイルをjsファイルにコンパイルしたものをパスに設置する



JavaScriptライブラリをGemにされる時に参考になれば幸いです。