うちのいぬ Tech Blog

Tech Blog of Uchinoinu/My dog

Crafting Rails 4 Applications - capter 1-1

f:id:susanne:20150403234922j:plain

p1

第一章

Rendererを自作する

多くのWebフレームワークと同様に、RailsMVC(model-view-controller)アーキテクチャをコードの組織化に用いています。Controllerはモデルから情報を収集し、レンダリングの為にViewにそのデータを渡すことを担当します。対してModelはModel自身の表現を担当し、Viewは、JSON(JavaScript Object Notation)リクエストの様なリクエストに関わりません。次の様なindex actionはこれら2つのシナリオを表します。

class PostsController < ApplicationController
  def index
    if client_authenticated?
      render json: Post.all
    else
       rende template: 'shared/not_authenticated', status: 401
     end
   end
end

与えられたModelやTemplateをレンダリングする共通のインターフェースには、 render() メソッドが用いられます。 :template や :file のレンダリング方法を知ることに加えて、Railsが生の :text や :xml, :json, :js といったフォーマットもレンダリングできます。Railsのデフォルトの状態でもアプリケーションを充分に自作できますが、 :pdf, :csv などをレンダリングするメソッドを知る必要が出てくることもあります。

そこで、RailsではAPI(Application Programming Inteface)を提供し、独自のrendererを作ることができます。それでは、:pdf オプションを使えるようにAPIを探り、Ruby用の軽量で高速なPDF作成ライブラリ[Prawn]を(https://github.com/prawnpdf/prawn)を使って作成したPDFファイルをPDFファイルをreturn出来る様にしてみましょう。

p2

この本の多くの章では、 rails plugin generatorを使ってRailsの能力を拡張するプラグインを作成していきます。それでは始めましょう。

1.1 初めてのRailsプラグイン

既にRailsがインストールされているようでしたら、いつでもプラグインを作ることができます。それでは、 pdf_rendererプラグインを作ってみましょう。

$ rails plugin new pdf-renderer

アウトプットは以下の通り。

create
create README.rdoc
create Rakefile
create pdf_renderer.gemspec
create MIT-LICENSE
create .gitignore
create Gemfile
create lib/pdf_renderer.rb
create lib/tasts/pdf_renderer_tasts.rake
create lib/pdf_renderer/version.rb
create test/test_helper.rb
create test/pdf_renderer_test.rb
append Rakefile
vendor_app test/dummy
run bundle install

このコマンドは、基本的なプラグイン構成を作成します。そこには、pdf_renderer.gemspecファイルや Rakefile, libやtextフォルダも含まれます。次のステップは少し興味深いです。test/dummyディレクトリ内にRailsアプリケーションが生成され、Railsアプリケーションのコンテキストでテストを走らせることが出来るようになります。

bundle installコマンドが走り、Bundlerを使って、プロジェクトに必要なすべての依存ファイルをインストールし終わると、generatorが終了します。セットアップが完了したので、生成されたファイルを見て行きましょう。

pdf_renderer.gemspec

pdf_renderer.gemspecは基本的なgemの仕様を提供します。この仕様により、gemの作者、バージョン、依存関係、ソースファイルなどが宣言されます。これにより、プラグインRuby gemの中に簡単にバンドルすることができます。別のRailsアプリケーションでも簡単に共有できるわけです。

libディレクトリの中にもpdf_rendererという名前が使われているファイルがあることに気づかれたでしょうか?この規約に従うことにより、いつGemfileにgemを宣言しても、lib/pdf_renderer.rbが自動的に読み込まれる事になります。現時点では、このファイルはただPdfRendererモジュールを定義しているだけです。

最後に、gemspecプロジェクトのバージョンを定義してはいないことに気づかれたでしょうか?代わりに、lib/pdf_renrerer/versions.rbで定義されています。これはPdfRenderer:VERSIONとしてgemspecで参照されます。これがRuby gemsの共通のプラクティスになります。

Gemfile

Railsアプリでは、Gemfileはすべての依存関係をリスティングするために使われ、test, development, productionなどの環境は問いません。しかし、私達のプラグインは既にgemspecファイルを持ち依存関係をリスティングしています。この場合Gemfileの役割はシンプルで、gemspecで記述された依存関係を再利用します。Gemfileは、debuggerやpryの様な開発に便利なgemを更なる依存関係に含むこともあります。

プラグインの依存関係を管理する為にBundlerを使用しますが、Bundlerは、pdf_renderer.gemspeccとGemfileの両方でリストされたgemを使用する環境を規定します。また、testは特定のgemを使って実行されます。プラグインのルートでbundle installやbundle updateコマンドを走らせると、新しい依存性を追加したり、既存のものを更新したりできます。

Rakefile

Rakefileは基本的なタスクを提供しており、テストを走らせたり、ドキュメントを生成したり、gemを公開したりします。rake -Tをpdf_rendererルートで叩くとタスクの全リストが見れます。

$ rake -T
---
rake build               # Build pdf_renderer-xxx.gem into pkg directory
rake clobber_rdoc  # Remove RDoc HTML files
rake install              # Build and install pdf_renderer-xxx.gem into system gems
rake rdoc                # Build RDoc HTML files
rake release            # Create tag v0.0.1 and build an push pdf_renderer
rake rerdoc             # Rebuild RDoc HTML files
rake test                 # Run tests

Booting the Dummy Application

rails pluginはダミーアプリを作成し、テストディレクトリに設置されます。このアプリの起動プロセスは、railsコマンドで作成された普通のアプリのそれと似ています。config/boot.rbファイルは、『アプリケーションのロードパスを設定する』ということのみに責務を負います。

config/application.rbは依存関係をすべてロードし、アプリケーションを設定します。これらのファイルはconfig/environment.rbで初期化されます。

rails pluginコマンドで生成されたbootファイルはtest/dummy/config/boot.rbに設置されます。それはアプリケーションのものと似ていますが、違いもあります。ひとつは、pdf_rendererプラグインのルートにGemfileを置く必要がある点。これにより、プラグインのlibディレクトリにRubyのロードパスが追記され、dummyアプリの中でプラグインが使用可能になります。

pdf_renderer/1_prawn/test/dummy/config/boot.rb

# Set up gems listed in the Gemifile
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__)

require 'bundle/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
$LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__)

bootファイルはBundlerに依存関係やロードパスのセットアップの責務を移譲します。test/dummy/config/application.rbはconfig/application.rbの下部バージョンです。

pdf_renderere/1_prawn/test/dummy/config/application.rb

require File.expand_path('../boot', __FILE__)
require 'rails/all'

Bundler.require(*Rails.groups)
require "pdf_renderer"

module Dummy
  class Application < Rails::Application
     #....
  end
end

config/environment.rbは通常のRailsアプリで見たものと同様です。

pdf_rendererer/1_prawn/test/dummy/config/environment.rb

# Load the rails appliaciton.
require File.expand_path('../applicatoin', __FILE__)

# Initialize the rails applicaiton.
Dummy::Application.initialize!

テストを走らせる

通常、rails pluginではプラグイン用の整合性のなるテストが生成されます。それではテストを走らせてみましょう。

p5

$ rake test
---
Run options: --seed 20094

# Runnging test:

.

Finished tests in ................

1 test, 1 assertions, 0 failures, 0 errors, 0 skips

テストはtest/pdf_renderere_test.rbで定義され、プラグインがPdfRendererというモジュールを定義していることを宣言します。

pdf_renderer/1_prawn/test/pdf_renderer_test.rb

require 'test_helper'
class PdfRendererTest < ActiveSupport::TestCase
  test 'trust' do
    assert_kind_of Module, PdfRenderer
  end
end

最後に、テストファイルがtest/test_helper.rbを読み込んでいることに気づいたでしょうか?このファイルはアプリのロードやテスト環境の設定に責務を負います。スケルトンが作られ、テストが問題なく通ったことを確認して、rendererのカスタムへと進みます。