foobara 0.1.1 → 0.1.2
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 +6 -0
- data/projects/command_connectors/src/command_connector.rb +39 -14
- data/projects/command_connectors/src/transformed_command.rb +33 -35
- data/projects/common/src/possible_error.rb +27 -21
- data/projects/detached_entity/src/detached_entity_type.rb +10 -0
- data/projects/domain/src/is_manifestable.rb +19 -7
- data/projects/type_declarations/src/type_declarations.rb +6 -83
- data/projects/types/src/type.rb +8 -5
- data/projects/value/src/processor.rb +0 -6
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 772c1b159a33ecf7e3f63a1a2a70d8574d986be541bf3ac88cd00a87c8fc8036
|
4
|
+
data.tar.gz: 94ccbde2bafcb9860d7275083659cd8f62f76f8d7bc27b03fcfa2d63e3e085ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a78ea31d30819836a421d49413f2ce379262dac03bf2cc4db2d779c74cc98ea4580f48bd7b472e5d473e3edf269f31af17927adc0248f1ecd7436127585b4e21
|
7
|
+
data.tar.gz: cf07fcd19587a5820afb06f4cba5d783f2f340f552f64fdb8a577bd5794f139e513828ddb97a8263f41c1d31c104b1d34bb1dfec751da3aa0da43d75c78d660d
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# [0.1.2] - 2025-08-24
|
2
|
+
|
3
|
+
- Exclude processor/processor_class categories from manifest by default
|
4
|
+
- Fix bug where entity types were not converted to detached_entity types in manifests
|
5
|
+
- Shorten cast_to declarations by using references when possible
|
6
|
+
|
1
7
|
# [0.1.1] - 2025-08-22
|
2
8
|
|
3
9
|
- Change type declarations for references to registered types to have a structure of `:some_type`
|
@@ -506,11 +506,16 @@ module Foobara
|
|
506
506
|
domain: {},
|
507
507
|
type: {},
|
508
508
|
command: {},
|
509
|
-
error: {}
|
510
|
-
processor: {},
|
511
|
-
processor_class: {}
|
509
|
+
error: {}
|
512
510
|
}
|
513
511
|
|
512
|
+
if TypeDeclarations.include_processors?
|
513
|
+
h.merge!(
|
514
|
+
processor: {},
|
515
|
+
processor_class: {}
|
516
|
+
)
|
517
|
+
end
|
518
|
+
|
514
519
|
TypeDeclarations.with_manifest_context(to_include: additional_to_include, remove_sensitive: true) do
|
515
520
|
until to_include.empty? && additional_to_include.empty?
|
516
521
|
object = nil
|
@@ -536,26 +541,47 @@ module Foobara
|
|
536
541
|
"Make sure these are not included."
|
537
542
|
# :nocov:
|
538
543
|
else
|
544
|
+
mode = Namespace::LookupMode::ABSOLUTE_SINGLE_NAMESPACE
|
539
545
|
domain_name = o.foobara_domain.scoped_full_name
|
540
546
|
|
541
|
-
|
542
|
-
|
543
|
-
|
547
|
+
exposed_domain = command_registry.foobara_lookup_domain(domain_name, mode:)
|
548
|
+
|
549
|
+
unless exposed_domain
|
550
|
+
exposed_domain = command_registry.build_and_register_exposed_domain(domain_name)
|
544
551
|
|
545
552
|
# Since we don't know which other domains/orgs creating this domain might have created,
|
546
553
|
# we will just add them all to be included just in case
|
547
|
-
command_registry.foobara_all_domain(
|
548
|
-
mode: Namespace::LookupMode::ABSOLUTE_SINGLE_NAMESPACE
|
549
|
-
).each do |exposed_domain|
|
554
|
+
command_registry.foobara_all_domain(mode:).each do |exposed_domain|
|
550
555
|
additional_to_include << exposed_domain
|
551
556
|
end
|
552
557
|
|
553
|
-
command_registry.foobara_all_organization(
|
554
|
-
mode: Namespace::LookupMode::ABSOLUTE_SINGLE_NAMESPACE
|
555
|
-
).each do |exposed_organization|
|
558
|
+
command_registry.foobara_all_organization(mode:).each do |exposed_organization|
|
556
559
|
additional_to_include << exposed_organization
|
557
560
|
end
|
558
561
|
end
|
562
|
+
|
563
|
+
if o.extends_type?(BuiltinTypes[:entity])
|
564
|
+
declaration_data = o.declaration_data
|
565
|
+
|
566
|
+
if declaration_data.is_a?(::Hash) && declaration_data[:type] == :entity
|
567
|
+
if o.foobara_root_namespace != command_registry.foobara_root_namespace
|
568
|
+
# Let's swap it out with a detached type
|
569
|
+
detached_entity = command_registry.foobara_lookup_type(o.scoped_full_name, mode:)
|
570
|
+
|
571
|
+
unless detached_entity
|
572
|
+
declaration_data = o.declaration_data.merge(
|
573
|
+
type: :detached_entity,
|
574
|
+
model_base_class: "Foobara::DetachedEntity"
|
575
|
+
)
|
576
|
+
|
577
|
+
detached_entity = exposed_domain.foobara_type_from_declaration(declaration_data)
|
578
|
+
end
|
579
|
+
|
580
|
+
additional_to_include << detached_entity
|
581
|
+
next
|
582
|
+
end
|
583
|
+
end
|
584
|
+
end
|
559
585
|
end
|
560
586
|
end
|
561
587
|
|
@@ -586,9 +612,8 @@ module Foobara
|
|
586
612
|
Foobara::Namespace.current
|
587
613
|
end
|
588
614
|
|
589
|
-
cat = h[category_symbol] ||= {}
|
590
615
|
# TODO: do we really need to enter the namespace here for this?
|
591
|
-
|
616
|
+
h[category_symbol][manifest_reference] = Foobara::Namespace.use namespace do
|
592
617
|
object.foobara_manifest
|
593
618
|
end
|
594
619
|
|
@@ -333,34 +333,22 @@ module Foobara
|
|
333
333
|
end
|
334
334
|
|
335
335
|
def foobara_manifest
|
336
|
-
to_include = TypeDeclarations.foobara_manifest_context_to_include
|
336
|
+
to_include = TypeDeclarations.foobara_manifest_context_to_include || Set.new
|
337
337
|
|
338
338
|
types = types_depended_on.select(&:registered?).map do |t|
|
339
|
-
|
340
|
-
to_include << t
|
341
|
-
end
|
339
|
+
to_include << t
|
342
340
|
t.foobara_manifest_reference
|
343
341
|
end.sort
|
344
342
|
|
345
343
|
inputs_transformers = TypeDeclarations.with_manifest_context(remove_sensitive: false) do
|
346
|
-
self.inputs_transformers
|
344
|
+
processors_to_manifest_symbols(self.inputs_transformers)
|
347
345
|
end
|
348
|
-
result_transformers = self.result_transformers
|
349
|
-
errors_transformers = self.errors_transformers
|
350
|
-
pre_commit_transformers = self.pre_commit_transformers
|
351
|
-
serializers = self.serializers
|
352
|
-
|
353
|
-
|
354
|
-
to_include << s
|
355
|
-
end
|
356
|
-
s.foobara_manifest_reference
|
357
|
-
else
|
358
|
-
{ proc: s.to_s }
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
response_mutators = mutators_to_manifest_symbols(self.response_mutators, to_include:)
|
363
|
-
request_mutators = mutators_to_manifest_symbols(self.request_mutators, to_include:)
|
346
|
+
result_transformers = processors_to_manifest_symbols(self.result_transformers)
|
347
|
+
errors_transformers = processors_to_manifest_symbols(self.errors_transformers)
|
348
|
+
pre_commit_transformers = processors_to_manifest_symbols(self.pre_commit_transformers)
|
349
|
+
serializers = processors_to_manifest_symbols(self.serializers)
|
350
|
+
response_mutators = processors_to_manifest_symbols(self.response_mutators)
|
351
|
+
request_mutators = processors_to_manifest_symbols(self.request_mutators)
|
364
352
|
|
365
353
|
authenticator_details = if authenticator
|
366
354
|
{
|
@@ -404,28 +392,32 @@ module Foobara
|
|
404
392
|
)
|
405
393
|
end
|
406
394
|
|
407
|
-
def
|
408
|
-
return nil if
|
395
|
+
def processors_to_manifest_symbols(processors)
|
396
|
+
return nil if processors.nil? || processors.empty?
|
397
|
+
|
398
|
+
to_include = TypeDeclarations.foobara_manifest_context_to_include || Set.new
|
399
|
+
include_processors = TypeDeclarations.include_processors?
|
409
400
|
|
410
|
-
|
411
|
-
if
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
401
|
+
processors.map do |processor|
|
402
|
+
if processor.respond_to?(:scoped_path_set?) && processor.scoped_path_set?
|
403
|
+
if include_processors
|
404
|
+
to_include << processor
|
405
|
+
end
|
406
|
+
processor.foobara_manifest_reference
|
407
|
+
elsif processor.is_a?(Value::Processor)
|
408
|
+
klass = processor.class
|
416
409
|
|
417
410
|
if klass.scoped_path_set?
|
418
|
-
|
411
|
+
if include_processors
|
412
|
+
to_include << klass
|
413
|
+
end
|
419
414
|
klass.foobara_manifest_reference
|
420
415
|
# TODO: Delete this nocov block
|
421
416
|
# TODO: make anonymous scoped path's have better names instead of random hexadecimal
|
422
417
|
# :nocov:
|
423
|
-
elsif
|
424
|
-
|
418
|
+
elsif processor.respond_to?(:symbol) && processor.symbol
|
419
|
+
processor.symbol
|
425
420
|
else
|
426
|
-
|
427
|
-
to_include << klass if klass.scoped_path_set?
|
428
|
-
|
429
421
|
name = klass.name
|
430
422
|
|
431
423
|
while name.nil?
|
@@ -436,6 +428,12 @@ module Foobara
|
|
436
428
|
"Anonymous#{Util.non_full_name(name)}"
|
437
429
|
# :nocov:
|
438
430
|
end
|
431
|
+
elsif processor.is_a?(::Proc)
|
432
|
+
"Proc"
|
433
|
+
else
|
434
|
+
# :nocov:
|
435
|
+
"Unknown"
|
436
|
+
# :nocov:
|
439
437
|
end
|
440
438
|
end
|
441
439
|
end
|
@@ -48,39 +48,45 @@ module Foobara
|
|
48
48
|
# TODO: technically does not belong in this project but maybe it should
|
49
49
|
def foobara_manifest
|
50
50
|
to_include = TypeDeclarations.foobara_manifest_context_to_include
|
51
|
+
include_processors = TypeDeclarations.include_processors?
|
51
52
|
|
52
53
|
if to_include
|
53
54
|
to_include << error_class
|
54
55
|
end
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
57
|
+
h = key.to_h.merge(
|
58
|
+
key: key.to_s,
|
59
|
+
error: error_class.foobara_manifest_reference,
|
60
|
+
manually_added:
|
61
|
+
)
|
61
62
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
63
|
+
if include_processors
|
64
|
+
if processor
|
65
|
+
processor_class = processor.class
|
66
|
+
if to_include
|
67
|
+
to_include << processor_class
|
68
|
+
end
|
69
|
+
|
70
|
+
if processor.scoped_path_set?
|
71
|
+
# Unclear why nothing in the test suite passes through here.
|
72
|
+
# TODO: either test this or delete it.
|
73
|
+
# :nocov:
|
74
|
+
to_include << processor
|
75
|
+
processor_reference = processor.foobara_manifest_reference
|
76
|
+
# :nocov:
|
77
|
+
end
|
69
78
|
end
|
70
|
-
end
|
71
79
|
|
72
|
-
|
80
|
+
processor_manifest_data = data unless processor_reference
|
73
81
|
|
74
|
-
|
75
|
-
key.to_h.merge(
|
76
|
-
key: key.to_s,
|
77
|
-
error: error_class.foobara_manifest_reference,
|
82
|
+
h.merge!(
|
78
83
|
processor: processor_reference,
|
79
84
|
processor_class: processor_class&.foobara_manifest_reference,
|
80
|
-
processor_manifest_data
|
81
|
-
manually_added:
|
85
|
+
processor_manifest_data:
|
82
86
|
)
|
83
|
-
|
87
|
+
end
|
88
|
+
|
89
|
+
Util.remove_blank(h)
|
84
90
|
end
|
85
91
|
end
|
86
92
|
end
|
@@ -25,11 +25,21 @@ module Foobara
|
|
25
25
|
declaration_data = manifest[:declaration_data]
|
26
26
|
|
27
27
|
if self.class.type_requires_conversion?(declaration_data[:type])
|
28
|
+
# TODO: No longer is hit in this test suite but ActiveRecordType needs this class
|
29
|
+
# and potentially this snippet of code in order to do the right thing.
|
30
|
+
# TODO: test that out and delete this method if possible.
|
31
|
+
# :nocov:
|
28
32
|
declaration_data = declaration_data.merge(type: :detached_entity)
|
33
|
+
# :nocov:
|
29
34
|
end
|
30
35
|
|
31
36
|
if self.class.model_base_class_requires_conversion?(declaration_data[:model_base_class])
|
37
|
+
# TODO: No longer is hit in this test suite but ActiveRecordType needs this class
|
38
|
+
# and potentially this snippet of code in order to do the right thing.
|
39
|
+
# TODO: test that out and delete this method if possible.
|
40
|
+
# :nocov:
|
32
41
|
declaration_data = declaration_data.merge(model_base_class: "Foobara::DetachedEntity")
|
42
|
+
# :nocov:
|
33
43
|
end
|
34
44
|
|
35
45
|
# TODO: remove private attributes, add delegated attributes
|
@@ -38,7 +38,8 @@ module Foobara
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def foobara_manifest
|
41
|
-
to_include = TypeDeclarations.foobara_manifest_context_to_include
|
41
|
+
to_include = TypeDeclarations.foobara_manifest_context_to_include || Set.new
|
42
|
+
include_processors = TypeDeclarations.include_processors?
|
42
43
|
|
43
44
|
h = {
|
44
45
|
scoped_path:,
|
@@ -53,17 +54,28 @@ module Foobara
|
|
53
54
|
organization: foobara_organization&.foobara_manifest_reference
|
54
55
|
}
|
55
56
|
|
56
|
-
|
57
|
+
candidate = scoped_namespace
|
58
|
+
parent = nil
|
57
59
|
|
58
|
-
|
59
|
-
parent_category = Namespace.global.foobara_category_symbol_for(
|
60
|
+
while candidate
|
61
|
+
parent_category = Namespace.global.foobara_category_symbol_for(candidate)
|
62
|
+
break unless parent_category
|
60
63
|
|
61
64
|
if parent_category
|
62
|
-
if
|
63
|
-
|
65
|
+
if include_processors || (parent_category != :processor && parent_category != :processor_class)
|
66
|
+
if candidate != Foobara::Value
|
67
|
+
parent = candidate
|
68
|
+
break
|
69
|
+
end
|
64
70
|
end
|
65
|
-
h[:parent] = [parent_category, parent.foobara_manifest_reference]
|
66
71
|
end
|
72
|
+
|
73
|
+
candidate = candidate.scoped_namespace
|
74
|
+
end
|
75
|
+
|
76
|
+
if parent
|
77
|
+
to_include << parent
|
78
|
+
h[:parent] = [parent_category, parent.foobara_manifest_reference]
|
67
79
|
end
|
68
80
|
|
69
81
|
h
|
@@ -99,6 +99,10 @@ module Foobara
|
|
99
99
|
foobara_manifest_context_mode == Mode::STRINGIFIED || strict_stringified?
|
100
100
|
end
|
101
101
|
|
102
|
+
def include_processors?
|
103
|
+
foobara_manifest_context_include_processors?
|
104
|
+
end
|
105
|
+
|
102
106
|
# TODO: we should desugarize these but can't because of a bug where desugarizing entities results in creating the
|
103
107
|
# entity class in memory, whoops.
|
104
108
|
def declarations_equal?(declaration1, declaration2)
|
@@ -112,8 +116,8 @@ module Foobara
|
|
112
116
|
Thread.inheritable_thread_local_var_get("foobara_manifest_context")
|
113
117
|
end
|
114
118
|
|
115
|
-
allowed_context_keys = [:detached, :to_include, :mode, :remove_sensitive]
|
116
|
-
booleans = [:detached, :remove_sensitive]
|
119
|
+
allowed_context_keys = [:detached, :to_include, :mode, :remove_sensitive, :include_processors]
|
120
|
+
booleans = [:detached, :remove_sensitive, :include_processors]
|
117
121
|
|
118
122
|
booleans.each do |context_item|
|
119
123
|
define_method "foobara_manifest_context_#{context_item}?" do
|
@@ -144,84 +148,3 @@ module Foobara
|
|
144
148
|
end
|
145
149
|
end
|
146
150
|
end
|
147
|
-
|
148
|
-
=begin
|
149
|
-
how many handlers do we need??
|
150
|
-
|
151
|
-
|
152
|
-
find registered type by symbol
|
153
|
-
|
154
|
-
extend registered type (find it by symbol and use it to construct new type with additional processors)
|
155
|
-
|
156
|
-
Duck
|
157
|
-
AtomicDuck
|
158
|
-
Number
|
159
|
-
Integer
|
160
|
-
BigInteger
|
161
|
-
Float
|
162
|
-
BigDecimal
|
163
|
-
String
|
164
|
-
Email
|
165
|
-
Phone
|
166
|
-
Datetime
|
167
|
-
Date
|
168
|
-
Boolean
|
169
|
-
Duckture
|
170
|
-
Array
|
171
|
-
Tuple
|
172
|
-
AssociativeArray
|
173
|
-
Attributes
|
174
|
-
Model
|
175
|
-
Address
|
176
|
-
UsAddress
|
177
|
-
Entity
|
178
|
-
|
179
|
-
Duck (RegisteredTypeExtensionTypeDeclarationHandler)
|
180
|
-
AtomicDuck (N/A)
|
181
|
-
Number (N/A)
|
182
|
-
Integer (RegisteredTypeExtensionTypeDeclarationHandler)
|
183
|
-
BigInteger (RegisteredTypeExtensionTypeDeclarationHandler)
|
184
|
-
Float (RegisteredTypeExtensionTypeDeclarationHandler)
|
185
|
-
BigDecimal (RegisteredTypeExtensionTypeDeclarationHandler)
|
186
|
-
String (RegisteredTypeExtensionTypeDeclarationHandler)
|
187
|
-
Email (implement in terms of string extension)
|
188
|
-
Phone (implement in terms of string extension)
|
189
|
-
Datetime (RegisteredTypeExtensionTypeDeclarationHandler)
|
190
|
-
Date (RegisteredTypeExtensionTypeDeclarationHandler)
|
191
|
-
Boolean (RegisteredTypeExtensionTypeDeclarationHandler)
|
192
|
-
Duckture (N/A)
|
193
|
-
Array (hmmmm we need an element processor initialized with the element type...)
|
194
|
-
Tuple (hmmmm we need an element processor initialized with the elements type...)
|
195
|
-
AssociativeArray (hmmmm we need an element processor initialized with the key type and the value type...)
|
196
|
-
Attributes (we need an element processor initialized with the attribute_types)
|
197
|
-
Model (same as above but need to add a name attribute...)
|
198
|
-
Address (implement in terms of model)
|
199
|
-
UsAddress (implement in terms of Address)
|
200
|
-
Entity (same as above but with a primary key processor of some sort)
|
201
|
-
|
202
|
-
|
203
|
-
TypeDeclarationHandler
|
204
|
-
RegisteredAtomTypeExtensionTypeDeclarationHandler
|
205
|
-
RegisteredStructuredTypeExtensionTypeDeclarationHandler
|
206
|
-
ExtendArrayTypeDeclarationHandler
|
207
|
-
ExtendTupleTypeDeclarationHandler
|
208
|
-
ExtendAssociativeArrayTypeDeclaration
|
209
|
-
ExtendAttributesTypeDeclaration
|
210
|
-
ExtendModelTypeDeclarationHandler
|
211
|
-
ExtendEntityTypeDeclarationHandler
|
212
|
-
|
213
|
-
I think we need these type declarations but not necessarily Type subclasses for all of these types
|
214
|
-
|
215
|
-
Maybe just one Type class??
|
216
|
-
|
217
|
-
let's see...
|
218
|
-
|
219
|
-
for atom could just not support element_types nor size
|
220
|
-
for array element_types could just be applied repeating?? Kind of goofy
|
221
|
-
For tuple element_types could be applied repeating and size validator could be added
|
222
|
-
for associative array... element_types need to be a hash instead of an array. Is that OK?
|
223
|
-
Or could be an array of pairs... like Hash#to_a ? Maybe we just operate off of #each? Seems that will work, wow...
|
224
|
-
For Attributes same as associative array but with key processor of symbol plus an attributes_processor
|
225
|
-
For model add a name processor
|
226
|
-
for entity add a primary key processor of some sort...
|
227
|
-
=end
|
data/projects/types/src/type.rb
CHANGED
@@ -330,7 +330,7 @@ module Foobara
|
|
330
330
|
end
|
331
331
|
|
332
332
|
Value::Processor::Casting.new(
|
333
|
-
{ cast_to:
|
333
|
+
{ cast_to: reference_or_declaration_data },
|
334
334
|
casters:,
|
335
335
|
target_classes:,
|
336
336
|
enforce_unique:
|
@@ -416,6 +416,7 @@ module Foobara
|
|
416
416
|
def foobara_manifest
|
417
417
|
to_include = TypeDeclarations.foobara_manifest_context_to_include
|
418
418
|
remove_sensitive = TypeDeclarations.foobara_manifest_context_remove_sensitive?
|
419
|
+
include_processors = TypeDeclarations.include_processors?
|
419
420
|
|
420
421
|
types = []
|
421
422
|
|
@@ -455,11 +456,13 @@ module Foobara
|
|
455
456
|
h[:sensitive_exposed] = true
|
456
457
|
end
|
457
458
|
|
458
|
-
|
459
|
-
|
460
|
-
|
459
|
+
if include_processors
|
460
|
+
h.merge!(
|
461
|
+
supported_processor_manifest.merge(
|
462
|
+
Util.remove_blank(processors: processor_manifest)
|
463
|
+
)
|
461
464
|
)
|
462
|
-
|
465
|
+
end
|
463
466
|
|
464
467
|
target_classes.sort_by(&:name).each do |target_class|
|
465
468
|
if target_class.respond_to?(:foobara_manifest)
|
@@ -322,12 +322,6 @@ module Foobara
|
|
322
322
|
def foobara_manifest
|
323
323
|
to_include = TypeDeclarations.foobara_manifest_context_to_include
|
324
324
|
|
325
|
-
unless scoped_path_set?
|
326
|
-
if self.class.scoped_path_set?
|
327
|
-
self.scoped_path = self.class.scoped_path
|
328
|
-
end
|
329
|
-
end
|
330
|
-
|
331
325
|
possible_errors = self.possible_errors.map do |possible_error|
|
332
326
|
[possible_error.key.to_s, possible_error.foobara_manifest]
|
333
327
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foobara
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miles Georgi
|
@@ -538,7 +538,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
538
538
|
- !ruby/object:Gem::Version
|
539
539
|
version: '0'
|
540
540
|
requirements: []
|
541
|
-
rubygems_version: 3.
|
541
|
+
rubygems_version: 3.7.1
|
542
542
|
specification_version: 4
|
543
543
|
summary: A command-centric and discoverable software framework with a focus on domain
|
544
544
|
concepts and abstracting away integration code
|