foobara 0.0.130 → 0.0.131
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 +7 -0
- data/projects/in_memory_crud_driver_minimal/src/in_memory_minimal.rb +1 -1
- data/projects/persistence/src/entity_attributes_crud_driver.rb +60 -27
- data/projects/persistence/src/entity_base/transaction/concerns/state_transitions.rb +47 -5
- data/projects/persistence/src/entity_base/transaction/state_machine.rb +4 -1
- data/projects/persistence/src/entity_base/transaction.rb +5 -1
- data/projects/persistence/src/entity_base/transaction_table/concerns/record_tracking.rb +16 -1
- data/projects/persistence/src/entity_base/transaction_table.rb +8 -0
- data/projects/persistence/src/entity_base.rb +52 -7
- data/projects/weak_object_set/src/weak_object_set.rb +48 -14
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c95217bebc01ab27c28cdf572b5e19eb2902fd68b1bb4d7328c093b02cfe8dbf
|
4
|
+
data.tar.gz: 76d32046c22f96ebf9e73d738aa4154fea3edb740e66fab71954abbd8594a35c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 148fe1548cc4731673ffa299fe1d5ef96a8377cdbc613e3aeff989b16efbc250beea9b58f1b3b1727d0b399b5f1925186c1ac97908c8e2bf6113f4ff0cba1835
|
7
|
+
data.tar.gz: e49bad06d6f305b4e1716fd56fa47298028c6b699977e95417e9013c95f3a25a90ca8a9b2d96b8a11c9fb5989b56587ee87d7b719d2397b90d95210168a594ce
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
# [0.0.131] - 2025-06-16
|
2
|
+
|
3
|
+
- Extract InMemoryMinimal crud driver specs to foobara-crud-driver-spec-helpers gem
|
4
|
+
- Better support for nested transactions
|
5
|
+
- Fix buggy actions carried out when committing/rolling back transactions
|
6
|
+
- Fix thread leaks in WeakObjectSet and test suite
|
7
|
+
|
1
8
|
# [0.0.130] - 2025-06-06
|
2
9
|
|
3
10
|
- Support using a proc as an attributes default for lazy evaluation of default values
|
@@ -20,7 +20,7 @@ module Foobara
|
|
20
20
|
# TODO: all multiple record methods should return enumerators and code further up should only use
|
21
21
|
# the lazy enumerator interface... to encourage that/catch bugs we will return lazy enumerators in these
|
22
22
|
# built-in crud drivers
|
23
|
-
def all
|
23
|
+
def all(page_size: nil)
|
24
24
|
records.each_value.lazy
|
25
25
|
end
|
26
26
|
|
@@ -4,6 +4,12 @@ module Foobara
|
|
4
4
|
class EntityAttributesCrudDriver
|
5
5
|
attr_accessor :raw_connection, :tables
|
6
6
|
|
7
|
+
class << self
|
8
|
+
def has_real_transactions?
|
9
|
+
false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
7
13
|
def initialize(connection_or_credentials = nil)
|
8
14
|
self.raw_connection = open_connection(connection_or_credentials)
|
9
15
|
self.tables = {}
|
@@ -31,7 +37,7 @@ module Foobara
|
|
31
37
|
def rollback_transaction(_raw_tx)
|
32
38
|
end
|
33
39
|
|
34
|
-
def
|
40
|
+
def commit_transaction(_raw_tx)
|
35
41
|
end
|
36
42
|
|
37
43
|
def table_for(entity_class)
|
@@ -91,7 +97,7 @@ module Foobara
|
|
91
97
|
# :nocov:
|
92
98
|
end
|
93
99
|
|
94
|
-
def all
|
100
|
+
def all(page_size: nil)
|
95
101
|
# :nocov:
|
96
102
|
raise "subclass responsibility"
|
97
103
|
# :nocov:
|
@@ -161,39 +167,22 @@ module Foobara
|
|
161
167
|
|
162
168
|
def matches_attributes_filter?(attributes, attributes_filter)
|
163
169
|
attributes_filter.all? do |attribute_name_or_path, value|
|
164
|
-
|
170
|
+
# get the model-free type?
|
171
|
+
attribute_type = entity_class.attributes_type.type_at_path(attribute_name_or_path)
|
172
|
+
|
173
|
+
value = restore_attributes(value, attribute_type)
|
165
174
|
|
166
175
|
if attribute_name_or_path.is_a?(::Array)
|
167
176
|
values = DataPath.values_at(attribute_name_or_path, attributes)
|
168
177
|
|
169
178
|
values.any? do |attribute_value|
|
170
|
-
|
179
|
+
restore_attributes(attribute_value, attribute_type) == value
|
171
180
|
end
|
172
181
|
else
|
173
|
-
attribute_value = attributes
|
174
|
-
|
175
|
-
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
def normalize_attribute_filter_value(value)
|
180
|
-
case value
|
181
|
-
when ::Array
|
182
|
-
value.map { |v| normalize_attribute_filter_value(v) }
|
183
|
-
when ::Hash
|
184
|
-
value.to_h do |k, v|
|
185
|
-
[normalize_attribute_filter_value(k), normalize_attribute_filter_value(v)]
|
186
|
-
end
|
187
|
-
when DetachedEntity
|
188
|
-
if value.persisted?
|
189
|
-
normalize_attribute_filter_value(value.primary_key)
|
190
|
-
else
|
191
|
-
value
|
182
|
+
attribute_value = DataPath.value_at(attribute_name_or_path, attributes)
|
183
|
+
attribute_value = restore_attributes(attribute_value, attribute_type)
|
184
|
+
attribute_value == value
|
192
185
|
end
|
193
|
-
when Model
|
194
|
-
normalize_attribute_filter_value(value.attributes)
|
195
|
-
else
|
196
|
-
value
|
197
186
|
end
|
198
187
|
end
|
199
188
|
|
@@ -262,6 +251,50 @@ module Foobara
|
|
262
251
|
def primary_key_attribute
|
263
252
|
entity_class.primary_key_attribute
|
264
253
|
end
|
254
|
+
|
255
|
+
def restore_attributes(object, type = entity_class.attributes_type)
|
256
|
+
if type.extends?(BuiltinTypes[:attributes])
|
257
|
+
object.to_h do |attribute_name, attribute_value|
|
258
|
+
attribute_type = type.type_at_path(attribute_name)
|
259
|
+
[attribute_name.to_sym, restore_attributes(attribute_value, attribute_type)]
|
260
|
+
end
|
261
|
+
elsif type.extends?(BuiltinTypes[:tuple])
|
262
|
+
# TODO: test this code path
|
263
|
+
# :nocov:
|
264
|
+
object.map.with_index do |value, index|
|
265
|
+
element_type = type.element_types[index]
|
266
|
+
restore_attributes(value, element_type)
|
267
|
+
end
|
268
|
+
# :nocov:
|
269
|
+
elsif type.extends?(BuiltinTypes[:array])
|
270
|
+
element_type = type.element_type
|
271
|
+
object.map { |value| restore_attributes(value, element_type) }
|
272
|
+
elsif type.extends?(BuiltinTypes[:entity])
|
273
|
+
if object.is_a?(Model)
|
274
|
+
if object.persisted?
|
275
|
+
object = object.primary_key
|
276
|
+
restore_attributes(object, type.target_class.primary_key_type)
|
277
|
+
else
|
278
|
+
object
|
279
|
+
end
|
280
|
+
else
|
281
|
+
restore_attributes(object, type.target_class.primary_key_type)
|
282
|
+
end
|
283
|
+
elsif type.extends?(BuiltinTypes[:model])
|
284
|
+
if object.is_a?(Model)
|
285
|
+
object = object.attributes
|
286
|
+
end
|
287
|
+
restore_attributes(object, type.element_types)
|
288
|
+
else
|
289
|
+
outcome = type.process_value(object)
|
290
|
+
|
291
|
+
if outcome.success?
|
292
|
+
outcome.result
|
293
|
+
else
|
294
|
+
object
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
265
298
|
end
|
266
299
|
end
|
267
300
|
end
|
@@ -15,12 +15,20 @@ module Foobara
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
def open_nested!(outer_tx)
|
19
|
+
state_machine.open_nested! do
|
20
|
+
self.is_nested = true
|
21
|
+
self.raw_tx = outer_tx.raw_tx
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
18
25
|
def flush!
|
19
26
|
state_machine.flush! do
|
20
27
|
each_table(&:validate!)
|
21
28
|
each_table(&:flush_created!)
|
22
29
|
each_table(&:flush_updated_and_hard_deleted!)
|
23
30
|
end
|
31
|
+
entity_attributes_crud_driver.flush_transaction(raw_tx)
|
24
32
|
rescue => e
|
25
33
|
# :nocov:
|
26
34
|
rollback!(e)
|
@@ -34,6 +42,7 @@ module Foobara
|
|
34
42
|
state_machine.revert! do
|
35
43
|
each_table(&:revert!)
|
36
44
|
end
|
45
|
+
entity_attributes_crud_driver.revert_transaction(raw_tx)
|
37
46
|
rescue => e
|
38
47
|
# :nocov:
|
39
48
|
rollback!(e)
|
@@ -42,11 +51,25 @@ module Foobara
|
|
42
51
|
end
|
43
52
|
|
44
53
|
def commit!
|
54
|
+
return commit_nested! if nested?
|
55
|
+
|
45
56
|
state_machine.commit! do
|
46
|
-
each_table(&:
|
47
|
-
|
48
|
-
each_table(&:
|
49
|
-
|
57
|
+
each_table(&:commit!)
|
58
|
+
entity_attributes_crud_driver.commit_transaction(raw_tx)
|
59
|
+
each_table(&:transaction_closed)
|
60
|
+
end
|
61
|
+
rescue => e
|
62
|
+
# :nocov:
|
63
|
+
rollback!(e)
|
64
|
+
raise
|
65
|
+
# :nocov:
|
66
|
+
end
|
67
|
+
|
68
|
+
def commit_nested!
|
69
|
+
state_machine.commit_nested! do
|
70
|
+
each_table(&:commit!)
|
71
|
+
entity_attributes_crud_driver.flush_transaction(raw_tx)
|
72
|
+
each_table(&:transaction_closed)
|
50
73
|
end
|
51
74
|
rescue => e
|
52
75
|
# :nocov:
|
@@ -61,13 +84,32 @@ module Foobara
|
|
61
84
|
end
|
62
85
|
|
63
86
|
def rollback!(because_of = nil)
|
87
|
+
return rollback_nested!(because_of) if nested?
|
88
|
+
|
64
89
|
state_machine.rollback! do
|
65
90
|
# TODO: raise error if already flushed and if crud_driver doesn't support true transactions
|
66
91
|
entity_attributes_crud_driver.rollback_transaction(raw_tx)
|
67
92
|
each_table(&:rollback!)
|
68
|
-
entity_attributes_crud_driver.close_transaction(raw_tx)
|
69
93
|
end
|
70
94
|
|
95
|
+
each_table(&:transaction_closed)
|
96
|
+
|
97
|
+
if !because_of && (self == entity_base.current_transaction)
|
98
|
+
raise RolledBack, "intentionally rolled back"
|
99
|
+
end
|
100
|
+
rescue
|
101
|
+
state_machine.error! if state_machine.currently_open?
|
102
|
+
raise
|
103
|
+
end
|
104
|
+
|
105
|
+
def rollback_nested!(because_of = nil)
|
106
|
+
state_machine.rollback_nested! do
|
107
|
+
entity_attributes_crud_driver.revert_transaction(raw_tx)
|
108
|
+
each_table(&:revert!)
|
109
|
+
end
|
110
|
+
|
111
|
+
each_table(&:transaction_closed)
|
112
|
+
|
71
113
|
if !because_of && (self == entity_base.current_transaction)
|
72
114
|
raise RolledBack, "intentionally rolled back"
|
73
115
|
end
|
@@ -7,6 +7,7 @@ module Foobara
|
|
7
7
|
set_transition_map({
|
8
8
|
unopened: {
|
9
9
|
open: :open,
|
10
|
+
open_nested: :open,
|
10
11
|
close: :closed
|
11
12
|
},
|
12
13
|
open: {
|
@@ -17,7 +18,9 @@ module Foobara
|
|
17
18
|
# TODO: should we have intermediate states to quickly get out of the open state?
|
18
19
|
rollback: :closed,
|
19
20
|
commit: :closed,
|
20
|
-
error: :closed
|
21
|
+
error: :closed,
|
22
|
+
commit_nested: :closed,
|
23
|
+
rollback_nested: :closed
|
21
24
|
}
|
22
25
|
})
|
23
26
|
end
|
@@ -6,7 +6,7 @@ module Foobara
|
|
6
6
|
include Concerns::EntityCallbackHandling
|
7
7
|
include Concerns::TransactionTracking
|
8
8
|
|
9
|
-
attr_accessor :state_machine, :entity_base, :raw_tx, :tables
|
9
|
+
attr_accessor :state_machine, :entity_base, :raw_tx, :tables, :is_nested
|
10
10
|
|
11
11
|
def initialize(entity_base)
|
12
12
|
self.entity_base = entity_base
|
@@ -162,6 +162,10 @@ module Foobara
|
|
162
162
|
def perform(&)
|
163
163
|
entity_base.using_transaction(self, &)
|
164
164
|
end
|
165
|
+
|
166
|
+
def nested?
|
167
|
+
is_nested
|
168
|
+
end
|
165
169
|
end
|
166
170
|
end
|
167
171
|
end
|
@@ -83,10 +83,25 @@ module Foobara
|
|
83
83
|
end
|
84
84
|
|
85
85
|
def rolled_back
|
86
|
+
closed
|
87
|
+
end
|
88
|
+
|
89
|
+
def committed
|
90
|
+
closed
|
91
|
+
end
|
92
|
+
|
93
|
+
def closed
|
86
94
|
marked_hard_deleted.clear
|
87
95
|
marked_updated.clear
|
88
96
|
marked_created.clear
|
89
|
-
|
97
|
+
marked_loading.clear
|
98
|
+
end
|
99
|
+
|
100
|
+
# We need to clear this one separately. That's because otherwise a different table
|
101
|
+
# might flush and create a thunk if it has an association to this table but we've stopped
|
102
|
+
# tracking the record.
|
103
|
+
def transaction_closed
|
104
|
+
tracked_records.close
|
90
105
|
end
|
91
106
|
|
92
107
|
def reverted
|
@@ -632,6 +632,14 @@ module Foobara
|
|
632
632
|
rolled_back
|
633
633
|
end
|
634
634
|
|
635
|
+
def commit!
|
636
|
+
validate!
|
637
|
+
flush_created!
|
638
|
+
flush_updated_and_hard_deleted!
|
639
|
+
|
640
|
+
committed
|
641
|
+
end
|
642
|
+
|
635
643
|
def revert!
|
636
644
|
# TODO: could pause record tracking while doing this as a performance boost
|
637
645
|
marked_updated.each(&:restore_without_callbacks!)
|
@@ -52,7 +52,31 @@ module Foobara
|
|
52
52
|
Thread.inheritable_thread_local_var_set(transaction_key, transaction)
|
53
53
|
end
|
54
54
|
|
55
|
-
|
55
|
+
# What types of transaction scenarios are there?
|
56
|
+
# 1. If a transaction is already open, use it as a "nested transaction", otherwise, open a new one.
|
57
|
+
# A nested transaction means that "rollback" is the same as "revert" and "commit" is the same as "flush".
|
58
|
+
# For a true
|
59
|
+
# 2. If a transaction is already open, raise an error. otherwise, open a new one.
|
60
|
+
# 3. Open a new, independent transaction, no matter what.
|
61
|
+
# 4. If a transaction is already open, use it, otherwise, open a new one.
|
62
|
+
# 5. We are outside of a transaction but have a handle on one. We want to set it as the current transaction.
|
63
|
+
# and do some work in that transaction.
|
64
|
+
# Which use cases do we probably need at the moment?
|
65
|
+
# 1. If we are running a command calling other commands, we will open transactions when needed but
|
66
|
+
# inherit any already-open transactions. Commands don't commit or flush to the already-open transactions
|
67
|
+
# that they inherit. So this feels like a "use existing" situation or a situation where we don't even
|
68
|
+
# bother calling open_transaction at all. This is the most important use-case. It can be helpful to raise
|
69
|
+
# in this situation because it is not expected that there's an existing transaction yet we're opening another.
|
70
|
+
# 2. We might have a situation where we are in one transaction but definitely want to open a new one and
|
71
|
+
# commit it ourselves and have its results committed and visible independent of the current transaction.
|
72
|
+
# So this feels like a "open new" situation where we don't want to raise an error if a transaction is
|
73
|
+
# already open.
|
74
|
+
VALID_MODES = [
|
75
|
+
:use_existing,
|
76
|
+
:open_nested,
|
77
|
+
:open_new,
|
78
|
+
nil
|
79
|
+
].freeze
|
56
80
|
|
57
81
|
def using_transaction(existing_transaction, &)
|
58
82
|
transaction(existing_transaction:, &)
|
@@ -67,18 +91,28 @@ module Foobara
|
|
67
91
|
|
68
92
|
old_transaction = current_transaction
|
69
93
|
|
70
|
-
if old_transaction
|
94
|
+
if old_transaction && !old_transaction.currently_open?
|
71
95
|
old_transaction = nil
|
72
96
|
end
|
73
97
|
|
74
|
-
|
98
|
+
open_nested = false
|
99
|
+
|
100
|
+
if old_transaction
|
75
101
|
if mode == :use_existing || existing_transaction == old_transaction
|
76
102
|
if block_given?
|
77
103
|
return yield old_transaction
|
78
104
|
else
|
79
105
|
return old_transaction
|
80
106
|
end
|
81
|
-
elsif mode
|
107
|
+
elsif mode == :open_nested
|
108
|
+
open_nested = true
|
109
|
+
elsif mode == :open_new
|
110
|
+
if existing_transaction
|
111
|
+
# :nocov:
|
112
|
+
raise ArgumentError, "Cannot use mode :open_new with existing_transaction:"
|
113
|
+
# :nocov:
|
114
|
+
end
|
115
|
+
else
|
82
116
|
# :nocov:
|
83
117
|
raise "Transaction already open. " \
|
84
118
|
"Use mode :use_existing if you want to make use of the existing transaction. " \
|
@@ -96,16 +130,27 @@ module Foobara
|
|
96
130
|
tx = existing_transaction
|
97
131
|
else
|
98
132
|
tx = Transaction.new(self)
|
99
|
-
|
133
|
+
|
134
|
+
if open_nested
|
135
|
+
tx.open_nested!(old_transaction)
|
136
|
+
else
|
137
|
+
tx.open!
|
138
|
+
end
|
100
139
|
end
|
101
140
|
|
102
141
|
set_current_transaction(tx)
|
142
|
+
|
103
143
|
result = yield tx
|
104
|
-
|
144
|
+
|
145
|
+
if tx.currently_open? && !existing_transaction
|
146
|
+
tx.commit!
|
147
|
+
end
|
105
148
|
result
|
106
149
|
rescue Foobara::Persistence::EntityBase::Transaction::RolledBack # rubocop:disable Lint/SuppressedException
|
107
150
|
rescue => e
|
108
|
-
|
151
|
+
if tx.currently_open?
|
152
|
+
tx.rollback!(e)
|
153
|
+
end
|
109
154
|
raise
|
110
155
|
ensure
|
111
156
|
set_current_transaction(old_transaction)
|
@@ -4,8 +4,6 @@ module Foobara
|
|
4
4
|
# TODO: a possible optimization: have a certain number of records before the Weakref approach kicks in
|
5
5
|
# that way we don't just immediately clear out useful information without any actual memory burden
|
6
6
|
class WeakObjectSet
|
7
|
-
class InvalidWtf < StandardError; end
|
8
|
-
|
9
7
|
class GarbageCleaner
|
10
8
|
attr_accessor :weak_object_set, :deactivated, :queue, :cleanup_thread
|
11
9
|
|
@@ -17,6 +15,12 @@ module Foobara
|
|
17
15
|
end
|
18
16
|
|
19
17
|
def cleanup_proc
|
18
|
+
if deactivated?
|
19
|
+
# :nocov:
|
20
|
+
raise "GarbageCleaner has been deactivated"
|
21
|
+
# :nocov:
|
22
|
+
end
|
23
|
+
|
20
24
|
@cleanup_proc ||= begin
|
21
25
|
queue = self.queue
|
22
26
|
|
@@ -35,6 +39,8 @@ module Foobara
|
|
35
39
|
end
|
36
40
|
|
37
41
|
def start_cleanup_thread
|
42
|
+
queue = self.queue
|
43
|
+
|
38
44
|
self.cleanup_thread = Thread.new do
|
39
45
|
loop do
|
40
46
|
object_id = queue.pop
|
@@ -57,9 +63,16 @@ module Foobara
|
|
57
63
|
end
|
58
64
|
|
59
65
|
def deactivate
|
66
|
+
raise if deactivated?
|
67
|
+
|
60
68
|
self.deactivated = true
|
61
69
|
queue.close
|
70
|
+
# TODO: don't bother to join here outside of test suite
|
62
71
|
cleanup_thread.join # just doing this for test suite/simplecov
|
72
|
+
@cleanup_proc = nil
|
73
|
+
@queue = nil
|
74
|
+
@weak_object_set = nil
|
75
|
+
@cleanup_thread = nil
|
63
76
|
end
|
64
77
|
|
65
78
|
def deactivated?
|
@@ -69,7 +82,7 @@ module Foobara
|
|
69
82
|
|
70
83
|
include Enumerable
|
71
84
|
|
72
|
-
attr_accessor :monitor, :key_method, :key_to_object_id, :object_id_to_key, :objects
|
85
|
+
attr_accessor :monitor, :key_method, :key_to_object_id, :object_id_to_key, :objects, :closed
|
73
86
|
attr_writer :garbage_cleaner
|
74
87
|
|
75
88
|
def initialize(key_method = nil)
|
@@ -136,19 +149,17 @@ module Foobara
|
|
136
149
|
@garbage_cleaner ||= begin
|
137
150
|
queue = Queue.new
|
138
151
|
|
139
|
-
|
140
|
-
|
141
|
-
ObjectSpace.define_finalizer gc do
|
142
|
-
# :nocov:
|
143
|
-
queue.close
|
144
|
-
# :nocov:
|
145
|
-
end
|
146
|
-
|
147
|
-
gc
|
152
|
+
GarbageCleaner.new(self, queue)
|
148
153
|
end
|
149
154
|
end
|
150
155
|
|
151
156
|
def <<(object)
|
157
|
+
if closed?
|
158
|
+
# :nocov:
|
159
|
+
raise "Cannot add objects to a closed WeakObjectSet"
|
160
|
+
# :nocov:
|
161
|
+
end
|
162
|
+
|
152
163
|
object_id = object.object_id
|
153
164
|
|
154
165
|
monitor.synchronize do
|
@@ -235,11 +246,30 @@ module Foobara
|
|
235
246
|
end
|
236
247
|
end
|
237
248
|
|
249
|
+
def close
|
250
|
+
raise if closed?
|
251
|
+
|
252
|
+
self.closed = true
|
253
|
+
stop_garbage_cleaner
|
254
|
+
end
|
255
|
+
|
256
|
+
def stop_garbage_cleaner
|
257
|
+
gc = nil
|
258
|
+
|
259
|
+
monitor.synchronize do
|
260
|
+
if @garbage_cleaner
|
261
|
+
gc = garbage_cleaner
|
262
|
+
self.garbage_cleaner = nil
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
gc&.deactivate
|
267
|
+
end
|
268
|
+
|
238
269
|
def clear
|
239
270
|
monitor.synchronize do
|
240
|
-
|
271
|
+
stop_garbage_cleaner
|
241
272
|
|
242
|
-
self.garbage_cleaner = nil
|
243
273
|
self.objects = {}
|
244
274
|
|
245
275
|
if key_method
|
@@ -248,5 +278,9 @@ module Foobara
|
|
248
278
|
end
|
249
279
|
end
|
250
280
|
end
|
281
|
+
|
282
|
+
def closed?
|
283
|
+
closed
|
284
|
+
end
|
251
285
|
end
|
252
286
|
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.0.
|
4
|
+
version: 0.0.131
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miles Georgi
|
@@ -486,8 +486,8 @@ licenses:
|
|
486
486
|
- MPL-2.0
|
487
487
|
metadata:
|
488
488
|
homepage_uri: https://foobara.com
|
489
|
-
source_code_uri: https://
|
490
|
-
changelog_uri: https://
|
489
|
+
source_code_uri: https://github.com/foobara/foobara
|
490
|
+
changelog_uri: https://github.com/foobara/foobara/blob/main/CHANGELOG.md
|
491
491
|
rubygems_mfa_required: 'true'
|
492
492
|
rdoc_options: []
|
493
493
|
require_paths:
|
@@ -529,7 +529,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
529
529
|
- !ruby/object:Gem::Version
|
530
530
|
version: '0'
|
531
531
|
requirements: []
|
532
|
-
rubygems_version: 3.6.
|
532
|
+
rubygems_version: 3.6.9
|
533
533
|
specification_version: 4
|
534
534
|
summary: A command-centric and discoverable software framework with a focus on domain
|
535
535
|
concepts and abstracting away integration code
|