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

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

西脇.rb & 東灘.rb 合同もくもく会 5th に参加してきました。 #nshgrb - 勉強課題: SpreeのExtensionをつくる。

f:id:aq2bq:20170802180612p:plain

前回から少し間があいて開催された4回目の参加です。

今回も強く思ったのは、自分ではひとつのテーマに取り組むのでいっぱいなのですが、もくもく会ではメンバー数分の成果も吸収できることがあるのでプログラミング、Ruby、Rails初学者としてはとても効率的ですし、継続的にモチベーションが注入されるので大満足の勉強会でした。 あと、もくもく後のビールは最高です。

概要

Wiki

5th 2013.07.20

自分のテーマについて

RailsのECアプリケーションであるSpreeを、エクステンションをつくることから始めて理解を深める。

つくるもの

Spreeでは販売する商品の販売開始日という値を設定できるか、その逆の終了日(期限)は設定できない

  • 賞味期限(消費期限)のある商品を扱うことを想定して、販売期限を管理画面で設定できるようにする
  • 販売期限を過ぎた商品は販売できないようにする

つくっていく。

1.エクステンションのbarebonesを生成

$ spree extension expiration_product
$ cd spree_expiration_product

Gemfileを更新してbundle install

source 'https://rubygems.org'

# Provides basic authentication functionality for testing parts of your engine
gem 'spree_auth_devise', :git => 'https://github.com/spree/spree_auth_devise.git', :branch => '2-0-stable'

group :test, :development do
  gem 'pry-rails'
end

gemspec

$ bundle install

2.販売期限日カラム(expired_at)を追加する

$ bundle exec rails g migration add_expired_at_to_spree_variants    expired_at:date

3.とりあえずSpreeアプリケーション側に追加しておく

$ cd spree_app

spree_app/Gemfileを編集

gem 'spree_expiration_product', :path => '../extensions/spree_expiration_product'

インストール

$ bundle install
$ bundle exec rails g spree_expiration_product:install

4. 期限切れの商品が表示されないようにする

コンソール商品を期限切れにしてみる

$ bundle exec rails console
> product = Spree::Product.first
> variant = product.master
> variant.expired_at = '2013/07/01'
> variant.save!

期限切れの商品を除外するスコープを追加する

$ cd ../extension/spree_expiration_product
商品一覧ページで表示させるProductのスコープがspree_coreのapp/models/products/scope.rbが定義されているのでこれをオーバライドすることにする
$ mkdir -p app/models/spree/products
$ touch app/models/spree/products/scopes_decorator.rb
販売期限内の商品を返すメソッド(Spree::Product.buyable)を追加し、既存メソッドの(Spree::Product.active)をオーバライドする
module Spree
  Product.class_eval do
    delegate_belongs_to :master, :expired_at
    def self.buyable
      joins(:variants_including_master).where('spree_variants.expired_at >= ?', Date.today)
    end

    # Override existing method
    def self.active(currency = nil)
      not_deleted.available(nil, currency).buyable
    end

  end
end

5.動いているかチェック

UIはまだ実装できていないのでコンソールで商品の販売期限を設定して、期限が過ぎた商品が表示されないようになっているかどうかチェック。

$ bundle exec rails console
Loading development environment (Rails 3.2.13)
[1] pry(main)> product = Spree::Product.first
  Spree::Product Load (0.4ms)  SELECT `spree_products`.* FROM `spree_products` LIMIT 1
  Spree::Product::Translation Load (0.2ms)  SELECT `spree_product_translations`.* FROM `spree_product_translations` WHERE `spree_product_translations`.`spree_product_id` = 1
=> #<Spree::Product id: 1, name: "るびー丼", description: nil, available_on: "2013-06-02 00:00:00", deleted_at: nil, permalink: "るびー丼", meta_description: nil, meta_keywords: nil, tax_category_id: 1, shipping_category_id: 1, created_at: "2013-07-13 07:58:07", updated_at: "2013-07-20 08:53:06">
[2] pry(main)> variant = product.master
  Spree::Variant Load (0.4ms)  SELECT `spree_variants`.* FROM `spree_variants` WHERE `spree_variants`.`product_id` = 1 AND `spree_variants`.`is_master` = 1 LIMIT 1
=> #<Spree::Variant id: 1, sku: "SKU-001", weight: nil, height: nil, width: nil, depth: nil, deleted_at: nil, is_master: true, product_id: 1, cost_price: #<BigDecimal:7fe54318bca0,'0.1E4',9(18)>, cost_currency: "JPY", position: 1, sale_price: nil, expired_at: "2013-06-01">
[3] pry(main)> variant.expired_at = '2013/06/01'
=> "2013/06/01"
[4] pry(main)> variant.save!
   (0.2ms)  BEGIN
  Spree::Price Load (0.2ms)  SELECT `spree_prices`.* FROM `spree_prices` WHERE `spree_prices`.`variant_id` = 1 AND `spree_prices`.`currency` = 'JPY' LIMIT 1
  Spree::Product Load (0.4ms)  SELECT `spree_products`.* FROM `spree_products` WHERE `spree_products`.`id` = 1 LIMIT 1
  SQL (0.3ms)  UPDATE `spree_products` SET `updated_at` = '2013-07-22 01:02:06' WHERE `spree_products`.`id` = 1
   (0.7ms)  COMMIT
=> true

意図通り、表示されなくなったことをhttp://localhost:3000で確認

今回はここでタイムオーバー。 ここまでの成果物はGithubにあげてあります。 aq2bq/spree_expiration_product

まとめ

実際にエクステンションを書いてみるとSpreeへの理解が少し深まりました。

  • 既存のメソッドを上書きするのは今後のSpreeのアップデートに対応できなくなる可能性があるので、フックするなど他の方法を検討する必要あり
  • #buyableのというメソッド名の意味が今回の意図に対して大きすぎるので、#not_expiredに変更する
  • viewとcontrollerを実装する
  • テストを書く

という方向で引き続きこのエクステンション開発を完結させていこうと思います。