Skip to content

Phase 1: messengers, pipeline, providers, retries, test helpers - #1

Merged
timimsms merged 3 commits into
mainfrom
phase-1-core
Oct 16, 2026
Merged

timimsms merged 3 commits into
mainfrom
phase-1-core

Conversation

@timimsms

@timimsms timimsms commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Implements Phase 1 of docs/SPEC.md.

What's included

  • Messengers. Smswire::Base on AbstractController, with default, with(params), .text.erb templates, layouts, I18n lazy lookup, URL helpers, callbacks, named senders, and recipient resolution.
  • Message. Mutable value object with GSM-7 / UCS-2 detection and greedy segment packing, so extension characters and surrogate pairs are never split.
  • Pipeline. E.164 normalization, empty-body rejection, interceptors, provider delivery, observers, and deliver.smswire / reject.smswire notifications. Bodies are never instrumented.
  • Providers. Twilio over REST with no SDK, plus log, test, null, and a registry for custom adapters. Twilio error codes map to permanent reasons such as :invalid_number and :opted_out. 429 responses honor Retry-After.
  • Delivery. deliver_now returns Smswire::Result. deliver_later goes through Smswire::DeliveryJob, which retries transient errors with polynomial backoff up to retry_attempts.
  • Testing. Smswire::TestHelper for Minitest and deliver_sms / have_enqueued_sms matchers in smswire/rspec.

Deviations from the spec, now reflected in it

  • Runtime dependency is actionpack rather than actionview, because rendering uses AbstractController like Action Mailer. activerecord arrives in Phase 2.
  • Default body whitespace mode is :strip rather than squish, since SMS bodies often need line breaks.
  • Two outcomes added: :rejected_empty_body and :skipped.
  • retry_attempts replaces the retry hash.

Verification

  • 69 tests, 202 assertions, all passing locally on Rails 7.1.6, 7.2.4, and 8.1.4 with Ruby 3.3.
  • The Phase 1 acceptance test renders a dummy-app template and sends it through Twilio against WebMock.
  • Rails 7.1 needs json < 3 in the test Gemfile, because ActiveSupport 7.1's to_json passes an option json 3.0 removed. Library code only parses JSON.

🤖 Generated with Claude Code

https://claude.ai/code/session_01L5SuBiSMN4r9qr5CXbKkHV

timimsms and others added 3 commits October 8, 2026 11:00
…pers

Messenger classes on AbstractController with .text.erb templates,
layouts, I18n, and URL helpers. A Message value with GSM-7/UCS-2
segment counting. A pipeline that normalizes to E.164, rejects empty
bodies, runs interceptors, delivers, and notifies observers and
ActiveSupport::Notifications without ever including the body.

Providers: Twilio over REST with error-code mapping to permanent
reasons and Retry-After handling, plus log, test, and null.
deliver_now returns Smswire::Result; deliver_later goes through
Smswire::DeliveryJob with polynomial backoff for transient errors.

Minitest TestHelper and RSpec matchers, a dummy Rails app, and a CI
matrix over Ruby 3.2-3.4 and Rails 7.1-8.1. Spec, README, and
changelog updated to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5SuBiSMN4r9qr5CXbKkHV
Pin json below 3 when testing Rails 8.0, which shares the 7.1
incompatibility. Replace numbered block parameters with named ones so
Standard on Ruby 3.4 passes without requiring the 3.4-only `it`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5SuBiSMN4r9qr5CXbKkHV
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant