mcpField
S
store-model-coder

store-model-coder

github.com/majesticlabs-dev開発5046 回インストール7 週間前に更新
リポジトリ

store-model-coder について

JSON データベース列を型付き ActiveRecord 属性クラスとして扱う Claude スキルです。Rails 開発者向けに、検証、入れ子構造、列挙型を扱います。

主な機能

  1. 01型付き属性とデフォルト値
  2. 02バリデーションとエラー統合
  3. 03列挙型
  4. 04入れ子モデルとモデル配列
  5. 05変更追跡のガイダンス

活用シーン

  1. 01JSON 列の設定データをモデル化・検証
  2. 02入れ子の住所や座標データを表現
  3. 03型付き明細項目の配列を保存

インストール

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

$npx skills add https://github.com/majesticlabs-dev/majestic-marketplace --skill store-model-coder

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

同梱ファイルreferences/advanced-patterns.md

詳細

StoreModel:JSON 対応 ActiveRecord 属性

JSON 対応のデータベース列を ActiveModel のようなクラスでラップし、型安全性、バリデーション、関心事の適切な分離を実現します。

このスキルを使用する場面

  • フィールドを検証する設定オブジェクト
  • JSON 列に保存するネストされた属性
  • 型安全性と ActiveModel の動作が必要な JSON データ
  • JSON のロジックを親の ActiveRecord モデルから分離する場合

StoreModel を使用しない場面

シナリオより適した代替手段
単純なキーと値の設定ActiveRecord::Store または store_accessor
JSON に対するデータベースレベルのクエリが必要PostgreSQL 演算子を使った生の jsonb
データにリレーションや結合が必要別テーブルに正規化
バリデーション不要の本当に単純な JSON通常の JSON 列へのアクセス

セットアップ

# Gemfile
gem "store_model", "~> 3.0"

基本的な使い方

StoreModel クラスを定義する
# app/models/configuration.rb
class Configuration
  include StoreModel::Model

  attribute :model, :string
  attribute :color, :string
  attribute :max_speed, :integer, default: 100

  validates :model, presence: true
  validates :max_speed, numericality: { greater_than: 0 }
end
ActiveRecord に登録する
# app/models/product.rb
class Product < ApplicationRecord
  attribute :configuration, Configuration.to_type
end
使い方
product = Product.new
product.configuration = { model: "rocket", color: "red" }
product.configuration.model  # => "rocket"
product.configuration.color = "blue"
product.save!

# Also accepts StoreModel instances
product.configuration = Configuration.new(model: "shuttle")

列挙型

class Configuration
  include StoreModel::Model

  attribute :model, :string

  enum :status, %i[draft active archived], default: :draft

  # With custom values
  enum :priority, { low: 0, medium: 1, high: 2 }, default: :medium
end
config = Configuration.new
config.status        # => "draft"
config.active?       # => false
config.active!       # Sets status to :active
config.status        # => "active"

バリデーション

class Address
  include StoreModel::Model

  attribute :street, :string
  attribute :city, :string
  attribute :zip, :string
  attribute :country, :string, default: "US"

  validates :street, :city, :zip, presence: true
  validates :zip, format: { with: /\A\d{5}(-\d{4})?\z/ }, if: -> { country == "US" }
end
エラーを親に統合する
class User < ApplicationRecord
  attribute :address, Address.to_type

  validates :address, store_model: { merge_errors: true }
end

user = User.new(address: { street: "", city: "" })
user.valid?
user.errors.full_messages
# => ["Address street can't be blank", "Address city can't be blank", "Address zip can't be blank"]

ネストされたモデル

class Coordinate
  include StoreModel::Model

  attribute :latitude, :float
  attribute :longitude, :float

  validates :latitude, :longitude, presence: true
end

class Location
  include StoreModel::Model

  attribute :name, :string
  attribute :coordinate, Coordinate.to_type

  validates :name, presence: true
  validates :coordinate, store_model: { merge_errors: true }
end

モデルの配列

class LineItem
  include StoreModel::Model

  attribute :name, :string
  attribute :quantity, :integer, default: 1
  attribute :price_cents, :integer

  validates :name, :price_cents, presence: true
end

class Order < ApplicationRecord
  attribute :line_items, LineItem.to_array_type

  validates :line_items, store_model: { merge_array_errors: true }
end
order = Order.new
order.line_items = [
  { name: "Widget", quantity: 2, price_cents: 1000 },
  { name: "Gadget", quantity: 1, price_cents: 2500 }
]
order.line_items.first.name  # => "Widget"
order.line_items.sum(&:price_cents)  # => 3500

変更追跡

StoreModel はネストされた変更を自動的に検出しません。次のいずれかの方法を使用してください。

# Option 1: Reassign the entire object
product.configuration = product.configuration.dup.tap { |c| c.color = "green" }

# Option 2: Mark as changed explicitly
product.configuration.color = "green"
product.configuration_will_change!
product.save!

# Option 3: Use attribute assignment
product.configuration = { **product.configuration.attributes, color: "green" }

コントローラーのパターン

class ProductsController < ApplicationController
  def create
    @product = Product.new(product_params)

    if @product.save
      redirect_to @product
    else
      render :new, status: :unprocessable_entity
    end
  end

  private

  def product_params
    params.require(:product).permit(
      :name,
      configuration: [:model, :color, :max_speed, :status]
    )
  end
end

テスト

RSpec.describe Configuration do
  describe "validations" do
    it "requires model" do
      config = described_class.new(model: nil)
      expect(config).not_to be_valid
      expect(config.errors[:model]).to include("can't be blank")
    end
  end

  describe "enums" do
    it "defaults to draft status" do
      config = described_class.new
      expect(config).to be_draft
    end

    it "transitions status" do
      config = described_class.new
      config.active!
      expect(config).to be_active
    end
  end
end

RSpec.describe Product do
  describe "configuration" do
    it "accepts hash" do
      product = described_class.new(configuration: { model: "rocket" })
      expect(product.configuration.model).to eq("rocket")
    end

    it "merges validation errors" do
      product = described_class.new(configuration: { model: nil })
      expect(product).not_to be_valid
      expect(product.errors[:configuration]).to be_present
    end
  end
end

詳細なリファレンス

高度なパターンについては、次を参照してください。

  • references/advanced-patterns.md - ネストされた属性、カスタム型、one-of 型、親の追跡

コメント