mcpField
S
spree-dependencies

spree-dependencies

github.com/spree開発10219 回インストール4 週間前に更新
リポジトリ

spree-dependencies について

Spreeのコアクラスを依存性注入で置き換える方法を解説する開発スキルです。Spreeアプリや拡張の開発者向けに、フックとクラス置換の選び方や、ワークフロー、サービス、APIシリアライザーの契約を説明します。

主な機能

  1. 01クラス置換が適切な場面の判断
  2. 02ワークフローとサービスの置換設定
  3. 03依存キーを使ったAPIシリアライザーの拡張
  4. 04旧依存キーへの対処
  5. 05Spreeのアクセサー経由で上書きを適用

活用シーン

  1. 01カスタムのカートまたは注文ワークフローの実装
  2. 02StoreまたはAdmin APIシリアライザーへのフィールド追加
  3. 03旧サービスの上書きをワークフローへ移行
  4. 04拡張コードでホストアプリの上書きを反映

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/spree/agent-skills --skill spree-dependencies

インストール後、Claude Code で/spree-dependenciesを入力して読み込まれたか確認します。

詳細

Spree の依存関係(クラス全体の差し替え)

コマンドでは Spree CLI(spree …)を使用します。CLI のない従来の Rails アプリでは、アプリのルートディレクトリから bin/rails … または bundle exec rake … を実行してください。パスは、Rails アプリが server/ にあることを前提としています。

Spree.dependencies を使うと、サービス、ワークフロー、ポリシー、シリアライザーなどのコアクラスを独自のクラスに置き換えられます。Spree のコードは Spree::Carts::AddItem.call(...) ではなく、Spree.cart_add_item_workflow.call(...) を呼び出すため、置き換えたクラスは Store API、Admin API、ダッシュボード、ジョブ、インポートなど、あらゆる場所で実行されます。

まず、そのクラスを本当に置き換える必要があるか確認してください

クラスを置き換えると、Spree のリリースごとに再同期が必要なコピーを自分で管理することになります。クラスを差し替える理由の多くは、アップグレード後も維持されるワークフックで対応できます。

目的差し替えの代わりに使うもの
カートへの追加/チェックアウト/キャンセル/返品をブロックするvalidate フック — Spree.hooks.register('carts.add_item.validate', 'MyStore::Rule')
税またはプロモーションの計算にデータを追加するコンテキストフック(carts.recalculate_totals.set_tax_line_context、carts.recalculate.set_promotion_context)
変更とあわせて行をアトミックに書き込むライフサイクルフック(carts.add_item.after_item_added、carts.complete.after_finalize)
ERP と同期する/メールを送信する/インデックスを再構築するイベントサブスクライバー(spree-events-webhooks)
チェックアウト要件を追加するSpree::Checkout::Registry.add_requirement(spree-checkout)
API レスポンスにフィールドを追加するシリアライザーをサブクラス化する(以下を参照)— 小規模で正当な差し替えです

フックの一覧については、spree-workflows スキルを参照してください。クラスの差し替えは、何かの計算方法を本当に変更する場合(異なるマージ戦略、カスタムの購入ボックスのランキング、B2B アクセスポリシー、コミッションモデル)に限ってください。

拡張ポイント

Spree::Core::Dependencies::INJECTION_POINTS_WITH_DEFAULTS(spree_core の lib/spree/core/dependencies.rb 内)では、167 個のコア拡張ポイントを定義しています。そのうち 106 個は *_workflow キーです。Spree::Api::ApiDependencies::INJECTION_POINTS_WITH_DEFAULTS(spree_api の lib/spree/api/dependencies.rb 内)には、**272 個の API 拡張ポイント(シリアライザーのみ)**があります。内訳は Store が 91 個(product_serializer)、Admin が 139 個(admin_product_serializer)、Seller が 42 個(seller_*_serializer)です。(数値は 6.0 のソースに基づいています。お使いのバージョンでは spree rake spree:dependencies:list を実行してください。)

名前から契約がわかります。

接尾辞基底クラスオーバーライドするもの
*_workflowSpree::Workflow のサブクラス#perform(**keywords) — #call ではありません
*_serviceprepend Spree::ServiceModule::Base を含むプレーンなクラス#call(**keywords)
*_class、*_finder、*_handler、その他さまざまです。デフォルトを確認してくださいデフォルトの説明に従ってください

よく差し替えられるもの:

キーデフォルト備考
cart_add_item_workflowSpree::Carts::AddItemperform(variant:, cart:, quantity:, metadata:, options:, price:)
cart_upsert_items_workflowSpree::Carts::UpsertItems数量の設定/削除/一括処理
cart_recalculate_workflowSpree::Carts::Recalculate価格を全面的に再計算します(配送、プロモーション、合計)
cart_recalculate_totals_workflowSpree::Carts::RecalculateTotals合計計算の単一拡張ポイント(割引/手数料/税の行 → 合計)
order_recalculate_totals_workflowSpree::Orders::RecalculateTotals注文確定後に再集計する対となる処理
cart_merge_workflowSpree::Carts::Mergeゲストのカートを顧客のカートにマージします
carts_complete_workflowSpree::Carts::Completeチェックアウトの完了 — carts.complete.* フックを優先してください
order_complete_workflow / order_cancel_workflowSpree::Orders::Complete / ::Cancel
order_update_statuses_serviceSpree::Orders::UpdateStatusespayment_status / fulfillment_status を更新する唯一の処理
fulfillment_create_workflow / fulfillment_update_workflowSpree::Fulfillments::Create / ::Update
payment_process_workflow、payment_capture_workflow、payment_void_workflow、refund_create_workflowSpree::Payments::*、Spree::Refunds::Create
return_create_workflow … claim_*_workflowSpree::Returns::*、Exchanges::*、Claims::*
storefront_access_policy_classSpree::Storefront::AccessPolicyStore API のレコード表示制御(所有権 + ゲストトークン)。B2B の拡張ポイントです
products_for_context_serviceSpree::Products::ForContext購入者向けにカタログを絞り込みます
product_buy_box_serviceSpree::Products::SelectBuyBoxマーケットプレイスで、どのバリアントを先頭に表示するかを決定します
purchase_amount_due_at_checkout_serviceSpree::Purchases::AmountDueAtCheckout頭金/後払い条件
tax_resolve_exemptions_serviceSpree::Tax::ResolveExemptions税プロバイダー向けの免税証明情報
reporting_adapterSpree::Reporting::Adapters::Live
search_product_presenternil(検索 gem によって設定されます)

ability_class(Spree::Ability)は、すべてのスタッフ向け CanCanCan 権限を構築します。対象は Admin API の JWT スタッフ、Seller API、インポート/エクスポートです。Spree::Ability をサブクラス化し、initialize(user, options = {})(options[:store]、options[:resource])を維持して、super を呼び出してからルールを追加してください。これは権限キーのゲートの背後で動作します。シークレット API キー(Spree::ApiKeyAbility)はこのクラスを参照しません。spree-auth-permissions を参照してください。

ワークフローの置き換え

提供されているワークフローをサブクラス化し、同じキーワード引数を受け取る perform をオーバーライドします。perform のシグネチャーが契約です(不一致があると Ruby は ArgumentError を発生させます)。引数なしの super を呼び出すと、親のフロー全体を実行して、その Result を返します。

# server/app/workflows/my_store/carts/add_item.rb
module MyStore
  module Carts
    class AddItem < Spree::Carts::AddItem
      # REQUIRED: without this the subclass dispatches 'my_store.carts.add_item.*'
      # and every handler registered on 'carts.add_item.*' silently stops firing.
      workflow_key 'carts.add_item'

      def perform(variant:, cart: nil, **rest)
        result = super                       # full Spree flow; a failure raises through, so code below runs only on success

        external_step :notify_recommendations # network I/O — outside any transaction
        result                                # Result#value is the line item
      end

      private

      def notify_recommendations
        MyStore::Recommendations.item_added(cart_id: cart.prefixed_id, sku: line_item.sku)
      end
    end
  end
end
# server/config/initializers/spree.rb
Spree.cart_add_item_workflow = MyStore::Carts::AddItem

ワークフローを置き換える際のルール:

  • call ではなく、perform をオーバーライドします。 Spree::Workflow#call は計測、failure/halt! の処理、補償処理を担います。
  • 親のキーを指す workflow_key を維持します(上記を参照)。これが、気付かれにくい不具合の最も一般的な原因です。
  • 一部だけを変更する場合は、プライベートなステップメソッド(例:add_to_line_item)をオーバーライドして super を呼び出します。ただし、ステップ名は内部実装であり、リリースによって変更される可能性があります。フックのほうが安全です。
  • Spree::ServiceModule::Result(super の戻り値、または success(value))を返します。Result 以外の戻り値は success でラップされます。
  • Spree.cart_add_item_workflow の呼び出し側は、親の Result#value の型(ここではラインアイテム)を想定しています。変更しないでください。
  • ワークフローはほかの接続点(step :recalculate, with: -> { Spree.cart_recalculate_workflow } など)を通じて連携するため、接続点を置き換えると、それに委譲するすべてのフローに影響します。

通常のサービスの置き換え

同じパターンで、call をオーバーライドします。

module MyStore
  class CartUpdate < Spree::Carts::Update
    def call(cart:, params:)
      result = super
      MyStore::ErpSync.enqueue(result.value.prefixed_id) if result.success?
      result
    end
  end
end

Spree.carts_update_service = MyStore::CartUpdate

デフォルトのクラスを継承すると、Spree::ServiceModule::Base の配線(クラスレベルの .call、success/failure)を維持できます。必ず Result を返してください。

レガシーな *_service キーは保存されますが、適用されません

ワークフローになったシームでは、アプリを起動できるように以前の名前も引き続き受け付けますが、割り当てても何も起きません。値は保存され、非推奨の警告で「Spree ではもはや参照されません — オーバーライドは適用されません」と表示されますが、Spree は引き続きワークフローを呼び出します。以前のサービスコントラクト(call(order:, …) と run ステップ)に合わせて書かれたクラスは、ワークフローコントラクトと互換性がありません。ワークフローに移植してください。ワークフローをサブクラス化し、ロジックを perform/フックに移して、新しいキーを割り当てます。これらの名前は 6.1 で削除されます。

レガシーキー(保存のみ)代わりに割り当てるキーデフォルト
cart_add_item_servicecart_add_item_workflowSpree::Carts::AddItem
cart_recalculate_servicecart_recalculate_workflowSpree::Carts::Recalculate
cart_merge_strategycart_merge_workflowSpree::Carts::Merge
carts_complete_servicecarts_complete_workflowSpree::Carts::Complete
carts_upsert_items_servicecart_upsert_items_workflowSpree::Carts::UpsertItems
order_complete_serviceorder_complete_workflowSpree::Orders::Complete
order_cancel_serviceorder_cancel_workflowSpree::Orders::Cancel
fulfillment_create_servicefulfillment_create_workflowSpree::Fulfillments::Create
shipment_update_service, fulfillment_update_servicefulfillment_update_workflowSpree::Fulfillments::Update
payments_handle_webhook_servicepayments_handle_webhook_workflowSpree::Payments::HandleWebhook
gift_card_apply/remove/redeem_servicegift_card_apply/remove/redeem_workflowSpree::GiftCards::*

単なる名前変更(適用され、警告が表示されます):checkout_add_store_credit_service → store_credit_apply_service、checkout_remove_store_credit_service → store_credit_remove_service。API では、stock_item_serializer → stock_level_serializer、digital_serializer → digital_asset_serializer(および admin_ の同名キー)。

削除済みのキー(割り当てると起動時に NoMethodError が発生します):carts_validate_service(完了時の検証は Spree::Checkout::Requirements と carts.complete.validate です)、checkout_next_service、checkout_update_service、checkout_complete_service、checkout_select_shipping_method_service、checkout_get_shipping_rates_service、cart_create_service、cart_update_service、cart_estimate_shipping_rates_service、cart_change_currency_service、line_item_*_service、shipment_*_service、account_*_service、およびレガシーな検索・ソート機能(products_finder、taxon_finder、variant_finder、…)。カートの作成・更新には carts_create_service/carts_update_service を使用します。チェックアウトの進行には checkout_advance_service を使用します。5.x から移行する場合は、spree-upgrade-5-to-6 をご覧ください。

API シリアライザー

API の依存関係はシリアライザーのみです。API ごとのサービスキーはありません。すべてのエンドポイントがコアの Spree.<key> シームを直接呼び出します。

# server/app/serializers/my_store/product_serializer.rb
module MyStore
  class ProductSerializer < Spree::Api::V3::ProductSerializer   # Alba resource
    typelize brand_name: [:string, nullable: true]
    attribute :brand_name do |product|
      product.brand&.name
    end
  end
end

# config/initializers/spree.rb
Spree.api.product_serializer       = 'MyStore::ProductSerializer'        # Store API
Spree.api.admin_product_serializer = 'MyStore::Admin::ProductSerializer' # Admin API — separate key
  • Store と Admin のキーは独立しています。Spree::Api::V3::Admin::ProductSerializer は、定数を通じて 同梱の Spree::Api::V3::ProductSerializer を継承します。そのため、product_serializer を差し替えても Admin の出力は変わりません。両方にフィールドが必要な場合は、サブクラス化して両方を差し替えてください。
  • ネストされた関連付けは、多くの場合アクセサーを通じて解決されます(one :product, resource: proc { Spree.api.product_serializer })。その場合、こちらでも独自のクラスが使用されます。
  • @spree/sdk の型付きフィールドを追加するには、typelize を使用してください(spree-typescript-sdk を参照)。

コントローラーごとのオーバーライド

1つのエンドポイントだけを変更するには、API コントローラーの名前付きフック(serializer_class、scope、collection_includes、model_class、resource_permitted_attributes)をデコレートします。

# server/app/controllers/spree/api/v3/store/products_controller_decorator.rb
module Spree::Api::V3::Store
  module ProductsControllerDecorator
    protected

    def serializer_class
      MyStore::DetailedProductSerializer
    end
  end
end

Spree::Api::V3::Store::ProductsController.prepend(Spree::Api::V3::Store::ProductsControllerDecorator)

コントローラーごとの *_service フックはありません。コントローラーはグローバルシームを呼び出します。spree-decorators をご覧ください。

独自のコードからのシームの利用

Spree.cart_add_item_workflow.call(cart: cart, variant: variant, quantity: 1)   # resolves overrides
Spree.api.cart_serializer.new(cart, params: { store: current_store }).to_h

Spree::Carts::AddItem.call(...)   # ❌ bypasses any override

特に拡張機能では、ホストアプリのオーバーライドと組み合わせられるよう、必ずアクセサーを経由してください。

構文:Spree.<key> = MyClass(推奨。定数名のタイプミスがあれば直ちにエラーになります)、複数の場合は Spree.dependencies { |d| d.<key> = MyClass }、または旧形式の Spree::Dependencies.<key> = 'MyClass'(文字列。遅延解決され、引き続きサポートされています)。

オーバーライドの確認

spree rake spree:dependencies:list        # every seam with its current value, [OVERRIDDEN] marked
spree rake spree:dependencies:list | grep workflow
spree rake spree:dependencies:overrides   # only overrides: default -> current (file:line that set it)
spree rake spree:dependencies:validate    # constantize every seam; exits 1 on NameError — good CI check

継承したプロジェクトでは、まず overrides を実行してください。プログラムから同等の操作を行うには、Spree::Dependencies.current_values、.overridden?(:cart_add_item_workflow)、.override_info(:cart_add_item_workflow)、.validate! を使用します。API 側では Spree::Api::Dependencies を使用してください。旧形式の stash のみに対する書き込みはオーバーライドとして表示されません。アップグレード後、イニシャライザー内で _service = を検索してください。

注意点

  • 読み込み順序: オーバーライドは config/initializers/spree.rb に記述します。2つの gem が同じキーを設定した場合、最後の代入が優先されます。spree:dependencies:overrides で確認してください。
  • ワークフローを差し替えたらフックが失われた → workflow_key がありません(上記を参照)。
  • アップグレード後、オーバーライドが「何もしない」 → 旧形式の *_service キーを代入しています。起動ログで「NO LONGER CONSULTED」の非推奨警告を確認してください。
  • 動作の欠落: super を呼び出さない置き換えでは、標準の予約、価格プロバイダー、利用可能性の確認、再計算、イベントを再実装する必要があります。まず親の perform を最後まで読んでください。
  • 呼び出し時の ArgumentError → perform のシグネチャが、呼び出し側から渡されるキーワード引数を受け付けていません。**rest を受け入れて転送してください。

さらに読むには

  • node_modules/@spree/docs/dist/developer/customization/dependencies.md (https://spreecommerce.org/docs/developer/customization/dependencies) — 注:公開ページの一部には、API v2 の storefront_* キーがまだ記載されています。ソース内の定数を正式な情報として参照してください。
  • node_modules/@spree/docs/dist/developer/customization/workflows.md — フックと、推奨される代替方法
  • ソース:spree_core の lib/spree/core/dependencies.rb(INJECTION_POINTS_WITH_DEFAULTS、LEGACY_WORKFLOW_KEYS、LEGACY_SERVICE_KEYS、RENAMED_SERVICE_KEYS)、spree_api の lib/spree/api/dependencies.rb、lib/tasks/dependencies.rake
  • 関連スキル:spree-workflows、spree-customization、spree-decorators、spree-extensions

コメント