CtrlK
BlogDocsLog inGet started
Tessl Logo

igmarin/rails-agent-skills

Curated library of AI agent skills for Ruby on Rails development. Covers code review, architecture, security, testing (RSpec), engines, service objects, DDD patterns, and workflow automation.

98

1.38x
Quality

99%

Does it follow best practices?

Impact

98%

1.38x

Average score across 26 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

SKILL.mdstrategy-factory-null-calculator/

name:
strategy-factory-null-calculator
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

strategy-factory-null-calculator

README.md

tile.json