CtrlK
BlogDocsLog inGet started
Tessl Logo

igmarin/rails-agent-skills

Curated library of 39 AI agent skills for Ruby on Rails development. Organized by category: planning, testing, code-quality, ddd, engines, infrastructure, api, patterns, context, orchestration, and workflows. Includes 5 callable workflow skills (rails-tdd-loop, rails-review-flow, rails-setup-flow, rails-quality-flow, rails-engines-flow) for complete development cycles. Covers code review, architecture, security, testing (RSpec), engines, service objects, DDD patterns, and TDD automation.

95

1.20x
Quality

98%

Does it follow best practices?

Impact

95%

1.20x

Average score across 35 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

SKILL.mdskills/patterns/strategy-factory-null-calculator/

name:
strategy-factory-null-calculator
license:
MIT
description:
Use when building variant-based calculators with a single entry point that picks the right implementation (Strategy + Factory), or when adding a no-op fallback (Null Object). Generates variant-based calculator classes, implements SERVICE_MAP routing, and scaffolds RSpec tests per variant. Trigger words: design pattern, Ruby, dispatch table, polymorphism, no-op default, variant calculator, strategy pattern, factory pattern, null object pattern.

Strategy + Factory + Null Object Calculator Pattern

One API for the client: Calculator::Factory.for(entity).calculate. The factory picks the strategy; NullService handles unknown variants safely.

HARD-GATE: Tests Gate Implementation

For each component (Factory → BaseService → NullService → Concrete):

  1. Write the spec — contexts per variant, plus the NullService path
  2. Run it — verify it fails because the component does not exist yet
  3. Implement the component — minimum code to make the spec pass
  4. Run again — confirm green, then proceed to the next component

Quick Reference

ComponentResponsibility
FactoryDispatch to correct service class via SERVICE_MAP; fall back to NullService
BaseServiceGuard with should_calculate?; delegate to compute_result
NullServiceAlways returns nil safely — never raises
ConcreteOverride should_calculate? (add variant check on top of super) and compute_result

File Structure

app/services/<calculator_name>/
├── factory.rb
├── base_service.rb
├── null_service.rb
├── standard_service.rb
├── premium_service.rb

1. Factory

# frozen_string_literal: true

module PricingCalculator
  class Factory
    SERVICE_MAP = {
      'standard' => StandardPricingService,
      'premium'  => PremiumPricingService
    }.freeze

    def self.for(order)
      plan = order.plan
      return NullService.new(order) unless plan&.active?

      service_class = SERVICE_MAP[plan.name] || NullService
      service_class.new(order)
    end
  end
end

No qualifying context or unknown variant → NullService. For full BaseService and NullService implementations, see IMPLEMENTATION.md.

2. Usage

price = PricingCalculator::Factory.for(order).calculate

Single entry point rule: Factory.for(entity) is the only permitted access path. Clients never instantiate service classes directly. If you see StandardPricingService.new(order) outside of Factory, that is a bug — route through the factory.

3. Tests (RSpec)

Factory dispatch (all branches):

RSpec.describe PricingCalculator::Factory do
  describe '.for' do
    it 'returns NullService when plan is nil' do
      order = create(:order, plan: nil)
      expect(described_class.for(order)).to be_a(PricingCalculator::NullService)
    end

    it 'returns StandardPricingService for standard plan' do
      order = create(:order, plan: create(:plan, name: 'standard', active: true))
      expect(described_class.for(order)).to be_a(PricingCalculator::StandardPricingService)
    end
  end
end

Cover inactive plan, each variant, and unknown variant. See TESTING.md for NullService and concrete service specs.

Pitfalls

PitfallFix
SERVICE_MAP key mismatchVerify keys match exactly what is stored in the database — typos cause silent NullService fallbacks
Missing NullService specAlways add a spec context for unknown/nil variants or tests will never catch the fallback regression
Direct service instantiation (ServiceClass.new(entity))Route through Factory.for(entity) — it is the sole public entry point; direct instantiation bypasses the NullService safety net

Integration

SkillWhen to chain
rspec-service-testingFor complete Factory, BaseService, NullService, and concrete strategy specs
ruby-service-objectsFor naming conventions, YARD docs, and frozen_string_literal baseline

Assets

skills

README.md

tile.json