foobara 0.0.101 → 0.0.103
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 +11 -0
- data/projects/builtin_types/src/attributes/transformers/remove_unexpected_attributes.rb +4 -1
- data/projects/command/src/command_pattern_implementation/concerns/transactions.rb +4 -0
- data/projects/command/src/transformed_command.rb +13 -4
- data/projects/command_connectors/src/command_connector.rb +1 -1
- data/projects/detached_entity/src/concerns/associations.rb +10 -5
- data/projects/foobara/lib/foobara/all.rb +1 -2
- data/projects/model/src/concerns/types.rb +6 -1
- data/projects/model/src/model.rb +4 -2
- data/projects/persistence/src/entity_attributes_crud_driver.rb +27 -16
- data/projects/persistence/src/entity_base/transaction_table.rb +0 -2
- data/projects/persistence/src/entity_base.rb +4 -2
- data/projects/type_declarations/src/type_declarations.rb +5 -3
- data/projects/types/src/type.rb +11 -2
- metadata +15 -4
- data/projects/thread_parent/lib/foobara/thread_parent.rb +0 -1
- data/projects/thread_parent/src/thread_parent.rb +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 506c445dbdf8c1bd6d1a2db990e317e3a8893754aea2f1d607aa03dccc516749
|
4
|
+
data.tar.gz: e7170ed694b6bc096391a98e724080bfeab36b3b2528bd49ad105a1e1f3453fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f5350c1728e42deb835e3d4d32b9ffcc7e5ae56cb4054a0738b9932c82d87d0fb6c608954f5bc0f30b55ecb7f688e6da46af364a80f40ed07c152f0c9ae6e50
|
7
|
+
data.tar.gz: fbadbfb1a7a086101a2b0e02fefb06e15ad12954c816566fadd6b17728a0b27db140d3e737617ace5fd3e80cb56b57752e50caac8d7063ac61a22b13bc44a48c
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
# [0.0.103] - 2025-04-17
|
2
|
+
|
3
|
+
- Fix bugs in complicated entity query calls involving mixtures of models/records/primary keys/attributes
|
4
|
+
- Fix bugs re: .construct_associations/_deep_associations resulting in terribad performance in some projects
|
5
|
+
- Allow lambdas to be used as allowed rules
|
6
|
+
- Improve .delegate_attribute interface
|
7
|
+
|
8
|
+
# [0.0.102] - 2025-04-13
|
9
|
+
|
10
|
+
- Extract ThreadParent to its own repository/gem
|
11
|
+
|
1
12
|
# [0.0.101] - 2025-04-12
|
2
13
|
|
3
14
|
- Add Entity#update
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "inheritable_thread_vars"
|
2
|
+
|
1
3
|
module Foobara
|
2
4
|
module BuiltinTypes
|
3
5
|
module Attributes
|
@@ -10,7 +12,8 @@ module Foobara
|
|
10
12
|
end
|
11
13
|
|
12
14
|
def applicable?(hash)
|
13
|
-
Thread.
|
15
|
+
Thread.inheritable_thread_local_var_get(:foobara_ignore_unexpected_attributes) &&
|
16
|
+
unexpected_attributes(hash).any?
|
14
17
|
end
|
15
18
|
|
16
19
|
def transform(hash)
|
@@ -35,6 +35,10 @@ module Foobara
|
|
35
35
|
entity_classes += Entity.construct_associations(
|
36
36
|
result_type
|
37
37
|
).values.uniq.map(&:target_class)
|
38
|
+
|
39
|
+
if result_type.extends?(BuiltinTypes[:entity])
|
40
|
+
entity_classes << result_type.target_class
|
41
|
+
end
|
38
42
|
end
|
39
43
|
|
40
44
|
entity_classes += entity_classes.uniq.map do |entity_class|
|
@@ -556,9 +556,7 @@ module Foobara
|
|
556
556
|
|
557
557
|
if rule
|
558
558
|
command.after_load_records do |command:, **|
|
559
|
-
|
560
|
-
# TODO: raise exception here if rule.lambda? is true, if this starts becoming a common error
|
561
|
-
is_allowed = instance_eval(&rule)
|
559
|
+
is_allowed = instance_exec(&rule)
|
562
560
|
|
563
561
|
unless is_allowed
|
564
562
|
explanation = allowed_rule.explanation
|
@@ -568,7 +566,18 @@ module Foobara
|
|
568
566
|
end
|
569
567
|
|
570
568
|
if explanation.nil?
|
571
|
-
|
569
|
+
source = begin
|
570
|
+
allowed_rule.block.source
|
571
|
+
rescue MethodSource::SourceNotFoundError
|
572
|
+
# This path is hit if the way the source code is extracted
|
573
|
+
# doesn't result in valid Ruby, for example, as part of a hash such as:
|
574
|
+
# allowed_rule: -> () { whatever?(something) },
|
575
|
+
# :nocov:
|
576
|
+
allowed_rule.block.source_location.join(":")
|
577
|
+
# :nocov:
|
578
|
+
end
|
579
|
+
|
580
|
+
explanation = source || "No explanation."
|
572
581
|
end
|
573
582
|
|
574
583
|
error = CommandConnector::NotAllowedError.new(rule_symbol: rule.symbol, explanation:)
|
@@ -374,7 +374,7 @@ module Foobara
|
|
374
374
|
request_command = request.command
|
375
375
|
|
376
376
|
request_command.after_load_records do |command:, **|
|
377
|
-
authenticated_user = request.
|
377
|
+
authenticated_user = request.instance_exec(&authenticator)
|
378
378
|
|
379
379
|
request_command.authenticated_user = authenticated_user
|
380
380
|
|
@@ -154,8 +154,13 @@ module Foobara
|
|
154
154
|
def construct_associations(
|
155
155
|
type = attributes_type,
|
156
156
|
path = DataPath.new,
|
157
|
-
result = {}
|
157
|
+
result = {},
|
158
|
+
initial: true
|
158
159
|
)
|
160
|
+
if initial && type.extends?(BuiltinTypes[:detached_entity])
|
161
|
+
return construct_associations(type.element_types, path, result, initial: false)
|
162
|
+
end
|
163
|
+
|
159
164
|
remove_sensitive = TypeDeclarations.foobara_manifest_context_remove_sensitive?
|
160
165
|
|
161
166
|
if type.extends?(BuiltinTypes[:entity])
|
@@ -171,7 +176,7 @@ module Foobara
|
|
171
176
|
end
|
172
177
|
|
173
178
|
element_types&.each&.with_index do |element_type, index|
|
174
|
-
construct_associations(element_type, path.append(index), result)
|
179
|
+
construct_associations(element_type, path.append(index), result, initial: false)
|
175
180
|
end
|
176
181
|
elsif type.extends?(BuiltinTypes[:array])
|
177
182
|
# TODO: what to do about an associative array type?? Unclear how to make a key from that...
|
@@ -179,7 +184,7 @@ module Foobara
|
|
179
184
|
element_type = type.element_type
|
180
185
|
|
181
186
|
if element_type && (!remove_sensitive || !element_type.sensitive?)
|
182
|
-
construct_associations(element_type, path.append(:"#"), result)
|
187
|
+
construct_associations(element_type, path.append(:"#"), result, initial: false)
|
183
188
|
end
|
184
189
|
elsif type.extends?(BuiltinTypes[:attributes]) # TODO: matches attributes itself instead of only subtypes
|
185
190
|
type.element_types.each_pair do |attribute_name, element_type|
|
@@ -187,7 +192,7 @@ module Foobara
|
|
187
192
|
next
|
188
193
|
end
|
189
194
|
|
190
|
-
construct_associations(element_type, path.append(attribute_name), result)
|
195
|
+
construct_associations(element_type, path.append(attribute_name), result, initial: false)
|
191
196
|
end
|
192
197
|
elsif type.extends?(BuiltinTypes[:model])
|
193
198
|
target_class = type.target_class
|
@@ -196,7 +201,7 @@ module Foobara
|
|
196
201
|
attributes_type = target_class.send(method)
|
197
202
|
|
198
203
|
if !remove_sensitive || !attributes_type.sensitive?
|
199
|
-
construct_associations(attributes_type, path, result)
|
204
|
+
construct_associations(attributes_type, path, result, initial: false)
|
200
205
|
end
|
201
206
|
elsif type.extends?(BuiltinTypes[:associative_array])
|
202
207
|
# not going to bother testing this for now
|
@@ -173,11 +173,16 @@ module Foobara
|
|
173
173
|
|
174
174
|
def delegate_attributes(delegates)
|
175
175
|
delegates.each_pair do |attribute_name, delegate_info|
|
176
|
-
|
176
|
+
data_path = DataPath.for(delegate_info[:data_path])
|
177
|
+
delegate_attribute(attribute_name, data_path, writer: delegate_info[:writer])
|
177
178
|
end
|
178
179
|
end
|
179
180
|
|
180
181
|
def delegate_attribute(attribute_name, data_path, writer: false)
|
182
|
+
if data_path.is_a?(::Symbol) || data_path.is_a?(::String)
|
183
|
+
data_path = [data_path, attribute_name]
|
184
|
+
end
|
185
|
+
|
181
186
|
data_path = DataPath.for(data_path)
|
182
187
|
|
183
188
|
delegate_manifest = { data_path: data_path.to_s }
|
data/projects/model/src/model.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "inheritable_thread_vars"
|
2
|
+
|
1
3
|
module Foobara
|
2
4
|
# TODO: either make this an abstract base class of ValueModel and Entity or rename it to ValueModel
|
3
5
|
# and have Entity inherit from it...
|
@@ -180,7 +182,7 @@ module Foobara
|
|
180
182
|
end
|
181
183
|
|
182
184
|
if options[:ignore_unexpected_attributes]
|
183
|
-
Thread.
|
185
|
+
Thread.with_inheritable_thread_local_var(:foobara_ignore_unexpected_attributes, true) do
|
184
186
|
initialize(attributes, options.except(:ignore_unexpected_attributes))
|
185
187
|
return
|
186
188
|
end
|
@@ -195,7 +197,7 @@ module Foobara
|
|
195
197
|
# :nocov:
|
196
198
|
end
|
197
199
|
else
|
198
|
-
if Thread.
|
200
|
+
if Thread.inheritable_thread_local_var_get(:foobara_ignore_unexpected_attributes)
|
199
201
|
outcome = attributes_type.process_value(attributes)
|
200
202
|
|
201
203
|
if outcome.success?
|
@@ -1,4 +1,5 @@
|
|
1
1
|
module Foobara
|
2
|
+
# Might be best to rename this to CrudDrivers or CrudDriver instead of Persistence?
|
2
3
|
module Persistence
|
3
4
|
class EntityAttributesCrudDriver
|
4
5
|
attr_accessor :raw_connection, :tables
|
@@ -156,29 +157,39 @@ module Foobara
|
|
156
157
|
|
157
158
|
def matches_attributes_filter?(attributes, attributes_filter)
|
158
159
|
attributes_filter.all? do |attribute_name_or_path, value|
|
159
|
-
|
160
|
+
value = normalize_attribute_filter_value(value)
|
160
161
|
|
161
162
|
if attribute_name_or_path.is_a?(::Array)
|
162
163
|
values = DataPath.values_at(attribute_name_or_path, attributes)
|
163
164
|
|
164
|
-
|
165
|
-
|
166
|
-
else
|
167
|
-
type ||= entity_class.model_type.type_at_path(attribute_name_or_path)
|
168
|
-
if type.extends?(:detached_entity)
|
169
|
-
values.any? do |v|
|
170
|
-
value.primary_key == v
|
171
|
-
end
|
172
|
-
end
|
165
|
+
values.any? do |attribute_value|
|
166
|
+
normalize_attribute_filter_value(attribute_value) == value
|
173
167
|
end
|
174
|
-
elsif attributes[attribute_name_or_path] == value
|
175
|
-
true
|
176
168
|
else
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
169
|
+
attribute_value = attributes[attribute_name_or_path]
|
170
|
+
normalize_attribute_filter_value(attribute_value) == value
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def normalize_attribute_filter_value(value)
|
176
|
+
case value
|
177
|
+
when ::Array
|
178
|
+
value.map { |v| normalize_attribute_filter_value(v) }
|
179
|
+
when ::Hash
|
180
|
+
value.to_h do |k, v|
|
181
|
+
[normalize_attribute_filter_value(k), normalize_attribute_filter_value(v)]
|
182
|
+
end
|
183
|
+
when DetachedEntity
|
184
|
+
if value.persisted?
|
185
|
+
normalize_attribute_filter_value(value.primary_key)
|
186
|
+
else
|
187
|
+
value
|
181
188
|
end
|
189
|
+
when Model
|
190
|
+
normalize_attribute_filter_value(value.attributes)
|
191
|
+
else
|
192
|
+
value
|
182
193
|
end
|
183
194
|
end
|
184
195
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "inheritable_thread_vars"
|
2
|
+
|
1
3
|
module Foobara
|
2
4
|
module Persistence
|
3
5
|
class EntityBase
|
@@ -43,11 +45,11 @@ module Foobara
|
|
43
45
|
end
|
44
46
|
|
45
47
|
def current_transaction
|
46
|
-
Thread.
|
48
|
+
Thread.inheritable_thread_local_var_get(transaction_key)
|
47
49
|
end
|
48
50
|
|
49
51
|
def set_current_transaction(transaction)
|
50
|
-
Thread.
|
52
|
+
Thread.inheritable_thread_local_var_set(transaction_key, transaction)
|
51
53
|
end
|
52
54
|
|
53
55
|
VALID_MODES = [:use_existing, :open_nested, :open_new, nil].freeze
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "inheritable_thread_vars"
|
2
|
+
|
1
3
|
module Foobara
|
2
4
|
require_project_file("type_declarations", "type_builder")
|
3
5
|
require_project_file("type_declarations", "error_extension")
|
@@ -91,7 +93,7 @@ module Foobara
|
|
91
93
|
end
|
92
94
|
|
93
95
|
def foobara_manifest_context
|
94
|
-
Thread.
|
96
|
+
Thread.inheritable_thread_local_var_get("foobara_manifest_context")
|
95
97
|
end
|
96
98
|
|
97
99
|
allowed_context_keys = %i[detached to_include mode remove_sensitive]
|
@@ -117,10 +119,10 @@ module Foobara
|
|
117
119
|
old_context = foobara_manifest_context
|
118
120
|
begin
|
119
121
|
new_context = (old_context || {}).merge(context)
|
120
|
-
Thread.
|
122
|
+
Thread.inheritable_thread_local_var_set("foobara_manifest_context", new_context)
|
121
123
|
yield
|
122
124
|
ensure
|
123
|
-
Thread.
|
125
|
+
Thread.inheritable_thread_local_var_set("foobara_manifest_context", old_context)
|
124
126
|
end
|
125
127
|
end
|
126
128
|
end
|
data/projects/types/src/type.rb
CHANGED
@@ -20,8 +20,6 @@ module Foobara
|
|
20
20
|
:validators,
|
21
21
|
:element_processors,
|
22
22
|
:structure_count,
|
23
|
-
:element_types,
|
24
|
-
:element_type,
|
25
23
|
:is_builtin,
|
26
24
|
:raw_declaration_data,
|
27
25
|
:name,
|
@@ -33,6 +31,9 @@ module Foobara
|
|
33
31
|
|
34
32
|
attr_reader :type_symbol
|
35
33
|
|
34
|
+
attr_writer :element_types,
|
35
|
+
:element_type
|
36
|
+
|
36
37
|
def initialize(
|
37
38
|
*,
|
38
39
|
target_classes:,
|
@@ -83,6 +84,14 @@ module Foobara
|
|
83
84
|
sensitive_exposed
|
84
85
|
end
|
85
86
|
|
87
|
+
def element_type
|
88
|
+
@element_type || base_type&.element_type
|
89
|
+
end
|
90
|
+
|
91
|
+
def element_types
|
92
|
+
@element_types || base_type&.element_types
|
93
|
+
end
|
94
|
+
|
86
95
|
def has_sensitive_types?
|
87
96
|
return true if sensitive?
|
88
97
|
|
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.0.
|
4
|
+
version: 0.0.103
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miles Georgi
|
@@ -51,6 +51,20 @@ dependencies:
|
|
51
51
|
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: 0.0.11
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: inheritable-thread-vars
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 0.0.1
|
61
|
+
type: :runtime
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 0.0.1
|
54
68
|
description: A command-centric and discoverable software framework with a focus on
|
55
69
|
domain concepts and abstracting away integration code
|
56
70
|
email:
|
@@ -364,8 +378,6 @@ files:
|
|
364
378
|
- projects/state_machine/src/sugar.rb
|
365
379
|
- projects/state_machine/src/transition_log.rb
|
366
380
|
- projects/state_machine/src/validations.rb
|
367
|
-
- projects/thread_parent/lib/foobara/thread_parent.rb
|
368
|
-
- projects/thread_parent/src/thread_parent.rb
|
369
381
|
- projects/type_declarations/lib/foobara/type_declarations.rb
|
370
382
|
- projects/type_declarations/src/attributes.rb
|
371
383
|
- projects/type_declarations/src/attributes_transformers/only.rb
|
@@ -471,7 +483,6 @@ require_paths:
|
|
471
483
|
- "./projects/namespace/lib"
|
472
484
|
- "./projects/persistence/lib"
|
473
485
|
- "./projects/state_machine/lib"
|
474
|
-
- "./projects/thread_parent/lib"
|
475
486
|
- "./projects/type_declarations/lib"
|
476
487
|
- "./projects/types/lib"
|
477
488
|
- "./projects/value/lib"
|
@@ -1 +0,0 @@
|
|
1
|
-
module Foobara::ThreadParent; end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
module Foobara
|
2
|
-
module ThreadParent
|
3
|
-
module ThreadClassExtensions
|
4
|
-
def new(...)
|
5
|
-
super.tap { |thread| thread.instance_variable_set("@foobara_parent", Thread.current) }
|
6
|
-
end
|
7
|
-
end
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
class Thread
|
12
|
-
class << self
|
13
|
-
prepend(Foobara::ThreadParent::ThreadClassExtensions)
|
14
|
-
|
15
|
-
def foobara_var_get(...)
|
16
|
-
Thread.current.foobara_var_get(...)
|
17
|
-
end
|
18
|
-
|
19
|
-
def foobara_var_set(...)
|
20
|
-
Thread.current.foobara_var_set(...)
|
21
|
-
end
|
22
|
-
|
23
|
-
def foobara_with_var(...)
|
24
|
-
Thread.current.foobara_with_var(...)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
attr_reader :foobara_parent
|
29
|
-
|
30
|
-
# NOTE: because there's not a way to unset a thread variable, storing nil is used as deletion.
|
31
|
-
# this means that a thread local variable with nil can't have any semantic meaning and should be
|
32
|
-
# treated the same as if #thread_variable? had returned false.
|
33
|
-
def foobara_var_get(...)
|
34
|
-
value = thread_variable_get(...)
|
35
|
-
|
36
|
-
value.nil? ? foobara_parent&.foobara_var_get(...) : value
|
37
|
-
end
|
38
|
-
|
39
|
-
def foobara_var_set(...)
|
40
|
-
thread_variable_set(...)
|
41
|
-
end
|
42
|
-
|
43
|
-
def foobara_with_var(key, value, &block)
|
44
|
-
old_value = foobara_var_get(key)
|
45
|
-
|
46
|
-
begin
|
47
|
-
foobara_var_set(key, value)
|
48
|
-
block.call
|
49
|
-
ensure
|
50
|
-
foobara_var_set(key, old_value)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|