From 35cfa8bb91c4e4f3535321b21acf5865df5c9c62 Mon Sep 17 00:00:00 2001 From: Bryan Rite Date: Tue, 17 Mar 2026 11:52:51 -0700 Subject: [PATCH 1/3] First pass at pre-release docs. --- AI_README.md | 320 ++++++++++++++++++++++ CHANGELOG.md | 9 + README.md | 629 ++++++++++++++++++++++++++++++++++++++++++-- logo.png | Bin 0 -> 128090 bytes operational.gemspec | 7 + 5 files changed, 948 insertions(+), 17 deletions(-) create mode 100644 AI_README.md create mode 100644 CHANGELOG.md create mode 100644 logo.png diff --git a/AI_README.md b/AI_README.md new file mode 100644 index 0000000..8eb26f4 --- /dev/null +++ b/AI_README.md @@ -0,0 +1,320 @@ +# Operational — AI Agent Reference + +This document is for AI coding agents. It describes the exact API of the `operational` gem so you can generate correct code without guessing. + +## Architecture + +Operational has four components: + +1. **Operation** — orchestrates a business process as a railway of steps +2. **Form** — validates and transforms user input, decoupled from models (built on ActiveModel) +3. **Contract** — step helpers that wire forms into operations (Build → Validate → Sync) +4. **Controller** — Rails mixin that runs operations from controller actions + +## Operations + +Subclass `Operational::Operation`. Define steps with `step`, `pass`, or `fail` at the class level. Call with `.call(state_hash)`. Returns an `Operational::Result`. + +```ruby +class CreateArticleOperation < Operational::Operation + step :build + step Contract::Build(contract: ArticleForm, model_key: :article) + step Contract::Validate() + step Contract::Sync(model_key: :article) + step :save + pass :notify # return value ignored, never derails + fail :handle # only runs on failure track + + def build(state) + state[:article] = Article.new + # must return truthy to continue, falsy switches to failure track + end + + def save(state) + state[:article].save # returns true/false naturally + end + + def notify(state) + # side effect, return value doesn't matter + end + + def handle(state) + # runs on failure track + # return truthy to recover back to success track + # return falsy to stay on failure track + false + end +end +``` + +### Step types + +| Type | Runs when | Truthy return | Falsy return | +|--------|------------------|-----------------------|-------------------------| +| `step` | On success track | Continue success | Switch to failure track | +| `fail` | On failure track | Recover to success | Continue failure | +| `pass` | On success track | Continue success | Continue success | + +### Step actions + +A step action can be: +- **Symbol** — calls instance method with `(state)` argument +- **Lambda/Proc** — called with `(state)` argument +- **Any object responding to `.call`** — called with `(state)` argument + +### State + +State is a plain Ruby hash passed to `.call`. It is mutable — steps read from and write to it. The result's state is a frozen duplicate. + +```ruby +result = MyOperation.call(user: user, params: params_hash) +``` + +### Result + +```ruby +result.succeeded? # => true/false +result.failed? # => true/false +result.state # => frozen hash +result[:key] # => shorthand for result.state[:key] +result.operation # => the operation instance +``` + +### Nested operations + +Use `Nested::Operation` to call one operation from within another. State is merged back. The nested result's `succeeded?` determines if the parent continues on success or failure track. + +```ruby +class CreateArticleOperation < Operational::Operation + class Present < Operational::Operation + step :init + step Contract::Build(contract: ArticleForm, model_key: :article) + + def init(state) + state[:article] = Article.new + end + end + + step Nested::Operation(operation: Present) + step Contract::Validate() + step Contract::Sync(model_key: :article) + step :save + + def save(state) + state[:article].save + end +end +``` + +## Forms + +Subclass `Operational::Form`. Uses `ActiveModel::Model`, `ActiveModel::Attributes`, `ActiveModel::Dirty`. + +```ruby +class ArticleForm < Operational::Form + attribute :title, :string + attribute :body, :string + + validates :title, presence: true + validates :body, presence: true +end +``` + +### Form.build + +```ruby +Form.build( + model: nil, # ActiveModel instance — copies matching attributes to form + model_persisted: nil, # override persisted? detection (true/false/nil) + state: {}, # context hash, available as @state in the form + prepopulate_method: :prepopulate # method to call after build +) +``` + +- Only attributes defined on the form are copied from the model (nil values are skipped) +- State is frozen and stored as `@state` +- If the form defines `prepopulate(state)`, it is called after attribute assignment +- `changes_applied` is called after build so dirty tracking starts clean + +### Form.validate + +```ruby +form.validate(params_hash) # => true/false +``` + +- Converts `ActionController::Parameters` automatically via `to_unsafe_h` +- Only assigns params matching defined attributes (ignores unknown keys) +- Calls `valid?` and returns the result + +### Form.sync + +```ruby +form.sync( + model: nil, # ActiveModel instance — copies matching attributes back + state: {}, # passed to on_sync + sync_method: :on_sync # custom hook method name +) +``` + +- Copies form attributes to model where attribute names match +- Calls `on_sync(state)` if defined on the form +- Always returns `true` + +### Important: do NOT define `#sync` on a form subclass + +Defining `#sync` raises `MethodCollision`. Use `#on_sync` instead — it is called automatically during sync. + +### Helper methods + +- `persisted?` — returns whether the model was persisted at build time +- `other_validators_have_passed?` — returns `errors.blank?`, useful for conditional validators +- `@state` — access the frozen state hash passed at build time + +## Contract step helpers + +These are used inside operations as step actions. They return lambdas. + +### Contract::Build + +```ruby +step Contract::Build( + contract: MyForm, # required — the form class + name: :contract, # state key to store the form instance + model_key: nil, # state key containing the model to prepopulate from + model_persisted: nil, # override persisted? detection + prepopulate_method: :prepopulate +) +``` + +Always returns `true`. + +### Contract::Validate + +```ruby +step Contract::Validate( + name: :contract, # state key where the form is stored + params_path: nil # nil → state[:params] + # :symbol → state[:params][:symbol] + # [:a, :b] → state.dig(:a, :b) +) +``` + +Returns the result of `form.validate(params)` — `true`/`false`. + +### Contract::Sync + +```ruby +step Contract::Sync( + name: :contract, # state key where the form is stored + model_key: nil, # state key containing the model to sync to + sync_method: :on_sync # custom sync hook method name +) +``` + +Returns `true` (from `form.sync`). + +## Controller mixin + +```ruby +class MyController < ApplicationController + include Operational::Controller + + def create + if run CreateArticleOperation + redirect_to @state[:article] + else + render :new, status: :unprocessable_entity + end + end +end +``` + +### run(operation, **extras) + +- Merges `extras` with default state (`params` and `current_user` if available) +- Calls `operation.call(state)` +- Sets `@state` to the frozen result state +- Returns `result.succeeded?` + +### Overridable methods + +- `_operational_default_state` — override to inject custom default state +- `_operational_state_variable` — override to change the instance variable name (default: `@state`) + +## Errors + +| Error class | Raised when | +|---|---| +| `Operational::InvalidContractModel` | Model doesn't respond to `attributes` | +| `Operational::UnknownStepType` | Step action is not a Symbol or callable | +| `Operational::MethodCollision` | Form subclass defines `#sync` instead of `#on_sync` | + +## File structure convention + +``` +app/concepts// + _form.rb + _operation.rb +``` + +Example: `app/concepts/article/article_form.rb`, `app/concepts/article/create_article_operation.rb` + +## Common patterns + +### New/Create with nested Present + +```ruby +class CreateThingOperation < Operational::Operation + class Present < Operational::Operation + step :init + step Contract::Build(contract: ThingForm, model_key: :thing) + + def init(state) + state[:thing] = Thing.new + end + end + + step Nested::Operation(operation: Present) + step Contract::Validate() + step Contract::Sync(model_key: :thing) + pass :persist + + def persist(state) + state[:thing].save! + end +end +``` + +Controller uses `CreateThingOperation::Present` for `new` and `CreateThingOperation` for `create`. + +### Multi-model form + +```ruby +class OrderForm < Operational::Form + attribute :item_name, :string + attribute :shipping_address, :string + + def prepopulate(state) + self.shipping_address = state[:user]&.default_address + end + + def on_sync(state) + state[:shipping].update!(address: shipping_address) + end +end +``` + +### State-dependent validation + +```ruby +class ArticleForm < Operational::Form + attribute :published, :boolean + validate :admin_only_publish + + def admin_only_publish + if published && !@state[:current_user]&.admin? + errors.add(:published, "requires admin privileges") + end + end +end +``` diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ce2c0da --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). + +## [Unreleased] + +- Initial release diff --git a/README.md b/README.md index f0a1e9d..0b42d0a 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,643 @@ -# Operational +

+ + Operational +

-Applications usually start out simple, actions are largely CRUD-y and isolated to a single database backed model. As they grow and complicated business logic creeps in, something to orchestrate the action helps to keep concerns separated and decouple business logic from data persistence. +

+ Lightweight, railway-oriented operation and form objects for buisness logic. +

-This is what **Operational** attempts to solve. +[![Gem Version](https://img.shields.io/gem/v/operational.svg)](https://rubygems.org/gems/operational) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) -Operational introduces a number of concepts to solve these problems while relying on Ruby on Rails' ActiveModel to keep the code **small** and **dependency free**. This enables powerful organization of code, high readability, and a DSL like collection of business actions with a very light touch. +Operational wraps your business logic into **operations** — small classes with a railway of steps that succeed or fail. Pair them with **Operational Forms** to decouple your UI and APIs from your models and **Contracts** to wire it all together. -This gem is heavily inspired by [Trailblazer](https://github.com/trailblazer/trailblazer) and [dry-rb](https://dry-rb.org/), both of which I have used extensively for many years. Operational solves a similar but much small subset of problems and relies on ActiveModel conventions, rather than being framework agnostic; meaning there is far less code, moving parts, and no dependencies. +One dependency: `activemodel`. If you've used Rails, you already know how Operational works. Use it to simplify and clean up complex business logic into isolated, easily testable classes. -Read more about Operational's motivations here: [https://bryanrite.com/simplifying-complex-rails-apps-with-operations/](https://bryanrite.com/simplifying-complex-rails-apps-with-operations/) +> [!NOTE] +> **AI agents:** See [AI_README.md](AI_README.md) for a concise API reference optimized for code generation. + +## Table of Contents + +- [Quick Example](#quick-example) +- [Installation](#installation) +- [Why You Need Operational](#why-you-need-operational) +- [Core Concepts](#core-concepts) + - [Operations](#operations) + - [Forms](#forms) + - [Contracts](#contracts) + - [Composing Operations](#composing-operations) +- [Rails Integration](#rails-integration) +- [Project Structure](#project-structure) +- [Full Example](#full-example) +- [Testing](#testing) +- [Requirements](#requirements) +- [Contributing](#contributing) +- [License](#license) + +## Quick Example + +```ruby +# A form object — validates input without touching your model +class SignupForm < Operational::Form + attribute :name, :string + attribute :email, :string + + validates :name, presence: true + validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP } +end + +# An operation — wires together validation, persistence, and side effects with railway functional programming. +class RegisterUserOperation < Operational::Operation + step :build_user + step Contract::Build(contract: SignupForm, model_key: :user) + step Contract::Validate() + step Contract::Sync(model_key: :user) + step :save + pass :send_welcome + fail :log_failure + + def build_user(state) + state[:user] = User.new(role: :member) + end + + def save(state) + state[:user].save + end + + def send_welcome(state) + WelcomeMailer.welcome(state[:user]).deliver_later + end + + def log_failure(state) + Rails.logger.warn("Signup failed: #{state[:contract].errors.full_messages}") + false + end +end +``` + +```ruby +# In your controller — two lines +if run RegisterUserOperation + redirect_to dashboard_path, notice: "Welcome #{@state[:user].name}!" +else + render :new, status: :unprocessable_entity +end +``` ## Installation -Add this line to your application's Gemfile: +Add to your Gemfile: ```ruby gem 'operational' ``` -And then execute: +Then run `bundle install`. + +## Why You Need Operational + +Rails apps start simple — a model, a controller, some validations. Then the business logic creeps in. "Register a user" isn't just `User.create` anymore — it's validate the input, assign a role, send a welcome email, and notify the sales team. That logic ends up scattered across callbacks, controller actions, and service objects that everyone has to remember to call in the right order. + +Operational gives you a place for all of that. Each operation describes a business process as a readable sequence of steps that anyone on the team can follow — no digging through models and callbacks to understand what happens. + +**Operational can help when:** + +- UI and APIs are touching multiple models (`accepts_nested_attributes_for`) +- Model validations need to change by outside context (e.g., only admins can publish) +- Model callbacks are doing too much (`after_create`, `after_save`, etc.) +- Business processes are duplicated between controllers, jobs, and scripts +- Strong parameters are getting complex with deeply nested or context-dependent permits +- Testing business logic requires full controller/request specs instead of simple unit tests + +## Core Concepts + +### Operations + +An operation is a class that defines a sequence of steps executed in order. Each step either succeeds (returns truthy) or fails (returns falsy), controlling the flow through the railway. + +**Operations orchestrate, they don't implement.** Keep your steps thin — they should try to delegate to plain Ruby objects, service classes, and model methods. An operation's job is to define the order things happen and what to do when something fails, in other words, _orchestrate the business process but not to contain the business logic itself_. If a step is getting long, extract the work into a PORO and call it from the step. + +#### Defining Steps + +Steps can be **symbols** (instance methods), **lambdas**, or any **callable** object: + +```ruby +class ProcessOrderOperation < Operational::Operation + step :validate_inventory # instance method + step ->(state) { ... } # lambda + step Policies::OrderPolicy() # callable object +end +``` + +Every step receives a `state` hash and returns a truthy or falsy value. + +#### Running an Operation + +Call `.call` on the operation with an optional initial state hash. You get back a `Result`: + +```ruby +result = ProcessOrderOperation.call(order: order, current_user: user) + +result.succeeded? # => true / false +result.failed? # => true / false +result.state # => the full state hash (frozen) +result[:order] # => shorthand for result.state[:order] +result.operation # => the operation instance +``` + +There is intentionally one entry point (`.call`) and one result type — no `.call!` or bang variants. Check `succeeded?` and branch accordingly. + +#### The Railway: step, pass, fail + +Operations follow a **railway pattern** with two tracks — success and failure: + +- **`step`** — Runs on the success track. If it returns falsy, execution switches to the failure track. +- **`fail`** — Runs on the failure track only. If it returns truthy, execution switches back to the success track (recovery). +- **`pass`** — Always runs on the success track and always continues on the success track, regardless of return value. Useful for side effects. + +```ruby +class PlaceOrderOperation < Operational::Operation + step :validate_cart # success track — runs first + step :charge_card # if this returns false → switches to failure track + step :send_confirmation # SKIPPED if charge_card failed + fail :notify_support # failure track — only runs after a failure + fail :refund # continues on failure track +end +``` + +**Recovery:** If a `fail` step returns truthy, execution moves back to the success track. This lets you handle errors and continue. - $ bundle +**`pass` for side effects:** A `pass` step always continues on the success track regardless of its return value — useful for logging, analytics, or other fire-and-forget work: -Or install it yourself as: +```ruby +class PublishArticleOperation < Operational::Operation + step :publish + pass :track_analytics # return value ignored — never derails the operation + step :notify_subscribers # always runs after pass +end +``` - $ gem install operational +#### A Realistic Example +```ruby +class ChargeOrderOperation < Operational::Operation + step :find_order + step :charge_payment + pass :track_analytics + step :send_confirmation + fail :refund -## Getting Started + def find_order(state) + state[:order] = Order.find_by(id: state[:params][:id]) + state[:order].present? + end -See the [Operational Wiki](https://github.com/bryanrite/operational/wiki) for an explanation of the concepts introduced by Operational and show how to start using them in your own applications. + def charge_payment(state) + state[:charge] = PaymentGateway.charge(state[:order].total) + state[:charge].success? + end + def track_analytics(state) + Analytics.track("order.charged", order_id: state[:order].id) + # return value doesn't matter — pass always continues + end -## RDocs + def send_confirmation(state) + OrderMailer.confirmation(state[:order]).deliver_later + true + end -To come. + def refund(state) + PaymentGateway.refund(state[:charge]) if state[:charge] + false + end +end +``` +### Forms + +Forms decouple input validation from your models. They allow you to build UI and APIs that aren't coupled to your database modeling and allow you define exactly what you're willing to accept. + +They're built on `ActiveModel::Model`, `ActiveModel::Attributes`, and `ActiveModel::Dirty` — so you already know the API. + +> [!TIP] +> Already familiar with form objects? Skip ahead to [Contracts](#contracts) to see how forms wire into operations. + +#### Defining a Form + +```ruby +class ArticleForm < Operational::Form + attribute :title, :string + attribute :body, :string + attribute :published, :boolean, default: false + + validates :title, presence: true, length: { maximum: 200 } + validates :body, presence: true +end +``` + +#### Building, Validating, and Syncing + +The basic lifecycle of a form is **build → validate → sync**. For single-model forms, this is straightforward — pass a model to `.build` and attributes defined in your form matching attributes in the model are automatically copied in both directions: + +```ruby +# Build — pre-populates form from the model's matching attributes +article = Article.find(params[:id]) +form = ArticleForm.build(model: article) +form.title # => article.title (auto-copied) +form.persisted? # => true (detected from model) + +# Validate — assigns params, runs validations, returns true/false +form.validate(title: "Updated", body: "New content") # => true +form.validate(title: "") # => false +form.errors.full_messages # => ["Title can't be blank"] + +# Sync — writes matching attributes back to the model +form.sync(model: article) +article.title # => "Updated" +``` + +Any params that don't match a defined form attribute are silently ignored — no need for `strong_parameters`. `ActionController::Parameters` are handled automatically. + +> [!NOTE] +> Inside an operation, [`Contract` helpers](#contracts) handle this entire lifecycle as steps — you won't call these methods directly. + +You can also pass **state** to `.build`, which is separate from the form's attributes — it's not user input, it's context. State is available as `@state` and is useful for conditional validation (e.g., only admins can publish) and prepopulating defaults from things the user doesn't control: + +```ruby +form = ArticleForm.build(model: article, state: { current_user: current_user, team: team }) +``` + +#### Multi-Model Forms: Prepopulate and Sync Hooks + +For simple single-model forms, the automatic attribute matching handles everything. For more complex cases — where a single form spans multiple models — you can define `prepopulate` and `on_sync` hooks to control how data flows in and out: + +```ruby +class NewArticleForm < Operational::Form + attribute :title, :string + attribute :body, :string + attribute :author_bio, :string + attribute :default_category, :string + + # Pull data IN from multiple sources when the form is built + def prepopulate(state) + self.author_bio = state[:current_user]&.bio + self.default_category = state[:team]&.default_category + end + + # Push data OUT to multiple models when the form is synced + def on_sync(state) + state[:author].update!(bio: author_bio) if author_bio_changed? + end +end + +# Build pulls from article (automatic) + current_user + team (via prepopulate) +form = NewArticleForm.build(model: article, state: { current_user: user, team: team, author: user }) + +# Sync writes to article (automatic) + author (via on_sync) +form.sync(model: article, state: { article: article, author: user }) +``` + +#### Dirty Tracking + +Forms support ActiveModel dirty tracking out of the box: + +```ruby +form = ArticleForm.build(model: article) +form.changed? # => false (clean after build) + +form.title = "New" +form.changed? # => true +form.title_changed? # => true +form.title_was # => "Original Title" +``` + +#### State-Dependent Validators + +Access operation state inside custom validators via `@state`: + +```ruby +class ArticleForm < Operational::Form + attribute :title, :string + validate :must_be_admin + + def must_be_admin + errors.add(:base, "Not authorized") unless @state[:current_user]&.admin? + end +end +``` + +### Contracts + +Contract helpers wire forms into operations as steps. This is where Operations and Forms come together. + +#### Contract.Build + +Creates a form instance and stores it in the state: + +```ruby +step Contract::Build(contract: ArticleForm) +# state[:contract] is now an ArticleForm instance + +# With a model for pre-population: +step Contract::Build(contract: ArticleForm, model_key: :article) +``` + +Options: +- `contract:` — the form class (required) +- `name:` — state key to store the form (default: `:contract`) +- `model_key:` — state key containing the model to pre-populate from +- `model_persisted:` — override `persisted?` detection +- `prepopulate_method:` — method to call for prepopulation (default: `:prepopulate`) + +#### Contract.Validate + +Validates the form using params from the state: + +```ruby +step Contract::Validate() +# Validates state[:contract] with state[:params] + +# With nested params: +step Contract::Validate(params_path: :article) +# Validates with state[:params][:article] + +# With a custom path: +step Contract::Validate(params_path: [:custom, :path]) +# Validates with state[:custom][:path] +``` + +Options: +- `name:` — state key where the form is stored (default: `:contract`) +- `params_path:` — `nil` for `state[:params]`, a symbol for `state[:params][symbol]`, or an array for a custom dig path + +Returns `true` if validation passes, `false` otherwise — making it a natural railway step. + +#### Contract.Sync + +Syncs form data back to a model: + +```ruby +step Contract::Sync(model_key: :article) +``` + +Options: +- `name:` — state key where the form is stored (default: `:contract`) +- `model_key:` — state key containing the model to sync to +- `sync_method:` — custom sync hook method name (default: `:on_sync`) + +#### Full Worked Example + +```ruby +# app/forms/article_form.rb +class ArticleForm < Operational::Form + attribute :title, :string + attribute :body, :string + + validates :title, presence: true + validates :body, presence: true + + def on_sync(state) + state[:article].published_at = Time.current if state[:publish] + end +end + +# app/operations/create_article_operation.rb +class CreateArticleOperation < Operational::Operation + step :build_article + step Contract::Build(contract: ArticleForm, model_key: :article) + step Contract::Validate() + step Contract::Sync(model_key: :article) + step :save + + def build_article(state) + state[:article] = Article.new + end + + def save(state) + state[:article].save + end +end + +# Usage +result = CreateArticleOperation.call(params: { title: "Hello", body: "World" }) +result.succeeded? # => true +result[:article] # => #
+``` + +### Composing Operations + +Just like Rails controllers pair `new`/`create` and `edit`/`update`, operations often share setup logic between actions. `Nested::Operation` lets you extract the common part — building the model, setting up the form — into a reusable operation that gets nested inside the action-specific ones: + +```ruby +class CreateArticleOperation < Operational::Operation + # The "new" part — builds the model and sets up the form + class Present < Operational::Operation + step :build_article + step Contract::Build(contract: ArticleForm, model_key: :article) + + def build_article(state) + state[:article] = Article.new(author: state[:current_user]) + end + end + + # The "create" part — nests Present, then validates, syncs, and persists + step Nested::Operation(operation: Present) + step Contract::Validate() + step Contract::Sync(model_key: :article) + pass :persist + + def persist(state) + ActiveRecord::Base.transaction do + state[:article].save! + end + end +end +``` + +The `CreateArticleOperation::Present` operation can be used on the `new` controller action and the `CreateArticleOperation` can be used on the `create` controller action, without duplicating the setup needed to present the form and save it... something often done by extracting helpers in the controller. + +### Rails Integration + +Include `Operational::Controller` in your controllers to get the `run` helper: + +```ruby +class ArticlesController < ApplicationController + include Operational::Controller + + def create + if run CreateArticleOperation + redirect_to @state[:article], notice: "Article created!" + else + render :new, status: :unprocessable_entity + end + end +end +``` + +`run` automatically injects `params` and `current_user` (if available) into the operation state, and exposes the result state as `@state`. + +You can pass additional state: + +```ruby +run CreateArticleOperation, publish: true, category: @category +``` + +Override `_operational_default_state` to customize what gets injected: + +```ruby +class ApplicationController < ActionController::Base + include Operational::Controller + + protected + + def _operational_default_state + super.merge(admin: current_user&.admin?) + end +end +``` + +## Project Structure + +We recommend organizing operations and forms under `app/concepts/`, grouped by the domain concept they belong to: + +``` +app/ + concepts/ + article/ + article_form.rb + create_article_operation.rb + publish_article_operation.rb + registration/ + signup_form.rb + register_user_operation.rb + controllers/ + articles_controller.rb + registrations_controller.rb + models/ + article.rb + user.rb +``` + +This keeps related operations and forms together — everything about articles lives in `app/concepts/article/`. Rails autoloading picks them up automatically — no configuration needed. + +## Full Example + +Here's a complete `new`/`create` flow — form, operation, and controller working together: + +```ruby +# app/concepts/article/article_form.rb +class ArticleForm < Operational::Form + attribute :title, :string + attribute :body, :string + + validates :title, presence: true, length: { maximum: 200 } + validates :body, presence: true +end + +# app/concepts/article/create_article_operation.rb +class CreateArticleOperation < Operational::Operation + # The "new" part — reusable for the new action + class Present < Operational::Operation + step :init + step Contract::Build(contract: ArticleForm, model_key: :article) + + def init(state) + state[:article] = Article.new(author: state[:current_user]) + end + end + + # The "create" part + step Nested::Operation(operation: Present) + step Contract::Validate() + step Contract::Sync(model_key: :article) + pass :persist + + def persist(state) + state[:article].save! + end +end + +# app/controllers/articles_controller.rb +class ArticlesController < ApplicationController + include Operational::Controller + + def new + run CreateArticleOperation::Present + end + + def create + if run CreateArticleOperation + redirect_to @state[:article], notice: "Article created!" + else + render :new, status: :unprocessable_entity + end + end +end +``` + +The `new` action runs just `Present` to build an empty form. The `create` action nests `Present` then adds validation, syncing, and persistence. The controller only handles HTTP routing — all business logic lives in the operation. + +## Testing + +### Testing Operations + +```ruby +RSpec.describe CreateArticleOperation do + it "creates an article with valid params" do + result = CreateArticleOperation.call( + params: { title: "Test", body: "Content" }, + current_user: create(:user) + ) + + expect(result).to be_succeeded + expect(result[:article]).to be_persisted + end + + it "fails with invalid params" do + result = CreateArticleOperation.call( + params: { title: "" }, + current_user: create(:user) + ) + + expect(result).to be_failed + expect(result[:contract].errors[:title]).to include("can't be blank") + end +end +``` + +### Testing Forms + +```ruby +class ArticleFormTest < Minitest::Test + def test_validates_presence_of_title + form = ArticleForm.build + form.validate(title: "", body: "Content") + + assert_includes form.errors[:title], "can't be blank" + end + + def test_syncs_attributes_to_the_model + article = Article.new + form = ArticleForm.build(model: article) + form.validate(title: "Updated", body: "New content") + form.sync(model: article) + + assert_equal "Updated", article.title + end +end +``` + +## Requirements + +- Ruby >= 3.0 +- ActiveModel >= 7.0 ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/bryanrite/operational. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. - ## License The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). - diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..455ebd236cd698168ecc3cc10f1296201ba003de GIT binary patch literal 128090 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

xt-M(oFjc!oM3kb4TzN^zwsfA9)!mbx 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 tZ|Dy05fKpX?~|m}{SgolP# "https://github.com/bryanrite/operational/blob/master/CHANGELOG.md", + "source_code_uri" => "https://github.com/bryanrite/operational", + "documentation_uri" => "https://github.com/bryanrite/operational#readme", + "bug_tracker_uri" => "https://github.com/bryanrite/operational/issues" + } + spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end From 08689baa05c5514d02114dd06b9f838cc16a0f1c Mon Sep 17 00:00:00 2001 From: Bryan Rite Date: Tue, 17 Mar 2026 14:30:07 -0700 Subject: [PATCH 2/3] Fill in more documentation. --- AI_README.md | 16 ++++---- README.md | 96 +++++++++++++++++++++++++-------------------- operational.gemspec | 10 ++--- 3 files changed, 64 insertions(+), 58 deletions(-) diff --git a/AI_README.md b/AI_README.md index 8eb26f4..2e96fcc 100644 --- a/AI_README.md +++ b/AI_README.md @@ -98,10 +98,10 @@ class CreateArticleOperation < Operational::Operation step Nested::Operation(operation: Present) step Contract::Validate() step Contract::Sync(model_key: :article) - step :save + pass :persist - def save(state) - state[:article].save + def persist(state) + state[:article].save! end end ``` @@ -127,13 +127,13 @@ Form.build( model: nil, # ActiveModel instance — copies matching attributes to form model_persisted: nil, # override persisted? detection (true/false/nil) state: {}, # context hash, available as @state in the form - prepopulate_method: :prepopulate # method to call after build + build_method: :on_build # method to call during build ) ``` - Only attributes defined on the form are copied from the model (nil values are skipped) - State is frozen and stored as `@state` -- If the form defines `prepopulate(state)`, it is called after attribute assignment +- If the form defines `on_build(state)`, it is called during build after attribute assignment - `changes_applied` is called after build so dirty tracking starts clean ### Form.validate @@ -180,9 +180,9 @@ These are used inside operations as step actions. They return lambdas. step Contract::Build( contract: MyForm, # required — the form class name: :contract, # state key to store the form instance - model_key: nil, # state key containing the model to prepopulate from + model_key: nil, # state key containing the model to build from model_persisted: nil, # override persisted? detection - prepopulate_method: :prepopulate + build_method: :on_build ) ``` @@ -294,7 +294,7 @@ class OrderForm < Operational::Form attribute :item_name, :string attribute :shipping_address, :string - def prepopulate(state) + def on_build(state) self.shipping_address = state[:user]&.default_address end diff --git a/README.md b/README.md index 0b42d0a..3724c46 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,20 @@

- Lightweight, railway-oriented operation and form objects for buisness logic. + Lightweight, railway-oriented operation and form objects for business logic.

-[![Gem Version](https://img.shields.io/gem/v/operational.svg)](https://rubygems.org/gems/operational) -[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) +Operational wraps your business logic into **Operations** — small classes with a railway of steps that succeed or fail. Pair them with **Forms** to decouple your UI and APIs from your models and **Contracts** to wire it all together. -Operational wraps your business logic into **operations** — small classes with a railway of steps that succeed or fail. Pair them with **Operational Forms** to decouple your UI and APIs from your models and **Contracts** to wire it all together. - -One dependency: `activemodel`. If you've used Rails, you already know how Operational works. Use it to simplify and clean up complex business logic into isolated, easily testable classes. +One dependency: `activemodel`. ~200 lines of plain ruby code. It's not a framework — it's a pattern. You probably already know how Operational works. > [!NOTE] > **AI agents:** See [AI_README.md](AI_README.md) for a concise API reference optimized for code generation. +[![Gem Version](https://img.shields.io/gem/v/operational.svg)](https://rubygems.org/gems/operational) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) + + ## Table of Contents - [Quick Example](#quick-example) @@ -38,7 +39,7 @@ One dependency: `activemodel`. If you've used Rails, you already know how Operat ## Quick Example ```ruby -# A form object — validates input without touching your model +# A form object — validates input without being linked to a specific model. class SignupForm < Operational::Form attribute :name, :string attribute :email, :string @@ -47,37 +48,31 @@ class SignupForm < Operational::Form validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP } end -# An operation — wires together validation, persistence, and side effects with railway functional programming. +# An operation — wires together validation, persistence, and business process with railway functional programming. class RegisterUserOperation < Operational::Operation - step :build_user + step :setup_user step Contract::Build(contract: SignupForm, model_key: :user) step Contract::Validate() step Contract::Sync(model_key: :user) - step :save + step :persist pass :send_welcome - fail :log_failure - def build_user(state) + def setup_user(state) state[:user] = User.new(role: :member) end - def save(state) + def persist(state) state[:user].save end def send_welcome(state) WelcomeMailer.welcome(state[:user]).deliver_later end - - def log_failure(state) - Rails.logger.warn("Signup failed: #{state[:contract].errors.full_messages}") - false - end end ``` ```ruby -# In your controller — two lines +# In your controller — simple boolean branching. if run RegisterUserOperation redirect_to dashboard_path, notice: "Welcome #{@state[:user].name}!" else @@ -103,7 +98,7 @@ Operational gives you a place for all of that. Each operation describes a busine **Operational can help when:** -- UI and APIs are touching multiple models (`accepts_nested_attributes_for`) +- UI and API requests are touching multiple models (`accepts_nested_attributes_for`) - Model validations need to change by outside context (e.g., only admins can publish) - Model callbacks are doing too much (`after_create`, `after_save`, etc.) - Business processes are duplicated between controllers, jobs, and scripts @@ -116,7 +111,7 @@ Operational gives you a place for all of that. Each operation describes a busine An operation is a class that defines a sequence of steps executed in order. Each step either succeeds (returns truthy) or fails (returns falsy), controlling the flow through the railway. -**Operations orchestrate, they don't implement.** Keep your steps thin — they should try to delegate to plain Ruby objects, service classes, and model methods. An operation's job is to define the order things happen and what to do when something fails, in other words, _orchestrate the business process but not to contain the business logic itself_. If a step is getting long, extract the work into a PORO and call it from the step. +**Operations orchestrate, they don't implement.** Keep your steps thin — they should try to delegate to plain Ruby objects, service classes, and model methods. An operation's job is to define the order things happen and what to do when something fails, in other words, _**orchestrate**_ the business process but don't contain the business logic itself. If a step is getting long, extract the work into a ruby service object and call it from the step. #### Defining Steps @@ -178,6 +173,24 @@ class PublishArticleOperation < Operational::Operation end ``` +#### State + +Every operation revolves around a single **state hash**. It's created when you call the operation, passed to every step, and returned in the result. Steps read from it, write to it, and use it to pass data to each other — similar to how Unix pipes pass data through a chain of commands: + +```ruby +result = ChargeOrderOperation.call(params: { id: 1 }, current_user: admin) +# └──────────── initial state ───────────┘ + +# Each step receives and mutates the same hash: +# step :find_order → state[:order] = Order.find_by(...) +# step :charge_payment → state[:charge] = PaymentGateway.charge(...) + +result.state # => frozen snapshot of the final state +result[:order] # => the order that was charged +``` + +This single shared hash means steps are fully decoupled — they don't know about each other, they just read and write to state. You can reorder, add, or remove steps without changing method signatures. And because state is frozen after the operation completes, the result is an immutable snapshot of everything that happened. + #### A Realistic Example ```ruby @@ -185,7 +198,7 @@ class ChargeOrderOperation < Operational::Operation step :find_order step :charge_payment pass :track_analytics - step :send_confirmation + pass :send_confirmation fail :refund def find_order(state) @@ -205,7 +218,6 @@ class ChargeOrderOperation < Operational::Operation def send_confirmation(state) OrderMailer.confirmation(state[:order]).deliver_later - true end def refund(state) @@ -217,7 +229,7 @@ end ### Forms -Forms decouple input validation from your models. They allow you to build UI and APIs that aren't coupled to your database modeling and allow you define exactly what you're willing to accept. +Forms decouple input validation from your models. They allow you to build UI and APIs that aren't coupled to your database modeling and allow you define exactly what parameters you'll accept in a declarative way. They're built on `ActiveModel::Model`, `ActiveModel::Attributes`, and `ActiveModel::Dirty` — so you already know the API. @@ -258,10 +270,7 @@ form.sync(model: article) article.title # => "Updated" ``` -Any params that don't match a defined form attribute are silently ignored — no need for `strong_parameters`. `ActionController::Parameters` are handled automatically. - -> [!NOTE] -> Inside an operation, [`Contract` helpers](#contracts) handle this entire lifecycle as steps — you won't call these methods directly. +Any params that don't match a defined form attribute are silently ignored — no need for `strong_parameters`. You can also pass **state** to `.build`, which is separate from the form's attributes — it's not user input, it's context. State is available as `@state` and is useful for conditional validation (e.g., only admins can publish) and prepopulating defaults from things the user doesn't control: @@ -269,9 +278,12 @@ You can also pass **state** to `.build`, which is separate from the form's attri form = ArticleForm.build(model: article, state: { current_user: current_user, team: team }) ``` -#### Multi-Model Forms: Prepopulate and Sync Hooks +> [!NOTE] +> Inside an operation, [`Contract` helpers](#contracts) handle this entire lifecycle as steps — you won't call these methods directly, and state is passed automatically. -For simple single-model forms, the automatic attribute matching handles everything. For more complex cases — where a single form spans multiple models — you can define `prepopulate` and `on_sync` hooks to control how data flows in and out: +#### Multi-Model Forms: on_build and on_sync Hooks + +For simple single-model forms, the automatic attribute matching handles everything. For more complex cases — where a single form spans multiple models — you can define `on_build` and `on_sync` hooks to control how data flows in and out: ```ruby class NewArticleForm < Operational::Form @@ -281,7 +293,7 @@ class NewArticleForm < Operational::Form attribute :default_category, :string # Pull data IN from multiple sources when the form is built - def prepopulate(state) + def on_build(state) self.author_bio = state[:current_user]&.bio self.default_category = state[:team]&.default_category end @@ -292,7 +304,7 @@ class NewArticleForm < Operational::Form end end -# Build pulls from article (automatic) + current_user + team (via prepopulate) +# Build pulls from article (automatic) + current_user + team (via on_build) form = NewArticleForm.build(model: article, state: { current_user: user, team: team, author: user }) # Sync writes to article (automatic) + author (via on_sync) @@ -349,7 +361,7 @@ Options: - `name:` — state key to store the form (default: `:contract`) - `model_key:` — state key containing the model to pre-populate from - `model_persisted:` — override `persisted?` detection -- `prepopulate_method:` — method to call for prepopulation (default: `:prepopulate`) +- `build_method:` — method to call during build (default: `:on_build`) #### Contract.Validate @@ -387,23 +399,19 @@ Options: - `model_key:` — state key containing the model to sync to - `sync_method:` — custom sync hook method name (default: `:on_sync`) -#### Full Worked Example +#### Putting It Together ```ruby -# app/forms/article_form.rb +# app/concepts/article/article_form.rb class ArticleForm < Operational::Form attribute :title, :string attribute :body, :string validates :title, presence: true validates :body, presence: true - - def on_sync(state) - state[:article].published_at = Time.current if state[:publish] - end end -# app/operations/create_article_operation.rb +# app/concepts/article/create_article_operation.rb class CreateArticleOperation < Operational::Operation step :build_article step Contract::Build(contract: ArticleForm, model_key: :article) @@ -456,9 +464,9 @@ class CreateArticleOperation < Operational::Operation end ``` -The `CreateArticleOperation::Present` operation can be used on the `new` controller action and the `CreateArticleOperation` can be used on the `create` controller action, without duplicating the setup needed to present the form and save it... something often done by extracting helpers in the controller. +Use `CreateArticleOperation::Present` for the `new` action and `CreateArticleOperation` for `create` — no need to duplicate setup or extract controller helpers. -### Rails Integration +## Rails Integration Include `Operational::Controller` in your controllers to get the `run` helper: @@ -581,6 +589,8 @@ The `new` action runs just `Present` to build an empty form. The `create` action ## Testing +Testing Operations and Forms is straight forward. Operations and Forms are just plain ruby objects that can be easily tested as unit tests. + ### Testing Operations ```ruby @@ -636,7 +646,7 @@ end ## Contributing -Bug reports and pull requests are welcome on GitHub at https://github.com/bryanrite/operational. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. +Bug reports and pull requests are welcome on [GitHub](https://github.com/bryanrite/operational). ## License diff --git a/operational.gemspec b/operational.gemspec index d26c374..afc0fde 100644 --- a/operational.gemspec +++ b/operational.gemspec @@ -8,8 +8,8 @@ Gem::Specification.new do |spec| spec.authors = ["Bryan Rite"] spec.email = ["bryan@bryanrite.com"] - spec.summary = %q{Simplify your Ruby Application with Operations} - spec.description = %q{Help organize a complex business domain into a consistent and functional interface of immutable, stateless, and repeatable Operations} + spec.summary = %q{Lightweight, railway-oriented operation and form objects for business logic} + spec.description = %q{Operational wraps your business logic into operations — small classes with a railway of steps that succeed or fail. Pair them with form objects and contracts to decouple your UI and APIs from your models.} spec.homepage = "https://github.com/bryanrite/operational" spec.license = "MIT" @@ -20,11 +20,7 @@ Gem::Specification.new do |spec| "bug_tracker_uri" => "https://github.com/bryanrite/operational/issues" } - spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } - end - spec.bindir = "exe" - spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } + spec.files = Dir["lib/**/*", "LICENSE", "README.md", "AI_README.md", "CHANGELOG.md"] spec.require_paths = ["lib"] spec.required_ruby_version = Gem::Requirement.new(">= 3.0") From 57aecec0ff52c6a34ac9285a690cc82e29f02ef3 Mon Sep 17 00:00:00 2001 From: Bryan Rite Date: Tue, 17 Mar 2026 15:31:51 -0700 Subject: [PATCH 3/3] Finalize docs. --- AI_README.md | 42 ++++++++++++++++----------- README.md | 81 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 73 insertions(+), 50 deletions(-) diff --git a/AI_README.md b/AI_README.md index 2e96fcc..f5f7fdc 100644 --- a/AI_README.md +++ b/AI_README.md @@ -17,21 +17,21 @@ Subclass `Operational::Operation`. Define steps with `step`, `pass`, or `fail` a ```ruby class CreateArticleOperation < Operational::Operation - step :build - step Contract::Build(contract: ArticleForm, model_key: :article) + step :init + step Contract::Build(contract: ArticleForm) step Contract::Validate() - step Contract::Sync(model_key: :article) + step Contract::Sync() step :save pass :notify # return value ignored, never derails fail :handle # only runs on failure track - def build(state) - state[:article] = Article.new + def init(state) + state[:model] = Article.new # must return truthy to continue, falsy switches to failure track end def save(state) - state[:article].save # returns true/false naturally + state[:model].save # returns true/false naturally end def notify(state) @@ -88,20 +88,20 @@ Use `Nested::Operation` to call one operation from within another. State is merg class CreateArticleOperation < Operational::Operation class Present < Operational::Operation step :init - step Contract::Build(contract: ArticleForm, model_key: :article) + step Contract::Build(contract: ArticleForm) def init(state) - state[:article] = Article.new + state[:model] = Article.new end end step Nested::Operation(operation: Present) step Contract::Validate() - step Contract::Sync(model_key: :article) + step Contract::Sync() pass :persist def persist(state) - state[:article].save! + state[:model].save! end end ``` @@ -180,7 +180,7 @@ These are used inside operations as step actions. They return lambdas. step Contract::Build( contract: MyForm, # required — the form class name: :contract, # state key to store the form instance - model_key: nil, # state key containing the model to build from + model_key: :model, # state key containing the model to build from (used only if present in state) model_persisted: nil, # override persisted? detection build_method: :on_build ) @@ -206,7 +206,7 @@ Returns the result of `form.validate(params)` — `true`/`false`. ```ruby step Contract::Sync( name: :contract, # state key where the form is stored - model_key: nil, # state key containing the model to sync to + model_key: :model, # state key containing the model to sync to sync_method: :on_sync # custom sync hook method name ) ``` @@ -221,7 +221,7 @@ class MyController < ApplicationController def create if run CreateArticleOperation - redirect_to @state[:article] + redirect_to @state[:model] else render :new, status: :unprocessable_entity end @@ -267,26 +267,34 @@ Example: `app/concepts/article/article_form.rb`, `app/concepts/article/create_ar class CreateThingOperation < Operational::Operation class Present < Operational::Operation step :init - step Contract::Build(contract: ThingForm, model_key: :thing) + step Contract::Build(contract: ThingForm) def init(state) - state[:thing] = Thing.new + state[:model] = Thing.new end end step Nested::Operation(operation: Present) step Contract::Validate() - step Contract::Sync(model_key: :thing) + step Contract::Sync() pass :persist def persist(state) - state[:thing].save! + state[:model].save! end end ``` Controller uses `CreateThingOperation::Present` for `new` and `CreateThingOperation` for `create`. +To use a descriptive state key instead of `:model`, pass `model_key:` explicitly: + +```ruby +step Contract::Build(contract: ThingForm, model_key: :thing) +step Contract::Sync(model_key: :thing) +# state[:thing] instead of state[:model] +``` + ### Multi-model form ```ruby diff --git a/README.md b/README.md index 3724c46..9a28812 100644 --- a/README.md +++ b/README.md @@ -50,23 +50,23 @@ end # An operation — wires together validation, persistence, and business process with railway functional programming. class RegisterUserOperation < Operational::Operation - step :setup_user - step Contract::Build(contract: SignupForm, model_key: :user) + step :setup + step Contract::Build(contract: SignupForm) step Contract::Validate() - step Contract::Sync(model_key: :user) + step Contract::Sync() step :persist pass :send_welcome - def setup_user(state) - state[:user] = User.new(role: :member) + def setup(state) + state[:model] = User.new(role: :member) end def persist(state) - state[:user].save + state[:model].save end def send_welcome(state) - WelcomeMailer.welcome(state[:user]).deliver_later + WelcomeMailer.welcome(state[:model]).deliver_later end end ``` @@ -74,7 +74,7 @@ end ```ruby # In your controller — simple boolean branching. if run RegisterUserOperation - redirect_to dashboard_path, notice: "Welcome #{@state[:user].name}!" + redirect_to dashboard_path, notice: "Welcome #{@state[:model].name}!" else render :new, status: :unprocessable_entity end @@ -141,7 +141,7 @@ result[:order] # => shorthand for result.state[:order] result.operation # => the operation instance ``` -There is intentionally one entry point (`.call`) and one result type — no `.call!` or bang variants. Check `succeeded?` and branch accordingly. +There is intentionally one entry point (`.call`) and one result type. Check `succeeded?` and branch accordingly. #### The Railway: step, pass, fail @@ -179,7 +179,7 @@ Every operation revolves around a single **state hash**. It's created when you c ```ruby result = ChargeOrderOperation.call(params: { id: 1 }, current_user: admin) -# └──────────── initial state ───────────┘ +# └──────────── initial state ───────────┘ # Each step receives and mutates the same hash: # step :find_order → state[:order] = Order.find_by(...) @@ -229,7 +229,7 @@ end ### Forms -Forms decouple input validation from your models. They allow you to build UI and APIs that aren't coupled to your database modeling and allow you define exactly what parameters you'll accept in a declarative way. +Forms decouple input validation from your models. They allow you to build UI and APIs that aren't coupled to your database modeling and allow you to define exactly what parameters you'll accept in a declarative way. They're built on `ActiveModel::Model`, `ActiveModel::Attributes`, and `ActiveModel::Dirty` — so you already know the API. @@ -270,7 +270,7 @@ form.sync(model: article) article.title # => "Updated" ``` -Any params that don't match a defined form attribute are silently ignored — no need for `strong_parameters`. +Any params that don't match a defined form attribute are ignored — no need for `strong_parameters`, your form defines what parameters you will accept. You can also pass **state** to `.build`, which is separate from the form's attributes — it's not user input, it's context. State is available as `@state` and is useful for conditional validation (e.g., only admins can publish) and prepopulating defaults from things the user doesn't control: @@ -304,7 +304,7 @@ class NewArticleForm < Operational::Form end end -# Build pulls from article (automatic) + current_user + team (via on_build) +# Build pulls from article (automatic) + current_user/team (via on_build) form = NewArticleForm.build(model: article, state: { current_user: user, team: team, author: user }) # Sync writes to article (automatic) + author (via on_sync) @@ -349,17 +349,17 @@ Contract helpers wire forms into operations as steps. This is where Operations a Creates a form instance and stores it in the state: ```ruby +# Simple — builds the form and pre-populates from state[:model] step Contract::Build(contract: ArticleForm) -# state[:contract] is now an ArticleForm instance -# With a model for pre-population: +# With a custom model key — pre-populates from state[:article] instead step Contract::Build(contract: ArticleForm, model_key: :article) ``` Options: - `contract:` — the form class (required) - `name:` — state key to store the form (default: `:contract`) -- `model_key:` — state key containing the model to pre-populate from +- `model_key:` — state key containing the model to build from (default: `:model`) - `model_persisted:` — override `persisted?` detection - `build_method:` — method to call during build (default: `:on_build`) @@ -368,16 +368,14 @@ Options: Validates the form using params from the state: ```ruby +# Simple — validates state[:contract] with state[:params] step Contract::Validate() -# Validates state[:contract] with state[:params] -# With nested params: +# With nested params — validates with state[:params][:article] step Contract::Validate(params_path: :article) -# Validates with state[:params][:article] -# With a custom path: +# With a custom path — validates with state.dig(:custom, :path) step Contract::Validate(params_path: [:custom, :path]) -# Validates with state[:custom][:path] ``` Options: @@ -391,12 +389,16 @@ Returns `true` if validation passes, `false` otherwise — making it a natural r Syncs form data back to a model: ```ruby +# Simple — syncs form attributes back to state[:model] +step Contract::Sync() + +# With a custom model key — syncs back to state[:article] instead step Contract::Sync(model_key: :article) ``` Options: - `name:` — state key where the form is stored (default: `:contract`) -- `model_key:` — state key containing the model to sync to +- `model_key:` — state key containing the model to sync to (default: `:model`) - `sync_method:` — custom sync hook method name (default: `:on_sync`) #### Putting It Together @@ -413,25 +415,38 @@ end # app/concepts/article/create_article_operation.rb class CreateArticleOperation < Operational::Operation - step :build_article - step Contract::Build(contract: ArticleForm, model_key: :article) + step :init + step Contract::Build(contract: ArticleForm) step Contract::Validate() - step Contract::Sync(model_key: :article) + step Contract::Sync() step :save - def build_article(state) - state[:article] = Article.new + def init(state) + state[:model] = Article.new end def save(state) - state[:article].save + state[:model].save end end -# Usage +# Direct usage result = CreateArticleOperation.call(params: { title: "Hello", body: "World" }) result.succeeded? # => true -result[:article] # => #
+result[:model] # => #
+ +# From a controller +class ArticlesController < ApplicationController + include Operational::Controller + + def create + if run CreateArticleOperation + redirect_to @state[:model], notice: "Article created!" + else + render :new, status: :unprocessable_entity + end + end +end ``` ### Composing Operations @@ -442,10 +457,10 @@ Just like Rails controllers pair `new`/`create` and `edit`/`update`, operations class CreateArticleOperation < Operational::Operation # The "new" part — builds the model and sets up the form class Present < Operational::Operation - step :build_article + step :init step Contract::Build(contract: ArticleForm, model_key: :article) - def build_article(state) + def init(state) state[:article] = Article.new(author: state[:current_user]) end end @@ -589,7 +604,7 @@ The `new` action runs just `Present` to build an empty form. The `create` action ## Testing -Testing Operations and Forms is straight forward. Operations and Forms are just plain ruby objects that can be easily tested as unit tests. +Testing Operations and Forms is straightforward. They are plain Ruby objects that can be tested as unit tests — no controller or request specs needed. ### Testing Operations