ユカシカド エンジニアブログ

体の栄養状態を把握する検査サービス VitaNoteを開発するエンジニアのブログ

(翻訳)Spreeドキュメント - エクステンション(2/2)

(翻訳)Spreeドキュメント - エクステンション(½)の続き。 にもかかわらず長かった。分割したほうがよかったかも。

※ 誤訳意訳多々注意

自分の残念な英語力ではぴんとこない箇所は英語のままpending。怪しい訳多し。

実践もしくは英語力の向上を伴って正しく理解することがあれば、加筆・修正する予定です。

この記事は2013/07/17現在のものです。

翻訳元: EXTENSIONS - SPREE DEVELOPER

エクステンションの作り方(original)

はじめに(original)

シンプルなエクステンションをつくってみましょう。ある商品がセール中であることを示したいとします。 商品にセール価格を設定して、”セール商品特集ページ”で表示されるようにしていきます。この例はSpreeの機構で適切なエクステンションをつくるのかをしる非常に良い例です。

エクステンションを生成するところから始めてみましょう。Spreeアプリケーションの外に用意したディレクトリで以下のコマンドを実行してください:

$ spree extension simple_sales

これでいくつかのファイルやディレクトリを含む、spree_simple_salesが作成されます。 生成されたディレクトリへ移動してください:

$ cd spree_simple_sales

Variantsへセール価格を追加(original)

まず最初にしなければならないのは、variantsへセール価格カラムを追加するマイグレーションを作成することです、

以下のコマンドを実行すると作成できます:

$ rails g migration add_sale_price_to_spree_variants sale_price:decimal

TODO: 上記のコマンドはエクステンションのディレクトリ内で実行してください。

価格を扱うのでマイグレーションファイルを編集して、:precision(有効桁数) と :scale(その内の小数部の桁数)を正しく調整する必要がありいます。

db/migrate/XXXXXXXXXXX_add_sale_price_to_spree_variants.rb

を以下のように編集してください:

class AddSalePriceToSpreeVariants < ActiveRecord::Migration
  def change
    add_column :spree_variants, :sale_price, :decimal, :precision => 8, :scale => 2
  end
end

Spreeアプリケーションにエクステンションを追加(original)

エクステンションの開発を続ける前に、作成したSpreeアプリケーションへ追加してみましょう。これで開発中に、実際のSpreeストアでエクステンションがどのように動作するのかわかります。

Spreeアプリケーションの'mystore'ディレクトリのGemfileに以下のコードを追加してください:

gem 'spree_simple_sales', :path => '../spree_simple_sales'

:pathについては、作成したエクステンションの場所にあわせて適宜調整してください。 ‘mystore'のロケーションへの相対パスになるようにしてください。

さきほど追加したgemをbundle:

$ bundle install

最後にspree_simple_salesのジェネレーターでマイグレーションを実行してください(プロンプトには'yes'と答えてください):

$ rails g spree_simple_sales:install

HomeControllerへのアクションの追加(original)

’Spree::HomeController’を拡張して、セール中の商品を選択するアクションを追加していきます。

spree_simple_salesのルートディレクトリにいることを確認して、コントローラーデコレーターの為のディレクトリを作成してください:

$ mkdir -p app/controllers/spree

このディレクトリに'home_controller_decorator.rb'を作成して以下のようにコードを書いてください:

module Spree
  HomeController.class_eval do
    def sale
      @products = Product.joins(:variants_including_master).where('spree_variants.sale_price is not null').uniq
    end
  end
end

これは'sale_price'を持っている商品だけを選択するでしょう。

'config/routes.rb'にこのアクションに対応したルートを追加する必要もあります。以下のように追記してください:

Spree::Core::Engine.routes.draw do
  get "/sale" => "home#sale"
end

セール商品特集ページのビューを用意する(original)

セール価格を設定

現在、'variants'には"セール価格"という属性があるのでサンプルデータを更新して少なくとも1商品をセール中にしてみましょう。今は管理画面のインターフェイスに'セール価格'を用意していないので、とりあえずrails consoleを使用しておこなう必要があります。次のチュートリアルではDefaceによるオーバーライドを使用して機能的に追加する方法を紹介します。

まず、rails consoleを起動してください:

$ rails console

ここから選んだ商品にセール価格を設定してみます。注意: 手元の環境では以下のコードにある同じ商品ではないこともありますが、あまり気にしないでください。単に’セール商品特集ページ’に指定の商品を表示することさえできればいいのです。

> product = Spree::Product.first
=> #<Spree::Product id: 107377505, name: "Spree Bag", description: "Lorem ipsum dolor sit amet, consectetuer adipiscing...", available_on: "2013-02-13 18:30:16", deleted_at: nil, permalink: "spree-bag", meta_description: nil, meta_keywords: nil, tax_category_id: 25484906, shipping_category_id: nil, count_on_hand: 10, created_at: "2013-02-13 18:30:16", updated_at: "2013-02-13 18:30:16", on_demand: false>

> variant = product.master
=> #<Spree::Variant id: 833839126, sku: "SPR-00012", weight: nil, height: nil, width: nil, depth: nil, deleted_at: nil, is_master: true, product_id: 107377505, count_on_hand: 10, cost_price: #<BigDecimal:7f8dda5eebf0,'0.21E2',9(36)>, position: nil, lock_version: 0, on_demand: false, cost_currency: nil, sale_price: nil>

> variant.sale_price = 8.00
=> 8.0

> variant.save
=> true

ビューの作成(original)

現在、データベースにはセール価格の商品が少なくとも一つ存在しています。この商品が表示されるようにビューを作っていきましょう。

まず、必要となるビューディレクトリを作成します:

$ mkdir -p app/views/spree/home

次に、'app/views/spree/home/sale.html.erb'を作成して以下のようにコードを書きます:

<div data-hook="homepage_products">
  <%= render 'spree/shared/products', :products => @products %>
</div>

'http://localhost:3000/sale'へアクセスすると、チュートリアルで'セール価格'を設定した商品がリスト表示されているはずです。しかし価格を見ると正しい価格が表示されていないことに気がつくでしょう。これは次のセクションで簡単に修正できます。

Variantsのデコレート(original)

'セール価格'を扱うエクステンションを修正してきましょう。

まず最初に、新しいdecoratorの為のディレクトリを作成します:

$ mkdir -p app/models/spree

次に、'app/models/spree/variant_decorator.rb'を作成して以下のように書きます:

module Spree
  Variant.class_eval do
    alias_method :orig_price_in, :price_in
    def price_in(currency)
      return orig_price_in(currency) unless sale_price.present?
      Spree::Price.new(:variant_id => self.id, :amount => self.sale_price, :currency => currency)
    end
  end
end

ここではオリジナルのメソッドの’price_in’へ'orig_price_in'という別名をつけてオーバーライドしています。商品のマスタに'セール価格'があれば、その価格を返します。それ以外は元の’price_in’を返します。

デコレーターのテスト(original)

書いたコードをテストするのにいい方法があります。Spreeのコアな機能を変更したVariant decoratorは特に注意して書かなければなりません。variant_decorator.rbの1組みのシンプルなユニットテストを書いていきましょう。

テストアプリケーションの作成

エクステンションは完全なRailsアプリケーションではないので、エクステンションに対してテストするものが必要です。'test_app'というRakeタスクを実行することで、テストを実行する為のセットがspecディレクトリに生成されます。

エクステンションのルートディレクトリで以下のコマンドを実行してください:

$ bundle exec rake test_app

このコマンドの実行が完了した後に'rspec'を実行すると以下のように出力されます:

No examples found.

Finished in 0.00005 seconds
0 examples, 0 failures

グレート!テストを追加する準備をしましょう。以下のようにspecディレクトリに以下のようなディレクトリ構造をつくってください:

$ mkdir -p spec/models/spree

では、variant_decorator_spec.rbを作成してテストを追加していきます:

require 'spec_helper'

describe Spree::Variant do
  describe "#price_in" do
    it "returns the sale price if it is present" do
      variant = create(:variant, :sale_price => 8.00)
      expected = Spree::Price.new(:variant_id => variant.id, :currency => "USD", :amount => variant.sale_price)

      result = variant.price_in("USD")

      result.variant_id.should == expected.variant_id
      result.amount.to_f.should == expected.amount.to_f
      result.currency.should == expected.currency
    end

    it "returns the normal price if it is not on sale" do
      variant = create(:variant, :price => 15.00)
      expected = Spree::Price.new(:variant_id => variant.id, :currency => "USD", :amount => variant.price)

      result = variant.price_in("USD")

      result.variant_id.should == expected.variant_id
      result.amount.to_f.should == expected.amount.to_f
      result.currency.should == expected.currency
    end
  end
end

エクステンションのバージョン管理(original)

異なるバージョンのSpreeでは作成したエクステンションの振る舞いも変わってしまいます。エクステンションが、異なるバージョンのSpreeで動作するようにブランチを作成して活発にメンテナンスして対応したほうがいいでしょう。

Spree自体のバージョン付けに従うようにエクステンションのバージョン管理をすることを推奨します。作成したエクステンションがSpree 2.0.xへ対応しているのであれば、'2-0-stable'というブランチをつくっておけば、使用する側のユーザにもわかりやすくなります。 もし、1.3.xにしか対応していないのであれば、同様に'1-3-stable'ブランチをつくっておきます。

Spreeとエクステンションの間に一貫した命名規則を持っていれば戸惑うことは少なくなるでしょう。

まとめ(original)

このチュートリアルでは、エクスションをインストールする方法と、自分自身で作る方法を学びました。 Spreeの開発コンセプトとSpree内部のいくつかの仕組みについてふれることができました。

次のパートではこのエクステンションをDefaceによるオーバライドを用いて改善する方法を紹介します。

(翻訳)Spreeドキュメント - Defaceでエクステンションを改善するへ続く