mcpField
S
spree-resource

spree-resource

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

spree-resource について

spree-resource は、CLI で API リソースを生成する Spree 開発者向けガイドです。モデル、マイグレーション、Store と Admin API の構成に加え、フィールド規約、権限、ルート、生成ファイルの管理方法を説明します。

主な機能

  1. 01モデルとデータベースマイグレーションの生成
  2. 02Store・Admin API のコントローラーとシリアライザー生成
  3. 03ルートと権限スコープの設定
  4. 04ストアスコープ、ライフサイクルイベント、カスタムフィールドの指定
  5. 05生成ファイルの管理規約の説明

活用シーン

  1. 01Spree に新しい API リソースを追加
  2. 02読み取り専用カタログや書き込み可能な顧客リソースを作成
  3. 03管理画面専用リソースを生成

インストール

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

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

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

詳細

Spree リソースの追加

コマンドは Spree CLI(spree …、Docker、server/ 内の Rails アプリ)を使用します。CLI を使わない従来の Rails アプリでは、アプリのルートディレクトリから bin/rails g spree:api_resource … を実行し、パスから server/ のプレフィックスを外してください(spree-project を参照)。

v3 API で公開するモデルを追加するには、spree:api_resource ジェネレーターを使用します。1 つのコマンドで、次のものが生成されます。

  • モデル(server/app/models/spree/<name>.rb)とそのマイグレーション
  • Store API と Admin API のコントローラーおよびシリアライザー
  • FactoryBot ファクトリーと、両方の API 用コントローラースペック
  • アプリの config/routes.rb にある Spree::Core::Engine.add_routes ブロック内のルート定義
  • read_<plural> / write_<plural> 権限スコープ(config/initializers/spree.rb に追加される Spree.permissions.register_scope)と、そのロールエディターのラベル(config/locales/spree_<plural>.en.yml)— Admin のみ

前提条件:先に spree eject を実行してください。ジェネレーターは Web コンテナ内で実行されます。また、./server をバインドマウントするのは eject 済みの開発用 compose のみなので、生成されたファイルはホスト上に表示され、保持されます。

1 つのコマンドで実行する方法

spree generate api_resource Brand name:string:index slug:string:uniq active:boolean
spree migrate

出力の概要:

✓ Generated Spree::Brand API resource
  Prefixed ID:  brand_xxxxxxxxxx
  Store API:    /api/v3/store/brands  (read-only)
  Admin API:    /api/v3/admin/brands  (full CRUD)

フィールドの構文は Rails の属性パーサーに従い、Spree の規約が適用されます。

入力内容生成されるもの
name:string / :text / :integer / :decimal / :boolean / :date / :datetimenull: false のカラム。boolean 以外のフィールドには presence バリデーション
name:string:index非ユニークインデックス
slug:string:uniqユニークインデックスと一意性バリデーション。モデルがストアスコープの場合(デフォルト)、store_id でスコープされ、さらに spree_base_uniqueness_scope が適用されます
brand:belongs_to(または :references)class_name とインデックスを持つ belongs_to。外部キー制約なし、null: false
category:belongs_to{TaxCategory}明示的なクラスヒント(非修飾名のみ)→ class_name: 'TaxCategory'。Spree 名前空間内で解決されます
description:rich_texthas_spree_rich_text と Spree::SanitizableRichText
logo:attachment / photos:attachmentshas_one_attached / has_many_attached

belongs_to のクラス解決:user → Spree.customer_class、admin_user、created_by、approver、canceler → Spree.admin_user_class、それ以外 → Spree::<Camelized>。

フラグ

フラグデフォルト効果
--writableオフStore API で create/update/destroy が可能になります。デフォルトの Store API は読み取り専用(index + show)です。
--no-store—Store API のコントローラー、シリアライザー、ルートをスキップします。Admin 専用のリソースです。
--no-admin—Admin API の機能をスキップします。まれなケースです。
--store-name=Discount—Store API で別の名前(/api/v3/store/discounts)として公開します。モデル、テーブル、Admin API では標準名が維持されます。
--no-store-scopedスコープありデフォルトでは、モデルに Spree::SingleStoreResource(store_id カラム、belongs_to :store、Spree::Current.store からの自動割り当て、for_store スコープ、後からのストア変更不可)が含まれます。グローバルな参照データの場合にのみ無効にしてください。
--no-lifecycle-eventsオンデフォルトでは、モデルが publishes_lifecycle_events を呼び出します(サブスクライバーと Webhook 向けの brand.created/updated/deleted イベント)。
--custom-fieldsオフSpree::HasCustomFields と Spree::Metadata(マーチャント定義のカスタムフィールドと metadata JSON)を含めます。
--paranoidオフacts_as_paranoid と deleted_at カラム/インデックスを追加します。Admin シリアライザーで deleted_at を公開します。
--id-prefix=brスネークケースにしたクラス名プレフィックス付き ID のプレフィックス(デフォルトは brand_…)。Core では短いプレフィックスを使用します:prod_、variant_、cart_、or_、py_、ful_、cust_、sl_。
--parent=Spree::FooSpree.base_class親クラスの式。
--permission-group=settingscatalogロールエディターと API キーのスコープ選択画面で read_ / write_ キーが表示される場所(orders、catalog、customers、settings など、または独自の小文字の識別子)。
--skip-routesオフルートを変更しません。
--skip-specsオフコントローラースペックを生成しません。

作成されるもの

server/app/models/spree/brand.rb                                    (owned-once)
server/db/migrate/<ts>_create_spree_brands.rb                       (append-only)
server/app/controllers/spree/api/v3/store/brands_controller.rb      (managed)
server/app/controllers/spree/api/v3/admin/brands_controller.rb      (managed)
server/app/serializers/spree/api/v3/brand_serializer.rb             (managed)
server/app/serializers/spree/api/v3/admin/brand_serializer.rb       (managed)
server/spec/factories/spree/brand_factory.rb                        (managed)
server/spec/controllers/spree/api/v3/{store,admin}/brands_controller_spec.rb (managed)
server/config/routes.rb                                             (idempotent inject)
server/config/initializers/spree.rb                                 (idempotent append — register_scope)
server/config/locales/spree_brands.en.yml                           (owned-once — permission label)

生成されるモデル(ストアスコープのデフォルト):

module Spree
  class Brand < Spree.base_class
    include Spree::SingleStoreResource

    has_prefix_id :brand
    publishes_lifecycle_events

    validates :name, presence: true
    validates :slug, presence: true, uniqueness: { scope: [:store_id, *spree_base_uniqueness_scope] }
    self.whitelisted_ransackable_attributes = %w[name slug active]
    self.whitelisted_ransackable_associations = %w[]
    self.whitelisted_ransackable_scopes = %w[]
  end
end

ルート。 ジェネレーターは、config/routes.rb の Spree::Core::Engine.add_routes 内にある namespace :store / namespace :admin ブロックに、それぞれ resources :brands, only: [:index, :show](ストア)と resources :brands(管理者)を挿入します(create-spree-app / spree-starter では、このブロックは空の状態で提供されます)。ブロックまたは名前空間がない場合は、警告を出してスキップします。手動で追加してください:

Rails.application.routes.draw do
  Spree::Core::Engine.add_routes do
    namespace :api, defaults: { format: 'json' } do
      namespace :v3 do
        namespace :store do
          resources :brands, only: [:index, :show]
        end
        namespace :admin do
          resources :brands
        end
      end
    end
  end
  mount Spree::Core::Engine, at: '/'
end

「一度だけ所有/管理対象/追記のみ」の契約

  • モデル(一度だけ所有) — 一度だけ書き込まれ、再実行しても変更されません。バリデーション、スコープ、アソシエーションはここに記述します。
  • マイグレーション(追記のみ) — 一度だけ生成されます。スキーマの変更には、新しいマイグレーションを使用します:spree generate migration AddLogoToSpreeBrands logo_url:string。
  • コントローラー、シリアライザー、ファクトリー、スペック(管理対象) — 再実行時に上書きされます。手動で編集した場合、そのリソースに対してジェネレーターを再実行しないでください(または、編集内容を再適用してください)。
  • ルートと権限スコープ(冪等) — ルートの行または register_scope(:brands, …) がすでに存在する場合、重複して追加されません。権限のロケールファイルは一度だけ所有されるため、編集したラベルは再実行後も保持されます。

ジェネレーターの実行後

  1. モデルを確認する — バリデーション、アソシエーション、スコープを追加します。生成される belongs_to は必須です(Spree のモデルでは、デフォルトで belongs_to が必須です。失敗時のメッセージは「must exist」です)。任意の参照にするには optional: true を追加し、マイグレーションの null: false を緩和します。

  2. マイグレーションを実行する — spree migrate。

  3. 権限を付与する — Admin コントローラーは scoped_resource :brands を宣言し、ジェネレーターは config/initializers/spree.rb に対応するスコープを登録しています:

    # Permissions for the Brand API — grants read_brands / write_brands
    # to staff roles and secret API keys. Labels: config/locales/spree_brands.en.yml
    Spree.permissions.register_scope(:brands, group: :catalog, resources: -> { [Spree::Brand] })
    

    スタッフのロール(ダッシュボードの Settings → Roles、Admin API、またはシード)に read_brands / write_brands を付与するか、シークレットキーにそれらを設定するまでは、フルアクセスのプリンシパルのみがアクセスできます(admin ロール、read_all / write_all キー)。ラベルは spree.permissions_catalog.resources.brands(label、任意の description)にあります。--no-admin を指定すると、何も登録されません。spree-auth-permissions を参照してください。

  4. 読み取り時のストアスコープ — ベースコントローラーは、コレクションを Model.for_store(current_store) でスコープします。SingleStoreResource がこのスコープを提供します。--no-store-scoped モデルでは、アソシエーションがあれば for_store は current_store.<plural> にフォールバックし、なければテーブル全体にフォールバックします。current_store.brands を使いたい場合は、デコレーター経由で Spree::Store に has_many :brands, class_name: 'Spree::Brand', dependent: :destroy を追加します。権限によって決まるのは、ブランドを読み取れるかどうかであり、どのブランドを読み取れるかではありません。検索はストアスコープのままにしてください。

  5. 許可する属性 — 生成されるコントローラーでは、permitted_params のオーバーライドではなく、resource_permitted_attributes で書き込み可能なカラムを宣言します。そのため、拡張機能が宣言した属性(Spree::Brand.additional_permitted_attributes += [...])とパラメーターの正規化は引き続き適用されます:

    def resource_permitted_attributes
      [:name, :slug, :active]
    end
    

    クライアントが書き込むべきでないカラムを除外するには、このリストを編集します。ただし、コントローラーは管理対象のため、再実行するとテンプレートに戻ります。

  6. スペックを実行する — spree rspec spec/controllers/spree/api/v3/。スペックでは、spree_api のテストサポートが提供する 'API v3 Store guest' / 'API v3 Admin authenticated' の共有コンテキストを使用します(spree-testing を参照してください)。

ステータスの追加

Spree にステートマシンはありません。ライフサイクルを持つレコードには、文字列の status カラムを追加して Spree::HasStatus を使用します。遷移はワークフローに記述します(spree-workflows を参照してください):

include Spree::HasStatus
has_status :draft, :active, :archived, default: :draft   # predicates (active?), scopes (.active), inclusion validation, .with_status(...)

拡張機能では、Spree::Brand.add_status('paused', after: 'active') を使ってステータスを追加できます。ステータスには Rails の enum を使用しないでください。

TypeScript の型

プロジェクト内で生成するシリアライザーはアプリ内のものであり、公開されている @spree/sdk / @spree/admin-sdk の型には含まれません。独自のエンドポイントは、手書きの型とともに client.request<T>(...) で呼び出します(spree-typescript-sdk を参照してください)。生成されるシリアライザーの typelize 行には、JSON の型が記載されています(小数は文字列としてシリアライズされます)。

一般的なパターン

# Read-only catalog resource (Store GET, Admin CRUD)
spree generate api_resource Brand name:string:uniq active:boolean

# Writable customer-facing resource
spree generate api_resource SavedList label:string user:belongs_to --writable

# Admin-only back-office data
spree generate api_resource SupplierNote body:text --no-store

# Soft-delete + custom fields
spree generate api_resource Vendor name:string:uniq slug:string:uniq --paranoid --custom-fields

# Global reference data (no store_id)
spree generate api_resource Certification code:string:uniq --no-store-scoped

書き込み可能な顧客リソースでは、Store API は所有権に基づいて認可されることを忘れないでください。Store コントローラーの scope をオーバーライドし、顧客経由で読み取るようにします(例:current_user.saved_lists)。そうしないと、すべての購入者がストア内のすべてのレコードを閲覧できてしまいます。

モデルのみ — API サーフェスなし

spree:model は、同じ規約(ID のプレフィックス、Spree.base_class、ストアスコープ、ライフサイクルイベント、null: false、外部キーなし、Ransack の許可リスト)に従ってモデルとマイグレーションを生成し、同じモデルフラグ(--paranoid、--custom-fields、--id-prefix、--parent、--no-store-scoped、--no-lifecycle-events)を受け付けます。api_resource と異なり、Rails のテストフレームワーク用フックはそのまま維持されるため、rspec/factory_bot が設定されていれば、スタブのモデルスペックとファクトリーも生成されます。

spree generate model BrandImage position:integer brand:belongs_to

レコードが内部用(ログ、結合テーブル)の場合、親の API 経由でのみアクセスする場合、またはコントローラーを手書きする場合に使用してください。後から API を追加するには、spree generate api_resource Brand … を実行します。既存のモデルを検出してモデルとマイグレーションをスキップし(所有者による一度限りの生成)、API サーフェスのみを生成します。

注意点

  • 外部キー制約や Rails の enum は追加しないでください。 foreign_key: false と文字列のステータスを使用してください。
  • コアモデルの拡張(例:products の brand_id)は、デコレーターと Spree::Product.additional_permitted_attributes += [:brand_id] で行います(配列は凍結されているため、<< は使用しないでください)。spree-decorators を参照してください。
  • ジョブ、rake タスク、コンソール: ストアスコープのレコードにはストアが必要です。Spree::Current.store = store を設定してください(Spree::Store.default は nil の場合があるため、これに依存しないでください)。
  • カスタムの読み取り専用メンバーアクション — コントローラーの read_actions に追加し、write_* ではなく read_* にマッピングされるようにしてください。

さらに詳しく読むには

  • node_modules/@spree/docs/dist/developer/tutorial/model-and-api.md — Brand の一連の手順を最初から最後まで解説
  • node_modules/@spree/docs/dist/developer/customization/permissions.md — register_scope
  • node_modules/@spree/docs/dist/developer/customization/api.md — API の拡張
  • ジェネレーターのソース:spree/core/lib/generators/spree/{api_resource,model}/

コメント