foobara 0.2.6 → 0.3.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 +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +20 -7
- data/projects/builtin_types/lib/foobara/builtin_types.rb +1 -0
- data/projects/builtin_types/src/attributes/supported_processors/element_type_declarations.rb +1 -3
- data/projects/builtin_types/src/builtin_types.rb +6 -4
- data/projects/builtin_types/src/email/transformers/downcase.rb +0 -2
- data/projects/callback/src/block/concerns/keyword_argumentable_block.rb +2 -0
- data/projects/command/src/command_pattern_implementation/concerns/namespace.rb +0 -2
- data/projects/command/src/command_pattern_implementation/concerns/reflection.rb +0 -2
- data/projects/command_connectors/src/command_connector/concerns/reflection.rb +187 -0
- data/projects/command_connectors/src/command_connector.rb +89 -255
- data/projects/command_connectors/src/command_registry/exposed_command.rb +6 -4
- data/projects/command_connectors/src/command_registry.rb +50 -48
- data/projects/command_connectors/src/serializers/atomic_serializer.rb +4 -1
- data/projects/command_connectors/src/transformed_command.rb +104 -100
- data/projects/command_connectors/src/transformers/load_atoms_transformer.rb +2 -0
- data/projects/command_connectors/src/transformers/load_delegated_attributes_entities_pre_commit_transformer.rb +2 -0
- data/projects/detached_entity/lib/foobara/detached_entity.rb +2 -0
- data/projects/detached_entity/src/remove_sensitive_values_transformer_extensions.rb +64 -0
- data/projects/domain/src/domain_module_extension.rb +0 -21
- data/projects/model/src/concerns/types.rb +1 -0
- data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/to_type_transformer.rb +1 -1
- data/projects/model_attribute_helpers/src/attribute_helpers.rb +4 -4
- data/projects/namespace/src/is_namespace.rb +1 -1
- data/projects/namespace/src/namespace/lookup_mode.rb +6 -0
- data/projects/nested_transactionable/lib/foobara/nested_transactionable.rb +0 -2
- data/projects/persistence/src/entity_base/transaction/concerns/state_transitions.rb +0 -2
- data/projects/persistence/src/entity_base.rb +1 -0
- data/projects/type_declarations/lib/foobara/type_declarations.rb +3 -1
- data/projects/type_declarations/src/remove_sensitive_values_transformer.rb +0 -59
- data/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1889d43f30de1d85f60a3a6245feffbed013f4a2ad2b357fd9b61209e815ccd3
|
|
4
|
+
data.tar.gz: e57dfcd26cfb93c4f96084a61e33325644bddfd2d5d592f19e9c608f285244fe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cd88755c0131cab9ee13fc46b38cafc779e5799aae3c33a8289198a809af9f9a021565ccaadc41d05b5a2c02aed22aad4e9ca0b3b664c0447e488ab8993c939e
|
|
7
|
+
data.tar.gz: b9c4090428c31817a87498f261a50a986594d90e8395dfd2250bd56c963373c44d10a8faa27ec0de7500c764ebfd90b6b0ee04d523d4cd7f061041727305e903
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
# [0.3.0] - 2025-11-23
|
|
2
|
+
|
|
3
|
+
- Fix bug caused by exposing errors with organizations as parents
|
|
4
|
+
- Restructure code in some projects and make many more methods private
|
|
5
|
+
|
|
6
|
+
# [0.2.7] - 2025-11-10
|
|
7
|
+
|
|
8
|
+
- Fix CommandConnector authorization bug that can fail to close transactions
|
|
9
|
+
|
|
1
10
|
# [0.2.6] - 2025-10-30
|
|
2
11
|
|
|
3
12
|
- Add a set inputs transformer
|
data/README.md
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
|
-
Foobara is a software framework
|
|
2
|
-
domain operations in commands, and automatically expose machine-readable formal metadata about those
|
|
3
|
-
commands so that integration code can be decoupled and abstracted away.
|
|
1
|
+
Foobara is a software framework that is command-centric and discoverable.
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Domain operations are
|
|
4
|
+
encapsulated in commands, which serve as the public interface to systems, and which automatically
|
|
5
|
+
provide machine-readable formal metadata about those commands.
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
This metadata makes your domain discoverable and can be used to abstract away integrations,
|
|
8
|
+
such as HTTP, CLI, MCP, whatever. It also makes your domain logic
|
|
9
|
+
forward-compatible with integrations you didn't know you needed or that might not even have
|
|
10
|
+
existed yet when you built your commands.
|
|
11
|
+
|
|
12
|
+
This metadata is also used to help export commands from one system and import them into another
|
|
13
|
+
as remote commands. This means you can write your domain logic and automatically get a Ruby SDK or Typescript SDK for
|
|
14
|
+
free.
|
|
15
|
+
|
|
16
|
+
This also helps with rearchitecting efforts since a remote command has the same interface
|
|
17
|
+
as the local command. Domain logic refactors are not required to relocate domain logic
|
|
18
|
+
in/out of other systems. And this also helps a system communicate and impose its mental model
|
|
19
|
+
on consuming systems.
|
|
20
|
+
|
|
21
|
+
You can use Foobara as a full standalone framework or you can use it with existing code
|
|
22
|
+
as a service-objects layer and leverage the integration features of Foobara when/if necessary.
|
|
10
23
|
|
|
11
24
|
<!-- TOC -->
|
|
12
25
|
* [Overview of Features/Concepts/Goals](#overview-of-featuresconceptsgoals)
|
data/projects/builtin_types/src/attributes/supported_processors/element_type_declarations.rb
CHANGED
|
@@ -35,9 +35,7 @@ module Foobara
|
|
|
35
35
|
return Outcome.error(
|
|
36
36
|
build_error(
|
|
37
37
|
attributes_hash,
|
|
38
|
-
message: "Unexpected attributes #{
|
|
39
|
-
unexpected_attributes
|
|
40
|
-
}. Expected only #{allowed_attributes}",
|
|
38
|
+
message: "Unexpected attributes #{unexpected_attributes}. Expected only #{allowed_attributes}",
|
|
41
39
|
context: {
|
|
42
40
|
unexpected_attributes:,
|
|
43
41
|
allowed_attributes:
|
|
@@ -5,10 +5,6 @@ module Foobara
|
|
|
5
5
|
|
|
6
6
|
module BuiltinTypes
|
|
7
7
|
class << self
|
|
8
|
-
def global_type_declaration_handler_registry
|
|
9
|
-
TypeDeclarations.global_type_declaration_handler_registry
|
|
10
|
-
end
|
|
11
|
-
|
|
12
8
|
# TODO: break this up
|
|
13
9
|
# TODO: much of this behavior is helpful to non-builtin types as well.
|
|
14
10
|
def build_and_register!(
|
|
@@ -158,6 +154,12 @@ module Foobara
|
|
|
158
154
|
type
|
|
159
155
|
end
|
|
160
156
|
|
|
157
|
+
private
|
|
158
|
+
|
|
159
|
+
def global_type_declaration_handler_registry
|
|
160
|
+
TypeDeclarations.global_type_declaration_handler_registry
|
|
161
|
+
end
|
|
162
|
+
|
|
161
163
|
def install_type_declaration_extensions_for(processor_class)
|
|
162
164
|
extension_module = Util.constant_value(processor_class, :TypeDeclarationExtension)
|
|
163
165
|
|
|
@@ -2,8 +2,6 @@ module Foobara
|
|
|
2
2
|
module BuiltinTypes
|
|
3
3
|
module Email
|
|
4
4
|
module Transformers
|
|
5
|
-
# Seems like it might be cleaner to just assemble these parts in one place instead of in different files?
|
|
6
|
-
# Hard to say.
|
|
7
5
|
class Downcase < BuiltinTypes::String::SupportedTransformers::Downcase
|
|
8
6
|
end
|
|
9
7
|
end
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
module Foobara
|
|
2
|
+
class CommandConnector
|
|
3
|
+
module Concerns
|
|
4
|
+
module Reflection
|
|
5
|
+
include Concern
|
|
6
|
+
|
|
7
|
+
def foobara_manifest
|
|
8
|
+
Namespace.use command_registry do
|
|
9
|
+
foobara_manifest_in_current_namespace
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# TODO: figure out how this is used
|
|
14
|
+
def all_exposed_type_names
|
|
15
|
+
# TODO: cache this or better yet cache #foobara_manifest
|
|
16
|
+
foobara_manifest[:type].keys.sort.map(&:to_s)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
# TODO: try to break this giant method up
|
|
22
|
+
def foobara_manifest_in_current_namespace
|
|
23
|
+
process_delayed_connections
|
|
24
|
+
|
|
25
|
+
to_include = Set.new
|
|
26
|
+
|
|
27
|
+
to_include << command_registry.global_organization
|
|
28
|
+
to_include << command_registry.global_domain
|
|
29
|
+
|
|
30
|
+
command_registry.foobara_each_command(
|
|
31
|
+
mode: Namespace::LookupMode::ABSOLUTE_SINGLE_NAMESPACE
|
|
32
|
+
) do |exposed_command|
|
|
33
|
+
to_include << exposed_command
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
included = Set.new
|
|
37
|
+
|
|
38
|
+
additional_to_include = Set.new
|
|
39
|
+
|
|
40
|
+
h = {
|
|
41
|
+
organization: {},
|
|
42
|
+
domain: {},
|
|
43
|
+
type: {},
|
|
44
|
+
command: {},
|
|
45
|
+
error: {}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if TypeDeclarations.include_processors?
|
|
49
|
+
h.merge!(
|
|
50
|
+
processor: {},
|
|
51
|
+
processor_class: {}
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
TypeDeclarations.with_manifest_context(to_include: additional_to_include, remove_sensitive: true) do
|
|
56
|
+
until to_include.empty? && additional_to_include.empty?
|
|
57
|
+
object = nil
|
|
58
|
+
|
|
59
|
+
if to_include.empty?
|
|
60
|
+
until additional_to_include.empty?
|
|
61
|
+
o = additional_to_include.first
|
|
62
|
+
additional_to_include.delete(o)
|
|
63
|
+
|
|
64
|
+
if o.is_a?(::Module)
|
|
65
|
+
if o.foobara_domain? || o.foobara_organization?
|
|
66
|
+
unless o.foobara_root_namespace == command_registry
|
|
67
|
+
next
|
|
68
|
+
end
|
|
69
|
+
elsif o.is_a?(::Class) && o < Foobara::Command
|
|
70
|
+
next
|
|
71
|
+
end
|
|
72
|
+
elsif o.is_a?(Types::Type)
|
|
73
|
+
if o.sensitive?
|
|
74
|
+
# :nocov:
|
|
75
|
+
raise UnexpectedSensitiveTypeInManifestError,
|
|
76
|
+
"Unexpected sensitive type in manifest: #{o.scoped_full_path}. " \
|
|
77
|
+
"Make sure these are not included."
|
|
78
|
+
# :nocov:
|
|
79
|
+
else
|
|
80
|
+
|
|
81
|
+
mode = Namespace::LookupMode::ABSOLUTE_SINGLE_NAMESPACE
|
|
82
|
+
domain_name = o.foobara_domain.scoped_full_name
|
|
83
|
+
|
|
84
|
+
exposed_domain = command_registry.foobara_lookup_domain(domain_name, mode:)
|
|
85
|
+
|
|
86
|
+
exposed_domain ||= command_registry.build_and_register_exposed_domain(domain_name)
|
|
87
|
+
|
|
88
|
+
# Since we don't know which other domains/orgs creating this domain might have created,
|
|
89
|
+
# we will just add them all to be included just in case
|
|
90
|
+
command_registry.foobara_all_domain(mode:).each do |exposed_domain|
|
|
91
|
+
additional_to_include << exposed_domain
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
command_registry.foobara_all_organization(mode:).each do |exposed_organization|
|
|
95
|
+
additional_to_include << exposed_organization
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
object = o
|
|
101
|
+
break
|
|
102
|
+
end
|
|
103
|
+
else
|
|
104
|
+
object = to_include.first
|
|
105
|
+
to_include.delete(object)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
break unless object
|
|
109
|
+
next if included.include?(object)
|
|
110
|
+
|
|
111
|
+
manifest_reference = object.foobara_manifest_reference.to_sym
|
|
112
|
+
|
|
113
|
+
category_symbol = command_registry.foobara_category_symbol_for(object)
|
|
114
|
+
|
|
115
|
+
unless category_symbol
|
|
116
|
+
# :nocov:
|
|
117
|
+
raise "no category symbol for #{object}"
|
|
118
|
+
# :nocov:
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
namespace = if object.is_a?(Types::Type)
|
|
122
|
+
object.created_in_namespace
|
|
123
|
+
else
|
|
124
|
+
Foobara::Namespace.current
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# TODO: do we really need to enter the namespace here for this?
|
|
128
|
+
h[category_symbol][manifest_reference] = Foobara::Namespace.use namespace do
|
|
129
|
+
object.foobara_manifest
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
included << object
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
h[:domain].each_value do |domain_manifest|
|
|
137
|
+
# TODO: hack, we need to trim types down to what is actually included in this manifest
|
|
138
|
+
domain_manifest[:types] = domain_manifest[:types].select do |type_name|
|
|
139
|
+
h[:type].key?(type_name.to_sym)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
h = normalize_manifest(h)
|
|
144
|
+
patch_up_broken_parents_for_errors_with_missing_command_parents(h)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def normalize_manifest(manifest_hash)
|
|
148
|
+
manifest_hash.map do |key, entries|
|
|
149
|
+
[key, entries.sort.to_h]
|
|
150
|
+
end.sort.to_h
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def patch_up_broken_parents_for_errors_with_missing_command_parents(manifest_hash)
|
|
154
|
+
root_manifest = Manifest::RootManifest.new(manifest_hash)
|
|
155
|
+
|
|
156
|
+
error_category = {}
|
|
157
|
+
|
|
158
|
+
root_manifest.errors.each do |error|
|
|
159
|
+
error_manifest = if (error.parent_category == :command || error.parent_category == :organization) &&
|
|
160
|
+
!root_manifest.contains?(error.parent_name, error.parent_category)
|
|
161
|
+
domain = error.domain
|
|
162
|
+
index = domain.scoped_full_path.size
|
|
163
|
+
|
|
164
|
+
fixed_scoped_path = error.scoped_full_path[index..]
|
|
165
|
+
fixed_scoped_name = fixed_scoped_path.join("::")
|
|
166
|
+
fixed_scoped_prefix = fixed_scoped_path[..-2]
|
|
167
|
+
fixed_parent = [:domain, domain.reference]
|
|
168
|
+
|
|
169
|
+
error.relevant_manifest.merge(
|
|
170
|
+
parent: fixed_parent,
|
|
171
|
+
scoped_path: fixed_scoped_path,
|
|
172
|
+
scoped_name: fixed_scoped_name,
|
|
173
|
+
scoped_prefix: fixed_scoped_prefix
|
|
174
|
+
)
|
|
175
|
+
else
|
|
176
|
+
error.relevant_manifest
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
error_category[error.scoped_full_name.to_sym] = error_manifest
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
manifest_hash.merge(error: error_category)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|