LoginSignup
14
8

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-12-08

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にされる時に参考になれば幸いです。

14
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
8