solid-process 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 973ae538a96362750bac5c31245c2e8390144c4d67688940fa8b28f2ee27e538
4
- data.tar.gz: 057c4bbbd8e2c4fc955c548a714fdb7fbe4396e1b8d624dc7702cc4750e929fa
3
+ metadata.gz: 56d7e7dae11f894dafd8cd1970ccf6fa1a9e6f6879a8f6370a3c5fa3b8419fcd
4
+ data.tar.gz: c15ca09276f4e0a366c980fd44cb6885f7cb5e1439786222c324828b2e7a5632
5
5
  SHA512:
6
- metadata.gz: 20c3a43c503dfc26560ca25cb20a3d4d53f2014de3f9883bfbd872e7759881fc636da985f335d53f9c9c81b730b797f9759649543b49644ddf2e774222a4d2cc
7
- data.tar.gz: 3802c1bab8d942f351099e0510f9cfae3d86b110508cea3d96b61c4bd4eb5953c249b2280d4565bc3b02990744d908f229267edffc88eab38100664bebe83959
6
+ metadata.gz: 3a7afa31c7de01092d6c75130d50e557c83e7e8b12b7a30667fce3bbfdb7c2876298907da67c8c0cfa8b1e1bd375b88d11d106d800bb628cb70e7efe7203324d
7
+ data.tar.gz: 53b395cec3553f20305db78fc24b51b09283034310363f26bdf53711aec85021aa5aa85cb24645d692d85df0218c8cec2700f42c315f983507dc27375f65f80d
data/.claude/CLAUDE.md ADDED
@@ -0,0 +1,112 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ `solid-process` is a Ruby gem for encapsulating business logic into manageable processes. It integrates with Rails and provides input validation, dependency injection, and observability features.
8
+
9
+ ## Common Commands
10
+
11
+ ```bash
12
+ # Setup (install dependencies: bundle + appraisal)
13
+ bin/setup
14
+
15
+ # Clean install + full test suite. Useful when switching Ruby versions.
16
+ bin/matrix
17
+
18
+ # Run all tests for current Ruby version
19
+ bin/rake matrix
20
+
21
+ # Run a single test file
22
+ bundle exec appraisal rails-8-1 ruby -Ilib:test test/solid/process/result_test.rb
23
+
24
+ # Run a specific test by name
25
+ bundle exec appraisal rails-8-1 ruby -Ilib:test test/solid/process/result_test.rb -n test_method_name
26
+
27
+ # Interactive console
28
+ bin/console
29
+ ```
30
+
31
+ ## Lint
32
+
33
+ **Ensure lint is green before committing.**
34
+
35
+ ```bash
36
+ # Check for style violations (Ruby 3.4+)
37
+ bin/rake standard
38
+
39
+ # Auto-fix style violations
40
+ bin/rake standard:fix
41
+ ```
42
+
43
+ ### Release Checklist
44
+
45
+ When `lib/solid/process/version.rb` is changed:
46
+ 1. Verify `CHANGELOG.md` has a matching version entry (e.g., `## [0.5.0] - YYYY-MM-DD`)
47
+ 2. Verify `README.md` compatibility matrix is up-to-date if Ruby/Rails support changed
48
+
49
+ ### Ruby/Rails Version Alignment
50
+
51
+ When modifying Ruby or Rails version support, ensure these files stay aligned:
52
+
53
+ | File | Purpose |
54
+ |------|---------|
55
+ | `Appraisals` | Source of truth for gem dependencies per Rails version |
56
+ | `Rakefile` | Local `rake matrix` task conditions |
57
+ | `.github/workflows/main.yml` | CI matrix and step conditions |
58
+ | `README.md` | Compatibility matrix table |
59
+
60
+ **Key checks:**
61
+ 1. Ruby version conditions must match across `Appraisals`, `Rakefile`, and CI
62
+ 2. CI string values (e.g., `'head'`) need explicit checks since numeric comparisons won't match them
63
+ 3. README table must reflect the actual tested combinations
64
+ 4. Ruby `head` should only run against Rails edge (not stable Rails versions)
65
+
66
+ ## Switching Ruby Versions
67
+
68
+ ### ASDF
69
+
70
+ ```bash
71
+ asdf list ruby # List installed Ruby versions
72
+ asdf set ruby 4.0.1 # Ruby 4.x
73
+ ```
74
+
75
+ ## Tests
76
+
77
+ Always test on multiple Ruby versions when fixing compatibility issues.
78
+
79
+ ### Support Files
80
+
81
+ Test fixtures in `test/support/` follow numbered naming (e.g., `051_user_token_creation.rb`) for load order. These define sample processes used across multiple tests.
82
+
83
+ ### Fix Library Code, Not Tests
84
+
85
+ When tests fail due to Ruby version differences:
86
+ 1. First check if the **library code** can be updated to normalize behavior
87
+ 2. Only modify tests if the library fix isn't possible
88
+
89
+ ### Available Rails versions (see Rakefile for full list)
90
+ ```sh
91
+ bundle exec appraisal rails-6-0 rake test # Ruby 2.7, 3.0
92
+ bundle exec appraisal rails-6-1 rake test # Ruby 2.7, 3.0
93
+ bundle exec appraisal rails-7-0 rake test # Ruby 3.0, 3.1, 3.2, 3.3
94
+ bundle exec appraisal rails-7-1 rake test # Ruby 3.0, 3.1, 3.2, 3.3
95
+ bundle exec appraisal rails-7-2 rake test # Ruby 3.1, 3.2, 3.3, 3.4
96
+ bundle exec appraisal rails-8-0 rake test # Ruby 3.2, 3.3, 3.4
97
+ bundle exec appraisal rails-8-1 rake test # Ruby 3.3, 3.4, 4.x
98
+ ```
99
+
100
+ ## Architecture
101
+
102
+ ### Core Classes
103
+
104
+ - **Solid::Process** (`lib/solid/process.rb`) - Base class for business processes. Requires `input` block and `call` method. Returns `Success` or `Failure` outputs.
105
+
106
+ - **Solid::Model** (`lib/solid/model.rb`) - ActiveModel-based concern providing attributes, validations, and callbacks.
107
+
108
+ - **Solid::Input** (`lib/solid/input.rb`) - Input validation class, includes Solid::Model.
109
+
110
+ - **Solid::Value** (`lib/solid/value.rb`) - Immutable value objects.
111
+
112
+ - **Solid::Output** (`lib/solid/output.rb`) - Wraps solid-result gem for Success/Failure return types.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.0] - 2025-01-27
4
+
5
+ ### Added
6
+
7
+ - Add support for Ruby 3.4 and 4.x.
8
+
9
+ - Add support for Rails 7.2, 8.0, and 8.1.
10
+
11
+ ### Fixed
12
+
13
+ - Fix `BacktraceCleaner::BLOCKS_PATTERN` to handle Ruby 4.x backtrace format (`'Kernel#then'` instead of `` `then' ``).
14
+
3
15
  ## [0.4.0] - 2024-06-23
4
16
 
5
17
  ### Added
data/README.md CHANGED
@@ -9,77 +9,242 @@
9
9
  </p>
10
10
  </p>
11
11
 
12
- ## Supported Ruby and Rails
13
-
14
- This library is tested against:
15
-
16
- | Ruby / Rails | 6.0 | 6.1 | 7.0 | 7.1 | Edge |
17
- |--------------|-----|-----|-----|-----|------|
18
- | 2.7 | ✅ | ✅ | ✅ | ✅ | |
19
- | 3.0 | ✅ | ✅ | ✅ | ✅ | |
20
- | 3.1 | ✅ | ✅ | ✅ | ✅ | ✅ |
21
- | 3.2 | ✅ | ✅ | ✅ | ✅ | ✅ |
22
- | 3.3 | ✅ | ✅ | ✅ | ✅ | ✅ |
23
- | Head | | | | ✅ | ✅ |
12
+ ## 📚 Table of Contents <!-- omit from toc -->
13
+
14
+ - [Introduction](#introduction)
15
+ - [Installation](#installation)
16
+ - [The Basic Structure](#the-basic-structure)
17
+ - [Further Reading](#further-reading)
18
+ - [Development](#development)
19
+ - [Contributing](#contributing)
20
+ - [License](#license)
21
+ - [Code of Conduct](#code-of-conduct)
22
+ - [Acknowledgments](#acknowledgments)
23
+ - [About](#about)
24
+
25
+ ## Supported Ruby and Rails <!-- omit from toc -->
26
+
27
+ This library is tested (100% coverage) against:
28
+
29
+ | Ruby / Rails | 6.0 | 6.1 | 7.0 | 7.1 | 7.2 | 8.0 | 8.1 | Edge |
30
+ |--------------|-----|-----|-----|-----|-----|-----|-----|------|
31
+ | 2.7 | ✅ | ✅ | ✅ | ✅ | | | | |
32
+ | 3.0 | ✅ | ✅ | ✅ | ✅ | | | | |
33
+ | 3.1 | | | ✅ | ✅ | ✅ | | | |
34
+ | 3.2 | | | ✅ | ✅ | ✅ | ✅ | | |
35
+ | 3.3 | | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
36
+ | 3.4 | | | | | ✅ | ✅ | ✅ | ✅ |
37
+ | 4.x | | | | | | | ✅ | ✅ |
24
38
 
25
39
  ## Introduction
26
40
 
27
41
  `solid-process` is a Ruby/Rails library designed to encapsulate business logic into manageable processes. It simplifies writing, testing, maintaining, and evolving your code, ensuring it remains clear and approachable as your application scales.
28
42
 
29
- **Key Objectives:**
43
+ **Features:** (_touch to expand_)
44
+
45
+ <details><summary>1️⃣ <strong>Seamless Rails integration</strong></summary>
46
+
47
+ > Designed to complement Ruby on Rails, this library integrates smoothly without conflicting with existing framework conventions and features.
48
+
49
+ </details>
50
+
51
+ <details><summary>2️⃣ <strong>Support progressive mastery</strong></summary>
52
+
53
+ > Offers an intuitive entry point for novices while providing robust, advanced features that cater to experienced developers.
54
+
55
+ </details>
30
56
 
31
- 1. **Seamless Rails integration:** Designed to fully complement the Ruby on Rails framework, this library integrates smoothly without conflicting with existing Rails conventions and capabilities.
57
+ <details><summary>3️⃣ <strong>Promote conceptual integrity and rapid onboarding</strong></summary>
32
58
 
33
- 2. **Support progressive mastery:** Offers an intuitive entry point for novices while providing robust, advanced features that cater to experienced developers.
59
+ > By maintaining a consistent design philosophy, `solid-process` reduces the learning curve for new developers, allowing them to contribute more effectively and quickly to a codebase.
34
60
 
35
- 3. **Promote conceptual integrity and rapid onboarding:** By maintaining a consistent design philosophy, `solid-process` reduces the learning curve for new developers, allowing them to contribute more effectively and quickly.
61
+ </details>
36
62
 
37
- 4. **Minimize technical debt:** Facilitate smoother transitions and updates as your application expands and your team size increases.
63
+ <details><summary>4️⃣ <strong>Enhanced observability</strong></summary>
38
64
 
39
- 5. **Enhanced observability:** Equipped with sophisticated instrumentation mechanisms, the library enables detailed logging and tracing without compromising clarity, even when processes are nested. This ensures the code is both easy to understand and to observe.
65
+ > Equipped with sophisticated instrumentation mechanisms, the library enables detailed logging and tracing without compromising code readability, even when processes are nested.
40
66
 
41
- ### Examples
67
+ </details>
42
68
 
43
- Checkout the [solid-rails-app](https://github.com/solid-process/solid-rails-app) for a full example of how to use `solid-process` in a Rails application. Or take a look at the [examples](examples) folder in this repository.
69
+ <p align="right"><a href="#-table-of-contents-">⬆️ &nbsp;back to top</a></p>
70
+
71
+ ### Examples <!-- omit in toc -->
72
+
73
+ Check out [Solid Rails App](https://github.com/solid-process/solid-rails-app) for a complete example of how to use `solid-process` in a Rails application. [Twelve versions (branches)](https://github.com/solid-process/solid-rails-app?tab=readme-ov-file#-repository-branches) show how the gem can be incrementally integrated, access it to see from simple services/form objects to implementing the ports and adapters (hexagonal) architectural pattern.
74
+
75
+ You can also check the [examples](examples) directory for more simple examples of how to use the gem.
76
+
77
+ <p align="right"><a href="#-table-of-contents-">⬆️ &nbsp;back to top</a></p>
44
78
 
45
79
  ## Installation
46
80
 
47
- Add this line to your application's Gemfile:
81
+ Install the gem and add to the application's Gemfile by executing:
82
+
83
+ $ bundle add solid-process
84
+
85
+ If bundler is not being used to manage dependencies, install the gem by executing:
86
+
87
+ $ gem install solid-process
88
+
89
+ And require it in your code:
90
+
91
+ require 'solid/process'
92
+
93
+ <p align="right"><a href="#-table-of-contents-">⬆️ &nbsp;back to top</a></p>
94
+
95
+ ## The Basic Structure
96
+
97
+ All `Solid::Process` requires at least two things: an `input` and a `call` method.
98
+
99
+ 1. The `input` is a set of attributes needed to perform the work.
100
+ 2. The `#call` method is the entry point and where the work is done.
101
+ - It receives the attributes Hash (symbolized keys), defined by the `input`.
102
+ - It returns a `Success` or `Failure` as the output.
48
103
 
49
104
  ```ruby
50
- gem 'solid-process'
105
+ class User::Creation < Solid::Process
106
+ input do
107
+ # Define the attributes needed to perform the work
108
+ end
109
+
110
+ def call(attributes)
111
+ # Perform the work and return a Success or Failure as the output
112
+ end
113
+ end
51
114
  ```
52
115
 
53
- And then execute:
116
+ #### Example <!-- omit in toc -->
54
117
 
55
- ```bash
56
- $ bundle install
118
+ ```ruby
119
+ class User::Creation < Solid::Process
120
+ input do
121
+ attribute :email
122
+ attribute :password
123
+ attribute :password_confirmation
124
+ end
125
+
126
+ def call(attributes)
127
+ user = User.create(attributes)
128
+
129
+ if user.persisted?
130
+ Success(:user_created, user: user)
131
+ else
132
+ Failure(:user_not_created, user: user)
133
+ end
134
+ end
135
+ end
57
136
  ```
58
137
 
59
- Or install it yourself as:
138
+ <p align="right"><a href="#-table-of-contents-">⬆️ &nbsp;back to top</a></p>
60
139
 
61
- ```bash
62
- $ gem install solid-process
140
+ ### Calling a Process <!-- omit from toc -->
141
+
142
+ To call a process, you can use the `call` method directly, or instantiate the class and call the `#call` method.
143
+
144
+ ```ruby
145
+ ###############
146
+ # Direct call #
147
+ ###############
148
+
149
+ User::Creation.call(email: 'john.doe@email.com', password: 'password', password_confirmation: 'password')
150
+ # => #<Solid::Output::Success type=:user_created value={:user=>#<User id: 1, ...>}>
151
+
152
+ ########################
153
+ # Instantiate and call #
154
+ ########################
155
+
156
+ process = User::Creation.new
157
+
158
+ process.call(email: 'john.doe@email.com', password: 'password', password_confirmation: 'password')
159
+ ```
160
+
161
+ For now, it's essential to know that a process instance is stateful, and because of this, you can call it only once.
162
+
163
+ ```ruby
164
+ process = User::Creation.new
165
+
166
+ input = {email: 'john.doe@email.com', password: 'password', password_confirmation: 'password'}
167
+
168
+ process.call(input)
169
+
170
+ process.call(input)
171
+ # The `User::Creation#output` is already set. Use `.output` to access the result or create a new instance to call again. (Solid::Process::Error)
63
172
  ```
64
173
 
65
- ## Usage
174
+ <p align="right"><a href="#-table-of-contents-">⬆️ &nbsp;back to top</a></p>
175
+
176
+ ## Further Reading
177
+
178
+ 1. [Key Concepts](docs/010_KEY_CONCEPTS.md)
179
+ 2. [Basic Usage](docs/020_BASIC_USAGE.md)
180
+ 3. [Intermediate Usage](docs/030_INTERMEDIATE_USAGE.md)
181
+ 4. [Advanced Usage](docs/040_ADVANCED_USAGE.md)
182
+ 5. [Error Handling](docs/050_ERROR_HANDLING.md)
183
+ 6. [Testing](docs/060_TESTING.md)
184
+ 7. [Instrumentation / Observability](docs/070_INSTRUMENTATION.md)
185
+ 8. [Rails Integration](docs/080_RAILS_INTEGRATION.md)
186
+ 9. [Internal libraries](docs/090_INTERNAL_LIBRARIES.md)
187
+ - Solid::Input
188
+ - Solid::Model
189
+ - Solid::Value
190
+ - ActiveModel validations
191
+ 10. [Ports and Adapters (Hexagonal Architecture)](docs/100_PORTS_AND_ADAPTERS.md)
66
192
 
67
- TODO: Write usage instructions here
193
+ <p align="right"><a href="#-table-of-contents-">⬆️ &nbsp;back to top</a></p>
68
194
 
69
195
  ## Development
70
196
 
71
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake dev` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
197
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake matrix` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
198
+
199
+ ```bash
200
+ # Run full test suite for current Ruby version
201
+ bin/rake matrix
202
+
203
+ # Run tests for a specific Rails version
204
+ bundle exec appraisal rails-8-1 rake test
205
+
206
+ # Run a single test file
207
+ bundle exec appraisal rails-8-1 ruby -Ilib:test test/solid/process/result_test.rb
208
+
209
+ # Lint (Ruby 3.4+)
210
+ bin/rake standard
211
+
212
+ # Clean install + full test suite (useful when switching Ruby versions)
213
+ # asdf set ruby <version>
214
+ bin/matrix
215
+ ```
72
216
 
73
217
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
74
218
 
219
+ <p align="right"><a href="#-table-of-contents-">⬆️ &nbsp;back to top</a></p>
220
+
75
221
  ## Contributing
76
222
 
77
223
  Bug reports and pull requests are welcome on GitHub at https://github.com/solid-process/solid-process. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/solid-process/solid-process/blob/main/CODE_OF_CONDUCT.md).
78
224
 
225
+ <p align="right"><a href="#-table-of-contents-">⬆️ &nbsp;back to top</a></p>
226
+
79
227
  ## License
80
228
 
81
229
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
82
230
 
231
+ <p align="right"><a href="#-table-of-contents-">⬆️ &nbsp;back to top</a></p>
232
+
83
233
  ## Code of Conduct
84
234
 
85
235
  Everyone interacting in the Solid::Process project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/solid-process/solid-process/blob/main/CODE_OF_CONDUCT.md).
236
+
237
+ <p align="right"><a href="#-table-of-contents-">⬆️ &nbsp;back to top</a></p>
238
+
239
+ ## Acknowledgments
240
+
241
+ I want to thank some people who helped me by testing and giving feedback as this project took shape, they are:
242
+
243
+ - [Diego Linhares](https://github.com/diegolinhares) and [Ralf Schmitz Bongiolo](https://github.com/mrbongiolo) they were the brave ones who worked for a few months with the first versions of the ecosystem (it was called B/CDD). Their feedback was essential for improving DX and helped me to pivot some core decisions.
244
+ - [Vitor Avelino](https://github.com/vitoravelino), [Tomás Coêlho](https://github.com/tomascco), [Haroldo Furtado](https://github.com/haroldofurtado) (I could repeat Ralf and Diego again) for the various feedbacks, documentation, API, support and words of encouragement.
245
+
246
+ ## About
247
+
248
+ [Rodrigo Serradura](https://rodrigoserradura.com) created this project. He is the Solid Process creator and has already made similar gems like the [u-case](https://github.com/serradura/u-case) and [kind](https://github.com/serradura/kind). This gem can be used independently, but it also contains essential features that facilitate the adoption of Solid Process (the method) in code.
249
+
250
+ <p align="right"><a href="#-table-of-contents-">⬆️ &nbsp;back to top</a></p>
data/Rakefile CHANGED
@@ -13,12 +13,32 @@ require "appraisal/task"
13
13
 
14
14
  Appraisal::Task.new
15
15
 
16
- require "standard/rake"
17
-
18
- task :dev do
19
- exec "bundle exec appraisal rails-7-1 rake test"
20
-
21
- Rake::Task[:standard].invoke
16
+ require "standard/rake" if RUBY_VERSION >= "3.4"
17
+
18
+ desc "Run the full test suite in all supported Rails versions"
19
+ task :matrix do
20
+ if RUBY_VERSION < "3.1"
21
+ system "bundle exec appraisal rails-6-0 rake test"
22
+ system "bundle exec appraisal rails-6-1 rake test"
23
+ end
24
+
25
+ if RUBY_VERSION >= "2.7" && RUBY_VERSION < "3.4"
26
+ system "bundle exec appraisal rails-7-0 rake test"
27
+ system "bundle exec appraisal rails-7-1 rake test"
28
+ end
29
+
30
+ if RUBY_VERSION >= "3.1" && RUBY_VERSION < "4.0"
31
+ system "bundle exec appraisal rails-7-2 rake test"
32
+ end
33
+
34
+ if RUBY_VERSION >= "3.2" && RUBY_VERSION < "4.0"
35
+ system "bundle exec appraisal rails-8-0 rake test"
36
+ end
37
+
38
+ if RUBY_VERSION >= "3.3"
39
+ system "bundle exec appraisal rails-8-1 rake test"
40
+ system "bundle exec appraisal rails-edge rake test"
41
+ end
42
+
43
+ Rake::Task[:standard].invoke if RUBY_VERSION >= "3.4"
22
44
  end
23
-
24
- task default: %i[test]
@@ -0,0 +1,31 @@
1
+ <small>
2
+
3
+ `Previous` [Table of Contents](../README.md#further-reading) | `Next` [Basic Usage](./020_BASIC_USAGE.md)
4
+
5
+ </small>
6
+
7
+ # The Key Concepts
8
+
9
+ ### What is a process?
10
+
11
+ A sequence of steps or actions to achieve a specific end. In other words, it is a series of steps that produce a result.
12
+
13
+ ### What is a `Solid::Process`?
14
+
15
+ It is a class that encapsulates reusable business logic. Its main goal is to **ACT AS AN ORCHESTRATOR** who knows the order, what to use, and the steps necessary to produce an expected result.
16
+
17
+ ### Emergent Design
18
+
19
+ The business rule is directly coupled with business needs. We are often unclear about these rules and how they will be implemented as code. Clarity tends to improve over time and after many maintenance cycles.
20
+
21
+ For this reason, this abstraction embraces emerging design, allowing developers to implement code in a basic structure that can evolve and become sophisticated through the learnings obtained over time.
22
+
23
+ ### The Mantra
24
+
25
+ * **Make it Work**, then
26
+ * **Make it Better**, then
27
+ * **Make it Even Better**.
28
+
29
+ Using the emerging design concept, I invite you to embrace this development cycle, write the minimum necessary to implement processes and add more solid-process features based on actual needs.
30
+
31
+ <p align="right"><a href="#the-key-concepts">⬆️ &nbsp;back to top</a></p>
@@ -0,0 +1,46 @@
1
+ <small>
2
+
3
+ `Previous` [Key Concepts](./010_KEY_CONCEPTS.md) | `Next` [Intermediate Usage](./030_INTERMEDIATE_USAGE.md)
4
+
5
+ </small>
6
+
7
+ # Basic Usage
8
+
9
+ **Status:** 🟡 `in-progress`
10
+
11
+ In this section, we will learn how to create a simple process to register a user.
12
+
13
+ ```ruby
14
+ class User::Registration < Solid::Process
15
+ input do
16
+ attribute :email
17
+ attribute :password
18
+ attribute :password_confirmation
19
+ end
20
+
21
+ def call(attributes)
22
+ user = User.new(attributes)
23
+
24
+ return Failure(:invalid_user, user:) if user.invalid?
25
+
26
+ ActiveRecord::Base.transaction do
27
+ user.save!
28
+
29
+ account = Account.create!(uuid: SecureRandom.uuid)
30
+
31
+ account.memberships.create!(user: user, role: :owner)
32
+
33
+ account.task_lists.inbox.create!
34
+
35
+ user.create_token!
36
+ end
37
+
38
+ UserMailer.with(
39
+ user: user,
40
+ token: user.generate_token_for(:email_confirmation)
41
+ ).email_confirmation.deliver_later
42
+
43
+ Success(:user_registered, user: user)
44
+ end
45
+ end
46
+ ```
@@ -0,0 +1,74 @@
1
+ <small>
2
+
3
+ `Previous` [Basic Usage](./020_BASIC_USAGE.md) | `Next` [Advanced Usage](./040_ADVANCED_USAGE.md)
4
+
5
+ </small>
6
+
7
+ # Intermediate Usage
8
+
9
+ **Status:** 🟡 `in-progress`
10
+
11
+ In this section, we will learn how to use steps to express the process in a more structured way.
12
+
13
+ ```ruby
14
+ class User::Registration < Solid::Process
15
+ input do
16
+ attribute :email, :string
17
+ attribute :password, :string
18
+ attribute :password_confirmation, :string
19
+ end
20
+
21
+ def call(attributes)
22
+ rollback_on_failure {
23
+ Given(attributes)
24
+ .and_then(:create_user)
25
+ .and_then(:create_user_account)
26
+ .and_then(:create_user_inbox)
27
+ .and_then(:create_user_token)
28
+ }
29
+ .and_then(:send_email_confirmation)
30
+ .and_expose(:user_registered, [:user])
31
+ end
32
+
33
+ private
34
+
35
+ def create_user(email:, password:, password_confirmation:, **)
36
+ user = User.create(email:, password:, password_confirmation:)
37
+
38
+ return Continue(user:) if user.persisted?
39
+
40
+ input.errors.merge!(user.errors)
41
+
42
+ Failure(:invalid_input, input:)
43
+ end
44
+
45
+ def create_user_account(user:, **)
46
+ account = Account.create!(uuid: SecureRandom.uuid)
47
+
48
+ account.memberships.create!(user:, role: :owner)
49
+
50
+ Continue(account:)
51
+ end
52
+
53
+ def create_user_inbox(account:, **)
54
+ account.task_lists.inbox.create!
55
+
56
+ Continue()
57
+ end
58
+
59
+ def create_user_token(user:, **)
60
+ user.create_token!
61
+
62
+ Continue()
63
+ end
64
+
65
+ def send_email_confirmation(user:, **)
66
+ UserMailer.with(
67
+ user:,
68
+ token: user.generate_token_for(:email_confirmation)
69
+ ).email_confirmation.deliver_later
70
+
71
+ Continue()
72
+ end
73
+ end
74
+ ```
@@ -0,0 +1,96 @@
1
+ <small>
2
+
3
+ `Previous` [Intermediate Usage](./030_INTERMEDIATE_USAGE.md) | `Next` [Error Handling](./050_ERROR_HANDLING.md)
4
+
5
+ </small>
6
+
7
+ # Advanced Usage
8
+
9
+ **Status:** 🟡 `in-progress`
10
+
11
+ In this section, we will learn how to use input normalization and validation, dependencies, and nested processes.
12
+
13
+ ```ruby
14
+ class User::Registration < Solid::Process
15
+ deps do
16
+ attribute :mailer, default: UserMailer
17
+ attribute :token_creation, default: User::Token::Creation
18
+ attribute :task_list_creation, default: Account::Task::List::Creation
19
+ end
20
+
21
+ input do
22
+ attribute :email, :string
23
+ attribute :password, :string
24
+ attribute :password_confirmation, :string
25
+
26
+ before_validation do
27
+ self.email = email.downcase.strip
28
+ end
29
+
30
+ with_options presence: true do
31
+ validates :email, format: User::Email::REGEXP
32
+ validates :password, confirmation: true, length: {minimum: User::Password::MINIMUM_LENGTH}
33
+ end
34
+ end
35
+
36
+ def call(attributes)
37
+ rollback_on_failure {
38
+ Given(attributes)
39
+ .and_then(:check_if_email_is_taken)
40
+ .and_then(:create_user)
41
+ .and_then(:create_user_account)
42
+ .and_then(:create_user_inbox)
43
+ .and_then(:create_user_token)
44
+ }
45
+ .and_then(:send_email_confirmation)
46
+ .and_expose(:user_registered, [:user])
47
+ end
48
+
49
+ private
50
+
51
+ def check_if_email_is_taken(email:, **)
52
+ input.errors.add(:email, "has already been taken") if User.exists?(email:)
53
+
54
+ input.errors.any? ? Failure(:invalid_input, input:) : Continue()
55
+ end
56
+
57
+ def create_user(email:, password:, password_confirmation:, **)
58
+ user = User.create(email:, password:, password_confirmation:)
59
+
60
+ return Continue(user:) if user.persisted?
61
+
62
+ input.errors.merge!(user.errors)
63
+
64
+ Failure(:invalid_input, input:)
65
+ end
66
+
67
+ def create_user_account(user:, **)
68
+ account = Account.create!(uuid: SecureRandom.uuid)
69
+
70
+ account.memberships.create!(user:, role: :owner)
71
+
72
+ Continue(account:)
73
+ end
74
+
75
+ def create_user_inbox(account:, **)
76
+ case deps.task_list_creation.call(account:, inbox: true)
77
+ in Solid::Success(task_list:) then Continue()
78
+ end
79
+ end
80
+
81
+ def create_user_token(user:, **)
82
+ case deps.token_creation.call(user:)
83
+ in Solid::Success(token:) then Continue()
84
+ end
85
+ end
86
+
87
+ def send_email_confirmation(user:, **)
88
+ deps.mailer.with(
89
+ user:,
90
+ token: user.generate_token_for(:email_confirmation)
91
+ ).email_confirmation.deliver_later
92
+
93
+ Continue()
94
+ end
95
+ end
96
+ ```
@@ -0,0 +1,3 @@
1
+ # Error handling
2
+
3
+ **Status:** 🔴 `to-do`
@@ -0,0 +1,3 @@
1
+ # Testing
2
+
3
+ **Status:** 🔴 `to-do`
@@ -0,0 +1,3 @@
1
+ # Instrumentation
2
+
3
+ **Status:** 🔴 `to-do`
@@ -0,0 +1,3 @@
1
+ # Instrumentation
2
+
3
+ **Status:** 🔴 `to-do`
@@ -0,0 +1,3 @@
1
+ # Internal Libraries
2
+
3
+ **Status:** 🔴 `to-do`
@@ -0,0 +1,3 @@
1
+ # Ports and Adapters (Hexagonal Architecture)
2
+
3
+ **Status:** 🔴 `to-do`
@@ -9,7 +9,7 @@ class Solid::Process::BacktraceCleaner < ActiveSupport::BacktraceCleaner
9
9
 
10
10
  private
11
11
 
12
- BLOCKS_PATTERN = /in [`']block in|in `then'|internal:kernel|block \(\d+ levels?\) in/.freeze
12
+ BLOCKS_PATTERN = /in [`']block in|in [`'](?:Kernel#)?then'|internal:kernel|block \(\d+ levels?\) in/.freeze
13
13
 
14
14
  def add_blocks_silencer
15
15
  add_silencer { |line| line.match?(BLOCKS_PATTERN) }
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Solid::Process::EventLogs::BasicLoggerListener
4
- include ActiveSupport::Configurable
5
4
  include Solid::Result::EventLogs::Listener
6
5
 
7
- config_accessor(:logger, :backtrace_cleaner)
6
+ class_attribute :logger, :backtrace_cleaner
8
7
 
9
8
  self.logger = ActiveSupport::Logger.new($stdout)
10
9
  self.backtrace_cleaner = Solid::Process::BacktraceCleaner.new
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Solid
4
4
  class Process
5
- VERSION = "0.4.0"
5
+ VERSION = "0.5.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid-process
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-06-23 00:00:00.000000000 Z
11
+ date: 2026-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: solid-result
@@ -59,11 +59,22 @@ executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
+ - ".claude/CLAUDE.md"
62
63
  - CHANGELOG.md
63
64
  - CODE_OF_CONDUCT.md
64
65
  - LICENSE.txt
65
66
  - README.md
66
67
  - Rakefile
68
+ - docs/010_KEY_CONCEPTS.md
69
+ - docs/020_BASIC_USAGE.md
70
+ - docs/030_INTERMEDIATE_USAGE.md
71
+ - docs/040_ADVANCED_USAGE.md
72
+ - docs/050_ERROR_HANDLING.md
73
+ - docs/060_TESTING.md
74
+ - docs/070_INSTRUMENTATION.md
75
+ - docs/080_RAILS_INTEGRATION.md
76
+ - docs/090_INTERNAL_LIBRARIES.md
77
+ - docs/100_PORTS_AND_ADAPTERS.md
67
78
  - examples/business_processes/.rubocop.yml
68
79
  - examples/business_processes/.ruby-version
69
80
  - examples/business_processes/.standard.yml
@@ -110,7 +121,8 @@ metadata:
110
121
  allowed_push_host: https://rubygems.org
111
122
  homepage_uri: https://github.com/serradura/solid-process
112
123
  source_code_uri: https://github.com/serradura/solid-process
113
- changelog_uri: https://github.com/serradura/solid-process/CHANGELOG.md
124
+ changelog_uri: https://github.com/solid-process/solid-process/blob/main/CHANGELOG.md
125
+ rubygems_mfa_required: 'true'
114
126
  post_install_message:
115
127
  rdoc_options: []
116
128
  require_paths:
@@ -126,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
126
138
  - !ruby/object:Gem::Version
127
139
  version: '0'
128
140
  requirements: []
129
- rubygems_version: 3.5.10
141
+ rubygems_version: 3.2.33
130
142
  signing_key:
131
143
  specification_version: 4
132
144
  summary: Write business logic for Ruby/Rails that scales.