store-model-coder

StoreModel: JSON-Backed ActiveRecord Attributes

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "store-model-coder" with this command: npx skills add majesticlabs-dev/majestic-marketplace/majesticlabs-dev-majestic-marketplace-store-model-coder

StoreModel: JSON-Backed ActiveRecord Attributes

Wrap JSON-backed database columns with ActiveModel-like classes for type safety, validations, and clean separation of concerns.

When to Use This Skill

  • Configuration objects with validated fields

  • Nested attributes stored in JSON columns

  • JSON data requiring type safety and ActiveModel behavior

  • Separating JSON logic from parent ActiveRecord model

When NOT to Use StoreModel

Scenario Better Alternative

Simple key-value settings ActiveRecord::Store or store_accessor

Need database-level queries on JSON Raw jsonb with PostgreSQL operators

Data needs relationships/joins Normalize into separate tables

Truly simple JSON without validation Plain JSON column access

Setup

Gemfile

gem "store_model", "~> 3.0"

Basic Usage

Define a StoreModel Class

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

Register in ActiveRecord

app/models/product.rb

class Product < ApplicationRecord attribute :configuration, Configuration.to_type end

Usage

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")

Enums

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"

Validations

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

Merging Errors to Parent

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"]

Nested Models

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

Array of Models

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

Dirty Tracking

StoreModel doesn't automatically detect nested changes. Use one of these approaches:

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" }

Controller Pattern

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

Testing

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

Detailed References

For advanced patterns:

  • references/advanced-patterns.md
  • Nested attributes, custom types, one-of types, parent tracking

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

google-ads-strategy

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

viral-content

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

market-research

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

free-tool-arsenal

No summary provided by upstream source.

Repository SourceNeeds Review