Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,19 @@
```
treaty/
├── lib/treaty/
│ ├── action/ # Treaty Action classes
│ │ ├── context/ # Execution context and callable mechanism
│ │ ├── executor/ # Executor wrapper for inventory
│ │ ├── info/rest/ # REST API information generation
│ │ ├── inventory/ # Inventory system
│ │ ├── request/ # Request handling
│ │ ├── response/ # Response handling
│ │ └── versions/ # Version management
│ ├── controller/ # Rails controller integration
│ ├── entity/ # Entity classes and core attribute system
│ │ └── attribute/ # Core attribute system (DSL, validation)
│ │ ├── option/ # Option processors (validators, modifiers)
│ │ └── validation/ # Attribute validation
│ ├── request/ # Request handling
│ ├── response/ # Response handling
│ ├── versions/ # Version management
│ ├── inventory/ # Inventory system
│ └── exceptions/ # Exception classes
├── spec/
│ ├── sandbox/app/ # Example implementations
Expand Down Expand Up @@ -403,8 +407,8 @@ RSpec.describe Posts::CreateTreaty do
subject(:perform) { described_class.call!(context:, inventory:, version:, params:) }

let(:context) { instance_double(ApplicationController) }
let(:inventory) { Treaty::Executor::Inventory.new(collection, context) }
let(:collection) { Treaty::Inventory::Collection.new }
let(:inventory) { Treaty::Action::Executor::Inventory.new(collection, context) }
let(:collection) { Treaty::Action::Inventory::Collection.new }

context "when valid params" do
let(:version) { "1" }
Expand Down
10 changes: 5 additions & 5 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ class MyTreaty < ApplicationTreaty
end
```

## Treaty::Result Class
## Treaty::Action::Result Class

### Overview

Every successful treaty execution returns a `Treaty::Result` object containing the validated data, HTTP status code, and version information.
Every successful treaty execution returns a `Treaty::Action::Result` object containing the validated data, HTTP status code, and version information.

**Structure:**

```ruby
class Treaty::Result
class Treaty::Action::Result
attr_reader :data, :status, :version
end
```
Expand Down Expand Up @@ -105,7 +105,7 @@ RSpec.describe Posts::CreateTreaty do
let(:params) { { post: { title: "Test" } } }

it "returns expected result structure" do
expect(perform).to be_a(Treaty::Result)
expect(perform).to be_a(Treaty::Action::Result)
expect(perform.data).to include(:post)
expect(perform.status).to eq(201)
expect(perform.version).to eq(Gem::Version.new("1"))
Expand Down Expand Up @@ -150,7 +150,7 @@ end
result = Posts::CreateTreaty.call!(version: "1", params: params)

result.inspect
# => "#<Treaty::Result @data={...}, @status=201, @version=#<Gem::Version \"1\">>"
# => "#<Treaty::Action::Result @data={...}, @status=201, @version=#<Gem::Version \"1\">>"
```

### Introspection Methods
Expand Down
2 changes: 1 addition & 1 deletion docs/core-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ A Treaty (contract) is a formal definition of the data structure for an API endp

### 1. Treaty Class

Inherits from `Treaty::Base` and defines the contract for a specific action.
Inherits from `Treaty::Action::Base` and defines the contract for a specific action.

```ruby
class Posts::CreateTreaty < ApplicationTreaty
Expand Down
2 changes: 1 addition & 1 deletion docs/defining-contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Learn how to define Treaty contracts, including request and response definitions

A Treaty contract consists of:

1. **Class definition** - inheriting from `Treaty::Base`
1. **Class definition** - inheriting from `Treaty::Action::Base`
2. **Version blocks** - one or more version definitions
3. **Request definition** - what data comes in
4. **Response definition(s)** - what data goes out
Expand Down
8 changes: 4 additions & 4 deletions docs/entities.md
Original file line number Diff line number Diff line change
Expand Up @@ -658,18 +658,18 @@ end
compare_entities(Posts::Create::RequestEntity, Posts::Create::ResponseEntity)
```

### Comparison with Treaty::Base.info
### Comparison with Treaty::Action::Base.info

While `Treaty::Entity::Base.info` returns entity attribute metadata, `Treaty::Base.info` (for REST API treaties) returns version-based contract information:
While `Treaty::Entity::Base.info` returns entity attribute metadata, `Treaty::Action::Base.info` (for REST API treaties) returns version-based contract information:

| Feature | Treaty::Entity::Base.info | Treaty::Base.info |
| Feature | Treaty::Entity::Base.info | Treaty::Action::Base.info |
|---------|---------------------|-------------------|
| **Returns** | `Treaty::Info::Entity::Result` | `Treaty::Info::Rest::Result` |
| **Primary attribute** | `.attributes` (Hash) | `.versions` (Array) |
| **Use case** | Entity structure introspection | API version and contract details |
| **Contains** | Attribute types, options, nesting | Versions, executors, request/response specs |

**Example of Treaty::Base.info:**
**Example of Treaty::Action::Base.info:**

```ruby
class Posts::IndexTreaty < ApplicationTreaty
Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ end
Create `app/treaties/application_treaty.rb`:

```ruby
class ApplicationTreaty < Treaty::Base
class ApplicationTreaty < Treaty::Action::Base
end
```

Expand Down
8 changes: 4 additions & 4 deletions docs/inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ The inventory system uses **lazy evaluation** to optimize performance. Inventory

**How it works:**

1. When a treaty executes, a `Treaty::Executor::Inventory` instance is created with:
1. When a treaty executes, a `Treaty::Action::Executor::Inventory` instance is created with:
- The inventory collection (definitions from your `provide` calls)
- The controller context (for method calls and proc evaluation)

Expand Down Expand Up @@ -152,7 +152,7 @@ end

## Accessing Inventory in Services

Inventory is passed to services as a `Treaty::Executor::Inventory` instance, which provides both **method-based** and **hash-based** access to inventory items.
Inventory is passed to services as a `Treaty::Action::Executor::Inventory` instance, which provides both **method-based** and **hash-based** access to inventory items.

### Method-Based Access (Recommended)

Expand Down Expand Up @@ -207,7 +207,7 @@ class Posts::IndexService < ApplicationService::Base
end
```

The `inventory` input receives a `Treaty::Executor::Inventory` instance providing method-based access to evaluated inventory items.
The `inventory` input receives a `Treaty::Action::Executor::Inventory` instance providing method-based access to evaluated inventory items.

### Proc Executors

Expand Down Expand Up @@ -422,7 +422,7 @@ input :inventory, required: false
input :inventory
```

**Note**: The inventory is a `Treaty::Executor::Inventory` instance. Servactory's type checking is flexible and will accept it.
**Note**: The inventory is a `Treaty::Action::Executor::Inventory` instance. Servactory's type checking is flexible and will accept it.

If a Servactory service receives inventory but hasn't declared the input, Servactory will raise `Servactory::Exceptions::Input` error.

Expand Down
31 changes: 31 additions & 0 deletions lib/treaty/action.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

module Treaty
# Action namespace containing the base class and versioning system.
#
# Users should inherit from Treaty::Action::Base:
#
# class Posts::CreateTreaty < Treaty::Action::Base
# version 1, default: true do
# summary "Create a new post"
#
# request do
# object :post do
# string :title, :required
# end
# end
#
# response 201 do
# object :post do
# string :id
# end
# end
#
# delegate_to Posts::CreateService
# end
# end
#
# @see Treaty::Action::Base for full documentation and examples
module Action
end
end
11 changes: 11 additions & 0 deletions lib/treaty/action/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module Treaty
module Action
class Base
include Info::DSL
include Context::DSL
include Versions::DSL
end
end
end
90 changes: 90 additions & 0 deletions lib/treaty/action/context/callable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# frozen_string_literal: true

module Treaty
module Action
module Context
# Class methods for calling treaty actions.
#
# ## Purpose
#
# Provides the public `call!` class method that serves as the entry
# point for treaty execution. Handles instance creation and delegates
# to instance methods.
#
# ## Usage
#
# Extended into:
# - Treaty classes (via Context::DSL)
#
# Called by:
# - Controller integration
# - Direct treaty invocation in tests
#
# ## Call Pattern
#
# ```ruby
# # Public API:
# result = Posts::CreateTreaty.call!(
# version: "1",
# params: { post: { title: "Hello" } },
# context: controller, # optional
# inventory: inventory # optional
# )
#
# result.data # => validated response hash
# result.status # => HTTP status code
# result.version # => resolved version
# ```
#
# ## Implementation
#
# Creates a new treaty instance and delegates to `_call!` which
# passes through to Workspace, then to Versions::Workspace for
# actual execution.
module Callable
# Executes treaty with given parameters
#
# Main entry point for treaty execution. Creates instance
# and delegates to instance methods for actual work.
#
# @param version [String, nil] Requested API version (nil uses default)
# @param params [Hash] Request parameters
# @param context [Object, nil] Controller context (for inventory evaluation)
# @param inventory [Treaty::Action::Inventory::Collection, nil] Inventory items
# @return [Treaty::Action::Result] Execution result
# @raise [Treaty::Exceptions::VersionNotFound] If version not found
# @raise [Treaty::Exceptions::Validation] If validation fails
# @raise [Treaty::Exceptions::Execution] If service execution fails
def call!(version:, params:, context: nil, inventory: nil)
treaty_instance = send(:new)

_call!(treaty_instance, context:, inventory:, version:, params:)
end

private

# Internal call delegation to instance
#
# Passes all parameters plus class-level collection_of_versions
# to the instance's _call! method.
#
# @param treaty_instance [Object] Treaty instance
# @param context [Object, nil] Controller context
# @param inventory [Treaty::Action::Inventory::Collection, nil] Inventory items
# @param version [String, nil] Requested version
# @param params [Hash] Request parameters
# @return [Treaty::Action::Result] Execution result
def _call!(treaty_instance, context:, inventory:, version:, params:)
treaty_instance.send(
:_call!,
context:,
inventory:,
version:,
params:,
collection_of_versions:
)
end
end
end
end
end
56 changes: 56 additions & 0 deletions lib/treaty/action/context/dsl.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# frozen_string_literal: true

module Treaty
module Action
module Context
# DSL module that wires up callable and workspace functionality.
#
# ## Purpose
#
# Acts as a composition root that includes both Callable (class methods)
# and Workspace (instance methods) when included in a treaty class.
# This enables the `call!` API pattern.
#
# ## Usage
#
# Included in:
# - Treaty::Action::Base (as part of core DSL)
#
# ## What it provides
#
# When included, adds:
# - Class method: `call!` (from Callable)
# - Instance methods: `_call!`, `call!` (from Workspace)
#
# ## Architecture
#
# The call chain works as follows:
#
# ```
# MyTreaty.call!(version:, params:, ...)
# │
# └─► Callable.call! (class method)
# │
# ├─► Creates treaty instance
# │
# └─► Workspace._call! (instance method)
# │
# └─► Workspace.call! (stores @collection_of_versions)
# │
# └─► super → Versions::Workspace.call!
# ```
module DSL
# Hook called when module is included
#
# Extends the including class with Callable (class methods)
# and includes Workspace (instance methods).
#
# @param base [Class] The class including this module
def self.included(base)
base.extend(Callable)
base.include(Workspace)
end
end
end
end
end
Loading
Loading