Back to list
radioactive-labs

plutonium-model-features

by radioactive-labs

Build production-ready Rails apps in minutes, not days. Convention-driven, fully customizable, AI-ready.

52🍴 7📅 Jan 23, 2026

SKILL.md


name: plutonium-model-features description: Plutonium model features - has_cents, associations, scopes, and routing

Plutonium Model Features

Advanced features available in Plutonium resource models.

Monetary Handling (has_cents)

Store monetary values as integers (cents) while exposing decimal interfaces.

Basic Usage

class Product < ResourceRecord
  has_cents :price_cents                    # Creates price getter/setter
  has_cents :cost_cents, name: :wholesale   # Custom accessor name
  has_cents :tax_cents, rate: 1000          # 3 decimal places
  has_cents :quantity_cents, rate: 1        # Whole numbers only
end

product = Product.new
product.price = 19.99
product.price_cents  # => 1999
product.price        # => 19.99

# Truncates (doesn't round)
product.price = 10.999
product.price_cents  # => 1099

Options

has_cents :field_cents,
  name: :custom_name,    # Accessor name (default: field without _cents)
  rate: 100,             # Conversion rate (default: 100)
  suffix: "amount"       # Suffix for generated name (default: "amount")

Validation

class Product < ResourceRecord
  has_cents :price_cents

  # Validate the cents field
  validates :price_cents, numericality: {greater_than: 0}
end

product = Product.new(price: -10)
product.valid?  # => false
product.errors[:price_cents]  # => ["must be greater than 0"]
product.errors[:price]        # => ["is invalid"] (propagated)

Introspection

Product.has_cents_attributes
# => {price_cents: {name: :price, rate: 100}, ...}

Product.has_cents_attribute?(:price_cents)  # => true

Association SGID Support

All associations get Signed Global ID (SGID) methods for secure serialization.

Singular Associations (belongs_to, has_one)

class Post < ResourceRecord
  belongs_to :user
  has_one :featured_image
end

post = Post.first

# Get SGID
post.user_sgid           # => "BAh7CEkiCG..."
post.featured_image_sgid # => "BAh7CEkiCG..."

# Set by SGID (finds and assigns)
post.user_sgid = "BAh7CEkiCG..."
post.featured_image_sgid = "BAh7CEkiCG..."

Collection Associations (has_many, has_and_belongs_to_many)

class User < ResourceRecord
  has_many :posts
  has_and_belongs_to_many :roles
end

user = User.first

# Get SGIDs
user.post_sgids  # => ["BAh7CEkiCG...", "BAh7CEkiCG..."]
user.role_sgids  # => ["BAh7CEkiCG...", "BAh7CEkiCG..."]

# Bulk assignment
user.post_sgids = ["BAh7CEkiCG...", ...]

# Individual manipulation
user.add_post_sgid("BAh7CEkiCG...")     # Add to collection
user.remove_post_sgid("BAh7CEkiCG...")  # Remove from collection

Use Cases

  • Secure form submissions without exposing internal IDs
  • API responses with portable references
  • Caching and serialization

Entity Scoping (associated_with)

Query records associated with another record. Essential for multi-tenant apps.

Basic Usage

class Comment < ResourceRecord
  belongs_to :post
end

# Find comments for a post
Comment.associated_with(post)
# => Comment.where(post: post)

Association Detection

Works with:

  • belongs_to - Uses WHERE clause (most efficient)
  • has_one - Uses JOIN + WHERE
  • has_many - Uses JOIN + WHERE
# Direct association (preferred)
Comment.associated_with(post)  # WHERE post_id = ?

# Reverse association (less efficient, logs warning)
Post.associated_with(comment)  # JOIN comments WHERE comments.id = ?

Custom Scopes

For optimal performance, define custom scopes:

class Comment < ResourceRecord
  # Custom scope naming: associated_with_{model_name}
  scope :associated_with_user, ->(user) do
    joins(:post).where(posts: {user_id: user.id})
  end
end

# Automatically uses custom scope
Comment.associated_with(user)

Error Handling

# When no association exists
UnrelatedModel.associated_with(user)
# Raises: Could not resolve the association between 'UnrelatedModel' and 'User'
#
# Define:
#  1. the associations between the models
#  2. a named scope on UnrelatedModel e.g.
#
# scope :associated_with_user, ->(user) { do_something_here }

URL Routing

Default Behavior

user = User.find(1)
user.to_param  # => "1"

Custom Path Parameters

Use a stable, unique field instead of ID:

class User < ResourceRecord
  private

  def path_parameter(param_name)
    :username  # Must be unique
  end
end

user = User.create(username: "john_doe")
user.to_param  # => "john_doe"
# URLs: /users/john_doe

Dynamic Path Parameters (SEO-friendly)

Include ID prefix for uniqueness with human-readable suffix:

class Article < ResourceRecord
  private

  def dynamic_path_parameter(param_name)
    :title
  end
end

article = Article.create(id: 1, title: "My Great Article")
article.to_param  # => "1-my-great-article"
# URLs: /articles/1-my-great-article

Path Parameter Lookup

# Scope for finding by path parameter
User.from_path_param("john_doe")
Article.from_path_param("1-my-great-article")  # Extracts ID

Association Route Discovery

class User < ResourceRecord
  has_many :posts
  has_many :comments
  accepts_nested_attributes_for :posts
end

# Get has_many association names
User.has_many_association_routes
# => ["posts", "comments"]

# Get nested attributes config
User.all_nested_attributes_options
# => {posts: {allow_destroy: false, update_only: false, macro: :has_many, class: Post}}

Performance Tips

Field Introspection

# Cached in production, fresh in development
User.resource_field_names  # First call queries, subsequent cached

Association Queries

# Efficient: Direct belongs_to
Comment.associated_with(post)  # Simple WHERE

# Less efficient: Reverse has_many (logs warning)
Post.associated_with(comment)  # JOIN required

# Optimal: Custom scope when direct isn't possible
scope :associated_with_user, ->(user) { where(user_id: user.id) }

SGID Operations

# Efficient: Batch assignment
user.post_sgids = sgid_array  # Single operation

# Inefficient: Individual adds
sgid_array.each { |sgid| user.add_post_sgid(sgid) }
  • plutonium-model - Model overview and structure
  • plutonium-create-resource - Scaffold generator

Score

Total Score

75/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

+10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon