solid-process 0.2.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +82 -1
- data/README.md +26 -6
- data/lib/solid/input.rb +0 -2
- data/lib/solid/model.rb +19 -7
- data/lib/solid/output.rb +13 -0
- data/lib/solid/process/backtrace_cleaner.rb +17 -0
- data/lib/solid/process/caller.rb +2 -2
- data/lib/solid/process/event_logs/basic_logger_listener.rb +78 -0
- data/lib/solid/process/event_logs.rb +7 -0
- data/lib/solid/process/version.rb +1 -1
- data/lib/solid/process.rb +15 -10
- data/lib/solid/validators/email_validator.rb +4 -2
- data/lib/solid/validators/id_validator.rb +17 -0
- data/lib/solid/validators/instance_of_validator.rb +5 -3
- data/lib/solid/validators/is_validator.rb +19 -0
- data/lib/solid/validators/kind_of_validator.rb +5 -3
- data/lib/solid/validators/respond_to_validator.rb +5 -3
- data/lib/solid/validators/singleton_validator.rb +8 -8
- data/lib/solid/validators/uuid_validator.rb +4 -4
- data/lib/solid/validators.rb +13 -0
- data/lib/solid/value.rb +41 -0
- metadata +16 -19
- data/lib/solid/result.rb +0 -17
- data/lib/solid/validators/all.rb +0 -12
- data/lib/solid/validators/bool_validator.rb +0 -9
- data/lib/solid/validators/is_a_validator.rb +0 -6
- data/lib/solid/validators/persisted_validator.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 973ae538a96362750bac5c31245c2e8390144c4d67688940fa8b28f2ee27e538
|
4
|
+
data.tar.gz: 057c4bbbd8e2c4fc955c548a714fdb7fbe4396e1b8d624dc7702cc4750e929fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20c3a43c503dfc26560ca25cb20a3d4d53f2014de3f9883bfbd872e7759881fc636da985f335d53f9c9c81b730b797f9759649543b49644ddf2e774222a4d2cc
|
7
|
+
data.tar.gz: 3802c1bab8d942f351099e0510f9cfae3d86b110508cea3d96b61c4bd4eb5953c249b2280d4565bc3b02990744d908f229267edffc88eab38100664bebe83959
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,88 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.4.0] - 2024-06-23
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- Add `Solid::Process.configuration(freeze: true)`.
|
8
|
+
|
9
|
+
- Add `Solid::Process::EventLogs::BasicLoggerListener`.
|
10
|
+
|
11
|
+
- Add `Solid::Process::BacktraceCleaner`.
|
12
|
+
|
13
|
+
- Add `Solid::Process.configure` as an alias to `Solid::Process.configuration`.
|
14
|
+
|
15
|
+
- Add `Solid::Value` to allow defining a value object.
|
16
|
+
|
17
|
+
- `Solid::Model`
|
18
|
+
- Add `after_initialize` callback.
|
19
|
+
- Add `#[]` to access instance attributes.
|
20
|
+
|
21
|
+
- `solid/validators`
|
22
|
+
- Add `id_validator`, to check if the value is a positive integer or a string that represents a positive integer.
|
23
|
+
- Add `is_validator`, to check if the value satisfy the given predicate methods.
|
24
|
+
|
25
|
+
### Changed
|
26
|
+
|
27
|
+
- Replace the usage of `deep_symbolize_keys` with `symbolize_keys` to perform the call more efficiently.
|
28
|
+
|
29
|
+
- Change `email_validator` and `uuid_validator` to use `I18n` messages.
|
30
|
+
|
31
|
+
- Relax `ActiveModel` dependency to `>= 6.0`.
|
32
|
+
|
33
|
+
### Removed
|
34
|
+
|
35
|
+
- Remove some validators:
|
36
|
+
- `bool_validator`
|
37
|
+
- `is_a_validator`
|
38
|
+
- `persisted_validator`
|
39
|
+
|
40
|
+
## [0.3.0] - 2024-04-14
|
41
|
+
|
42
|
+
### Changed
|
43
|
+
|
44
|
+
- Replace `bcdd-result` gem `solid-result` gem. This change removes the constant aliases `Solid::Output` and `Solid::Result`, as they are no longer needed.
|
45
|
+
|
3
46
|
## [0.2.0] - 2024-03-18
|
4
47
|
|
48
|
+
### Added
|
49
|
+
|
50
|
+
- Add `Solid::Process#success?`, `Solid::Process#failure?` that delegate to `Solid::Process#output.success?` and `Solid::Process#output.failure?`.
|
51
|
+
|
52
|
+
- Add `Solid::Process#with` method to create a new process instance with new dependencies.
|
53
|
+
|
54
|
+
- Add `Solid::Process#new` method. It's similar to `Solid::Process#with` but does not require passing the dependencies. If nothing is passed, it will use the same dependencies.
|
55
|
+
|
56
|
+
- Add `rescue_from` (from `::ActiveSupport::Rescuable`) method to `Solid::Process` to rescue exceptions and return a `Solid::Output`.
|
57
|
+
|
58
|
+
- Add `Solid::Process.config` and `Solid::Process.configuration` methods to define a configuration for all processes.
|
59
|
+
|
60
|
+
### Changed
|
61
|
+
|
62
|
+
- Move `Solid::Input` features to `Solid::Model` module. This change does not promote breaking changes.
|
63
|
+
|
5
64
|
## [0.1.0] - 2024-03-16
|
6
65
|
|
7
|
-
|
66
|
+
### Added
|
67
|
+
|
68
|
+
- Add `Solid::Model` module to define a model with attributes and validations.
|
69
|
+
- It includes `ActiveModel::Api`, `ActiveModel::Access`, `ActiveModel::Attributes`, `ActiveModel::Dirty`, `ActiveModel::Validations::Callbacks`.
|
70
|
+
|
71
|
+
- Add `Solid::Input` class (which includes `Solid::Model`) to define an input object with attributes and validations.
|
72
|
+
|
73
|
+
- Add `Solid::Output` and `Solid::Result` as constant aliases of `BCDD::Context`.
|
74
|
+
|
75
|
+
- Add `Solid::Success()` and `Solid::Failure()` methods to create `BCDD::Context` instances.
|
76
|
+
|
77
|
+
- Add `Solid::Process` class to be inherited and define a process with inputs, outputs, and steps.
|
78
|
+
|
79
|
+
- Add several ActiveModel validations to be used in `Solid::Model` and `Solid::Input` classes.
|
80
|
+
- `bool_validator`
|
81
|
+
- `email_validator`
|
82
|
+
- `instance_of_validator`
|
83
|
+
- `is_a_validator`
|
84
|
+
- `kind_of_validator`
|
85
|
+
- `persisted_validator`
|
86
|
+
- `respond_to_validator`
|
87
|
+
- `singleton_validator`
|
88
|
+
- `uuid_validator`
|
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
<p align="center">
|
2
|
-
<h1 align="center" id="-solidprocess"
|
3
|
-
<p align="center"><i>
|
2
|
+
<h1 align="center" id="-solidprocess">⚛️ Solid::Process</h1>
|
3
|
+
<p align="center"><i>Write business logic for Ruby/Rails that scales.</i></p>
|
4
4
|
<p align="center">
|
5
|
-
<a href="https://codeclimate.com/github/
|
6
|
-
<a href="https://codeclimate.com/github/
|
5
|
+
<a href="https://codeclimate.com/github/solid-process/solid-process/maintainability"><img src="https://api.codeclimate.com/v1/badges/643a53e99bb591321c9f/maintainability" /></a>
|
6
|
+
<a href="https://codeclimate.com/github/solid-process/solid-process/test_coverage"><img src="https://api.codeclimate.com/v1/badges/643a53e99bb591321c9f/test_coverage" /></a>
|
7
7
|
<img src="https://img.shields.io/badge/Ruby%20%3E%3D%202.7%2C%20%3C%3D%20Head-ruby.svg?colorA=444&colorB=333" alt="Ruby">
|
8
8
|
<img src="https://img.shields.io/badge/Rails%20%3E%3D%206.0%2C%20%3C%3D%20Edge-rails.svg?colorA=444&colorB=333" alt="Rails">
|
9
9
|
</p>
|
@@ -22,6 +22,26 @@ This library is tested against:
|
|
22
22
|
| 3.3 | ✅ | ✅ | ✅ | ✅ | ✅ |
|
23
23
|
| Head | | | | ✅ | ✅ |
|
24
24
|
|
25
|
+
## Introduction
|
26
|
+
|
27
|
+
`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
|
+
|
29
|
+
**Key Objectives:**
|
30
|
+
|
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.
|
32
|
+
|
33
|
+
2. **Support progressive mastery:** Offers an intuitive entry point for novices while providing robust, advanced features that cater to experienced developers.
|
34
|
+
|
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.
|
36
|
+
|
37
|
+
4. **Minimize technical debt:** Facilitate smoother transitions and updates as your application expands and your team size increases.
|
38
|
+
|
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.
|
40
|
+
|
41
|
+
### Examples
|
42
|
+
|
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.
|
44
|
+
|
25
45
|
## Installation
|
26
46
|
|
27
47
|
Add this line to your application's Gemfile:
|
@@ -54,7 +74,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
54
74
|
|
55
75
|
## Contributing
|
56
76
|
|
57
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
77
|
+
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).
|
58
78
|
|
59
79
|
## License
|
60
80
|
|
@@ -62,4 +82,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
62
82
|
|
63
83
|
## Code of Conduct
|
64
84
|
|
65
|
-
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/
|
85
|
+
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).
|
data/lib/solid/input.rb
CHANGED
data/lib/solid/model.rb
CHANGED
@@ -5,6 +5,16 @@ require_relative "model/access"
|
|
5
5
|
module Solid::Model
|
6
6
|
extend ::ActiveSupport::Concern
|
7
7
|
|
8
|
+
module ClassMethods
|
9
|
+
def [](...)
|
10
|
+
new(...)
|
11
|
+
end
|
12
|
+
|
13
|
+
def inherited(subclass)
|
14
|
+
subclass.include(::Solid::Model)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
8
18
|
included do
|
9
19
|
include ::ActiveModel.const_defined?(:Api, false) ? ::ActiveModel::Api : ::ActiveModel::Model
|
10
20
|
include ::ActiveModel.const_defined?(:Access, false) ? ::ActiveModel::Access : ::Solid::Model::Access
|
@@ -12,19 +22,21 @@ module Solid::Model
|
|
12
22
|
include ::ActiveModel::Attributes
|
13
23
|
include ::ActiveModel::Dirty
|
14
24
|
include ::ActiveModel::Validations::Callbacks
|
25
|
+
|
26
|
+
extend ActiveModel::Callbacks
|
27
|
+
|
28
|
+
define_model_callbacks :initialize, only: :after
|
15
29
|
end
|
16
30
|
|
17
|
-
|
18
|
-
|
19
|
-
new(...)
|
20
|
-
end
|
31
|
+
def initialize(...)
|
32
|
+
super
|
21
33
|
|
22
|
-
|
23
|
-
subclass.include(::Solid::Model)
|
24
|
-
end
|
34
|
+
run_callbacks(:initialize)
|
25
35
|
end
|
26
36
|
|
27
37
|
def inspect
|
28
38
|
"#<#{self.class.name} #{attributes.map { |k, v| "#{k}=#{v.inspect}" }.join(" ")}>"
|
29
39
|
end
|
40
|
+
|
41
|
+
alias_method :[], :public_send
|
30
42
|
end
|
data/lib/solid/output.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Solid::Process::BacktraceCleaner < ActiveSupport::BacktraceCleaner
|
4
|
+
def initialize
|
5
|
+
super
|
6
|
+
|
7
|
+
add_blocks_silencer
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
BLOCKS_PATTERN = /in [`']block in|in `then'|internal:kernel|block \(\d+ levels?\) in/.freeze
|
13
|
+
|
14
|
+
def add_blocks_silencer
|
15
|
+
add_silencer { |line| line.match?(BLOCKS_PATTERN) }
|
16
|
+
end
|
17
|
+
end
|
data/lib/solid/process/caller.rb
CHANGED
@@ -9,14 +9,14 @@ class Solid::Process
|
|
9
9
|
self.input = arg
|
10
10
|
|
11
11
|
run_callbacks(:call) do
|
12
|
-
::
|
12
|
+
::Solid::Result.event_logs(name: self.class.name) do
|
13
13
|
self.output =
|
14
14
|
if dependencies&.invalid?
|
15
15
|
Failure(:invalid_dependencies, dependencies: dependencies)
|
16
16
|
elsif input.invalid?
|
17
17
|
Failure(:invalid_input, input: input)
|
18
18
|
else
|
19
|
-
super(input.attributes.
|
19
|
+
super(input.attributes.symbolize_keys)
|
20
20
|
end
|
21
21
|
rescue ::Exception => exception
|
22
22
|
rescue_with_handler(exception) || raise
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Solid::Process::EventLogs::BasicLoggerListener
|
4
|
+
include ActiveSupport::Configurable
|
5
|
+
include Solid::Result::EventLogs::Listener
|
6
|
+
|
7
|
+
config_accessor(:logger, :backtrace_cleaner)
|
8
|
+
|
9
|
+
self.logger = ActiveSupport::Logger.new($stdout)
|
10
|
+
self.backtrace_cleaner = Solid::Process::BacktraceCleaner.new
|
11
|
+
|
12
|
+
module MessagesNesting
|
13
|
+
MAP_STEP_METHOD = lambda do |record_result|
|
14
|
+
kind, type, value = record_result.values_at(:kind, :type, :value)
|
15
|
+
|
16
|
+
value_keys = "#{value.keys.join(":, ")}:"
|
17
|
+
value_keys = "" if value_keys == ":"
|
18
|
+
|
19
|
+
case type
|
20
|
+
when :_given_ then "Given(#{value_keys})"
|
21
|
+
when :_continue_ then "Continue(#{value_keys})"
|
22
|
+
else "#{kind.capitalize}(:#{type}, #{value_keys})"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
MAP_STEP_MESSAGE = lambda do |record|
|
27
|
+
step = MAP_STEP_METHOD[record[:result]]
|
28
|
+
|
29
|
+
method_name = record.dig(:and_then, :method_name)
|
30
|
+
|
31
|
+
" * #{step} from method: #{method_name}".chomp("from method: ").chomp(" ")
|
32
|
+
end
|
33
|
+
|
34
|
+
MAP_IDS_WITH_MESSAGES = lambda do |records|
|
35
|
+
process_ids = []
|
36
|
+
|
37
|
+
records.each_with_object([]) do |record, messages|
|
38
|
+
current = record[:current]
|
39
|
+
|
40
|
+
current_id = current[:id]
|
41
|
+
|
42
|
+
unless process_ids.include?(current_id)
|
43
|
+
process_ids << current_id
|
44
|
+
|
45
|
+
id, name, desc = current.values_at(:id, :name, :desc)
|
46
|
+
|
47
|
+
messages << [current_id, "##{id} #{name} - #{desc}".chomp("- ").chomp(" ")]
|
48
|
+
end
|
49
|
+
|
50
|
+
messages << [current_id, MAP_STEP_MESSAGE[record]]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.map(event_logs)
|
55
|
+
ids_level_parent = event_logs.dig(:metadata, :ids, :level_parent)
|
56
|
+
|
57
|
+
messages = MAP_IDS_WITH_MESSAGES[event_logs[:records]]
|
58
|
+
|
59
|
+
messages.map { |(id, msg)| "#{" " * ids_level_parent[id].first}#{msg}" }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def on_finish(event_logs:)
|
64
|
+
messages = MessagesNesting.map(event_logs)
|
65
|
+
|
66
|
+
logger.info messages.join("\n")
|
67
|
+
end
|
68
|
+
|
69
|
+
def before_interruption(exception:, event_logs:)
|
70
|
+
messages = MessagesNesting.map(event_logs)
|
71
|
+
|
72
|
+
logger.info messages.join("\n")
|
73
|
+
|
74
|
+
cleaned_backtrace = backtrace_cleaner.clean(exception.backtrace).join("\n ")
|
75
|
+
|
76
|
+
logger.error "\nException:\n #{exception.message} (#{exception.class})\n\nBacktrace:\n #{cleaned_backtrace}"
|
77
|
+
end
|
78
|
+
end
|
data/lib/solid/process.rb
CHANGED
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
require "active_support/all"
|
4
4
|
require "active_model"
|
5
|
-
require "bcdd/result"
|
6
5
|
|
7
6
|
module Solid
|
8
|
-
|
9
|
-
|
7
|
+
require_relative "model"
|
8
|
+
require_relative "value"
|
9
|
+
require_relative "input"
|
10
|
+
require_relative "output"
|
10
11
|
|
11
12
|
class Process
|
12
13
|
require "solid/process/version"
|
@@ -16,12 +17,14 @@ module Solid
|
|
16
17
|
require "solid/process/callbacks"
|
17
18
|
require "solid/process/class_methods"
|
18
19
|
require "solid/process/active_record"
|
20
|
+
require "solid/process/backtrace_cleaner"
|
21
|
+
require "solid/process/event_logs"
|
19
22
|
|
20
23
|
extend ClassMethods
|
21
24
|
|
22
25
|
include Callbacks
|
23
26
|
include ::ActiveSupport::Rescuable
|
24
|
-
include ::
|
27
|
+
include ::Solid::Output.mixin(config: {addon: {continue: true}})
|
25
28
|
|
26
29
|
def self.inherited(subclass)
|
27
30
|
super
|
@@ -33,15 +36,17 @@ module Solid
|
|
33
36
|
new.call(arg)
|
34
37
|
end
|
35
38
|
|
36
|
-
def self.
|
39
|
+
def self.config
|
40
|
+
Config.instance
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.configuration(freeze: true, &block)
|
37
44
|
yield config
|
38
45
|
|
39
|
-
config.freeze
|
46
|
+
config.tap { _1.freeze if freeze }
|
40
47
|
end
|
41
48
|
|
42
|
-
|
43
|
-
Config.instance
|
44
|
-
end
|
49
|
+
singleton_class.alias_method :configure, :configuration
|
45
50
|
|
46
51
|
attr_reader :output, :input, :dependencies
|
47
52
|
|
@@ -120,7 +125,7 @@ module Solid
|
|
120
125
|
def output=(result)
|
121
126
|
output_already_set! unless output.nil?
|
122
127
|
|
123
|
-
raise Error, "The result #{result.inspect} must be a
|
128
|
+
raise Error, "The result #{result.inspect} must be a Solid::Output." unless result.is_a?(::Solid::Output)
|
124
129
|
|
125
130
|
@output = result
|
126
131
|
end
|
@@ -1,9 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class EmailValidator < ActiveModel::EachValidator
|
4
|
-
def validate_each(
|
4
|
+
def validate_each(model, attribute, value)
|
5
|
+
return model.errors.add(attribute, :blank, **options) if value.blank?
|
6
|
+
|
5
7
|
return if value.is_a?(String) && URI::MailTo::EMAIL_REGEXP.match?(value)
|
6
8
|
|
7
|
-
|
9
|
+
model.errors.add(attribute, :invalid, **options)
|
8
10
|
end
|
9
11
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "solid/validators"
|
4
|
+
|
5
|
+
class IdValidator < ActiveModel::EachValidator
|
6
|
+
OPTIONS = {only_integer: true, greater_than: 0}.freeze
|
7
|
+
|
8
|
+
def validate_each(model, attribute, value)
|
9
|
+
opts = OPTIONS.merge(options.except(*OPTIONS.keys))
|
10
|
+
|
11
|
+
opts[:attributes] = attribute
|
12
|
+
|
13
|
+
::ActiveModel::Validations::NumericalityValidator.new(opts).validate_each(model, attribute, value)
|
14
|
+
end
|
15
|
+
|
16
|
+
private_constant :OPTIONS
|
17
|
+
end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "solid/validators"
|
4
|
+
|
3
5
|
class InstanceOfValidator < ActiveModel::EachValidator
|
4
|
-
def validate_each(
|
6
|
+
def validate_each(model, attribute, value)
|
5
7
|
with_option = Array.wrap(options[:with] || options[:in])
|
6
8
|
|
7
9
|
return if with_option.any? { |type| value.instance_of?(type) }
|
8
10
|
|
9
|
-
|
11
|
+
message = "is not an instance of #{with_option.map(&:name).join(" | ")}"
|
10
12
|
|
11
|
-
|
13
|
+
Solid::Validators.add_error(model, attribute, message, options)
|
12
14
|
end
|
13
15
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "solid/validators"
|
4
|
+
|
5
|
+
class IsValidator < ActiveModel::EachValidator
|
6
|
+
def validate_each(model, attribute, value)
|
7
|
+
with_option = Array.wrap(options[:with] || options[:in])
|
8
|
+
|
9
|
+
return if with_option.all? do |predicate|
|
10
|
+
raise ArgumentError, "expected a predicate method, got #{predicate.inspect}" unless predicate.end_with?("?")
|
11
|
+
|
12
|
+
value.try(predicate)
|
13
|
+
end
|
14
|
+
|
15
|
+
message = "does not satisfy the predicate#{"s" if with_option.size > 1}: #{with_option.join(" & ")}"
|
16
|
+
|
17
|
+
Solid::Validators.add_error(model, attribute, message, options)
|
18
|
+
end
|
19
|
+
end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "solid/validators"
|
4
|
+
|
3
5
|
class KindOfValidator < ActiveModel::EachValidator
|
4
|
-
def validate_each(
|
6
|
+
def validate_each(model, attribute, value)
|
5
7
|
with_option = Array.wrap(options[:with] || options[:in])
|
6
8
|
|
7
9
|
return if with_option.any? { |type| value.is_a?(type) }
|
8
10
|
|
9
|
-
|
11
|
+
message = "is not a #{with_option.map(&:name).join(" | ")}"
|
10
12
|
|
11
|
-
|
13
|
+
Solid::Validators.add_error(model, attribute, message, options)
|
12
14
|
end
|
13
15
|
end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "solid/validators"
|
4
|
+
|
3
5
|
class RespondToValidator < ActiveModel::EachValidator
|
4
|
-
def validate_each(
|
6
|
+
def validate_each(model, attribute, value)
|
5
7
|
with_option = Array.wrap(options[:with] || options[:in])
|
6
8
|
|
7
9
|
return if with_option.all? { value.respond_to?(_1) }
|
8
10
|
|
9
|
-
|
11
|
+
message = "does not respond to #{with_option.map(&:inspect).join(" & ")}"
|
10
12
|
|
11
|
-
|
13
|
+
Solid::Validators.add_error(model, attribute, message, options)
|
12
14
|
end
|
13
15
|
end
|
@@ -1,21 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "solid/validators"
|
4
|
+
|
3
5
|
class SingletonValidator < ActiveModel::EachValidator
|
4
|
-
def validate_each(
|
6
|
+
def validate_each(model, attribute, value)
|
5
7
|
with_option = Array.wrap(options[:with] || options[:in])
|
6
8
|
|
7
|
-
unless value.is_a?(Module)
|
8
|
-
return obj.errors.add(attribute, options[:message] || "is not a class or module")
|
9
|
-
end
|
9
|
+
return model.errors.add(attribute, options[:message] || "is not a class or module") unless value.is_a?(Module)
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
return if with_option.any? do |type|
|
12
|
+
raise ArgumentError, "#{type.inspect} is not a class or module" unless type.is_a?(Module)
|
13
13
|
|
14
14
|
value == type || (value < type || value.is_a?(type))
|
15
15
|
end
|
16
16
|
|
17
|
-
|
17
|
+
message = "is not #{with_option.map(&:name).join(" | ")}"
|
18
18
|
|
19
|
-
|
19
|
+
Solid::Validators.add_error(model, attribute, message, options)
|
20
20
|
end
|
21
21
|
end
|
@@ -5,16 +5,16 @@ class UuidValidator < ActiveModel::EachValidator
|
|
5
5
|
CASE_SENSITIVE = /\A#{PATTERN}\z/.freeze
|
6
6
|
CASE_INSENSITIVE = /\A#{PATTERN}\z/i.freeze
|
7
7
|
|
8
|
-
def validate_each(
|
8
|
+
def validate_each(model, attribute, value)
|
9
9
|
case_sensitive = options.fetch(:case_sensitive, true)
|
10
10
|
|
11
|
+
return model.errors.add(attribute, :blank, **options) if value.blank?
|
12
|
+
|
11
13
|
regexp = case_sensitive ? CASE_SENSITIVE : CASE_INSENSITIVE
|
12
14
|
|
13
15
|
return if value.is_a?(String) && value.match?(regexp)
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
obj.errors.add(attribute, message)
|
17
|
+
model.errors.add(attribute, :invalid, **options)
|
18
18
|
end
|
19
19
|
|
20
20
|
private_constant :PATTERN
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solid
|
4
|
+
module Validators
|
5
|
+
def self.add_error(model, attribute, message, options)
|
6
|
+
if ActiveModel.const_defined?(:Error)
|
7
|
+
model.errors.add(attribute, **options.merge(message: message))
|
8
|
+
else
|
9
|
+
model.errors.add(attribute, options.fetch(:message, message))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/solid/value.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solid::Value
|
4
|
+
module ClassMethods
|
5
|
+
UNDEFINED = ::Object.new
|
6
|
+
|
7
|
+
def new(value = UNDEFINED)
|
8
|
+
return value if value.is_a?(self)
|
9
|
+
|
10
|
+
UNDEFINED.equal?(value) ? super() : super(value: value)
|
11
|
+
end
|
12
|
+
|
13
|
+
def attribute(...)
|
14
|
+
super(:value, ...)
|
15
|
+
end
|
16
|
+
|
17
|
+
def validates(...)
|
18
|
+
super(:value, ...)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.included(subclass)
|
23
|
+
subclass.include Solid::Model
|
24
|
+
subclass.extend ClassMethods
|
25
|
+
subclass.attribute
|
26
|
+
end
|
27
|
+
|
28
|
+
def ==(other)
|
29
|
+
other.is_a?(self.class) && other.value == value
|
30
|
+
end
|
31
|
+
|
32
|
+
def hash
|
33
|
+
value.hash
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
value.to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
alias_method :eql?, :==
|
41
|
+
end
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solid-process
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.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-
|
11
|
+
date: 2024-06-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: solid-result
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '2.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activemodel
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -31,9 +31,6 @@ dependencies:
|
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '6.0'
|
34
|
-
- - "<"
|
35
|
-
- !ruby/object:Gem::Version
|
36
|
-
version: '8.0'
|
37
34
|
type: :runtime
|
38
35
|
prerelease: false
|
39
36
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -41,9 +38,6 @@ dependencies:
|
|
41
38
|
- - ">="
|
42
39
|
- !ruby/object:Gem::Version
|
43
40
|
version: '6.0'
|
44
|
-
- - "<"
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: '8.0'
|
47
41
|
- !ruby/object:Gem::Dependency
|
48
42
|
name: appraisal
|
49
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,7 +52,7 @@ dependencies:
|
|
58
52
|
- - "~>"
|
59
53
|
- !ruby/object:Gem::Version
|
60
54
|
version: '2.5'
|
61
|
-
description:
|
55
|
+
description: Write business logic for Ruby/Rails that scales.
|
62
56
|
email:
|
63
57
|
- rodrigo.serradura@gmail.com
|
64
58
|
executables: []
|
@@ -87,25 +81,28 @@ files:
|
|
87
81
|
- lib/solid/input.rb
|
88
82
|
- lib/solid/model.rb
|
89
83
|
- lib/solid/model/access.rb
|
84
|
+
- lib/solid/output.rb
|
90
85
|
- lib/solid/process.rb
|
91
86
|
- lib/solid/process/active_record.rb
|
87
|
+
- lib/solid/process/backtrace_cleaner.rb
|
92
88
|
- lib/solid/process/callbacks.rb
|
93
89
|
- lib/solid/process/caller.rb
|
94
90
|
- lib/solid/process/class_methods.rb
|
95
91
|
- lib/solid/process/config.rb
|
96
92
|
- lib/solid/process/error.rb
|
93
|
+
- lib/solid/process/event_logs.rb
|
94
|
+
- lib/solid/process/event_logs/basic_logger_listener.rb
|
97
95
|
- lib/solid/process/version.rb
|
98
|
-
- lib/solid/
|
99
|
-
- lib/solid/validators/all.rb
|
100
|
-
- lib/solid/validators/bool_validator.rb
|
96
|
+
- lib/solid/validators.rb
|
101
97
|
- lib/solid/validators/email_validator.rb
|
98
|
+
- lib/solid/validators/id_validator.rb
|
102
99
|
- lib/solid/validators/instance_of_validator.rb
|
103
|
-
- lib/solid/validators/
|
100
|
+
- lib/solid/validators/is_validator.rb
|
104
101
|
- lib/solid/validators/kind_of_validator.rb
|
105
|
-
- lib/solid/validators/persisted_validator.rb
|
106
102
|
- lib/solid/validators/respond_to_validator.rb
|
107
103
|
- lib/solid/validators/singleton_validator.rb
|
108
104
|
- lib/solid/validators/uuid_validator.rb
|
105
|
+
- lib/solid/value.rb
|
109
106
|
homepage: https://github.com/serradura/solid-process
|
110
107
|
licenses:
|
111
108
|
- MIT
|
@@ -129,8 +126,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
126
|
- !ruby/object:Gem::Version
|
130
127
|
version: '0'
|
131
128
|
requirements: []
|
132
|
-
rubygems_version: 3.
|
129
|
+
rubygems_version: 3.5.10
|
133
130
|
signing_key:
|
134
131
|
specification_version: 4
|
135
|
-
summary:
|
132
|
+
summary: Write business logic for Ruby/Rails that scales.
|
136
133
|
test_files: []
|
data/lib/solid/result.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Solid
|
4
|
-
Output = ::BCDD::Context
|
5
|
-
|
6
|
-
Result = ::BCDD::Context
|
7
|
-
Success = Result::Success
|
8
|
-
Failure = Result::Failure
|
9
|
-
|
10
|
-
def self.Success(...)
|
11
|
-
Result::Success(...)
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.Failure(...)
|
15
|
-
Result::Failure(...)
|
16
|
-
end
|
17
|
-
end
|
data/lib/solid/validators/all.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "instance_of_validator"
|
4
|
-
require_relative "is_a_validator"
|
5
|
-
require_relative "kind_of_validator"
|
6
|
-
require_relative "respond_to_validator"
|
7
|
-
require_relative "singleton_validator"
|
8
|
-
|
9
|
-
require_relative "bool_validator"
|
10
|
-
require_relative "email_validator"
|
11
|
-
require_relative "persisted_validator"
|
12
|
-
require_relative "uuid_validator"
|
@@ -1,9 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class PersistedValidator < ActiveModel::EachValidator
|
4
|
-
def validate_each(record, attribute, value)
|
5
|
-
return if (options[:allow_nil] && value.nil?) || value.try(:persisted?)
|
6
|
-
|
7
|
-
record.errors.add(attribute, (options[:message] || "must be persisted"))
|
8
|
-
end
|
9
|
-
end
|