mcpField
S
store-model-coder

store-model-coder

github.com/majesticlabs-dev开发5046 次安装7 周前更新
代码仓库

关于 store-model-coder

面向 Rails 开发者的 Claude Skill,将 JSON 数据库列建模为带类型的 ActiveRecord 属性类,支持验证、嵌套结构和枚举。

主要功能

  1. 01带类型的属性与默认值
  2. 02验证及错误合并
  3. 03枚举
  4. 04嵌套模型与模型数组
  5. 05脏数据跟踪指引

适用场景

  1. 01为 JSON 列中的配置建模并验证
  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 属性

使用类似 ActiveModel 的类封装 JSON 后端数据库列,实现类型安全、验证和清晰的职责分离。

适用场景

  • 带有已验证字段的配置对象
  • 存储在 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 类型、父级跟踪

评论

支持 Markdown