Skip to content

Commit 9a69a6d

Browse files
committed
bug fixes in legacy store and state management
1 parent b183824 commit 9a69a6d

File tree

6 files changed

+35
-10
lines changed

6 files changed

+35
-10
lines changed

ruby/hyper-state/lib/hyperstack/internal/auto_unmount.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def unmount
2424
nil
2525
end
2626
end
27+
remove
2728
@__hyperstack_internal_auto_unmount_unmounted = true
2829
end
2930

ruby/hyper-state/lib/hyperstack/internal/state/mapper.rb

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ def update_objects_to_observe(observer = @current_observer)
130130
def remove(observer = @current_observer)
131131
remove_current_observers_and_objects(observer)
132132
new_objects.delete observer
133+
# see run_delayed_updater for the purpose of @removed_observers
134+
@removed_observers << observer if @removed_observers
133135
end
134136

135137
# Internal (Private) Methods
@@ -166,11 +168,13 @@ def current_objects
166168
# lists by not adding an object hash key unless the object
167169
# already has pending state changes. (See the
168170
# schedule_delayed_updater method below)
171+
169172
def update_exclusions
170173
@update_exclusions ||= Hash.new
171174
end
172175

173176
# remove_current_observers_and_objects clears the hashes between renders
177+
174178
def remove_current_observers_and_objects(observer)
175179
raise 'state management called outside of watch block' unless observer
176180
deleted_objects = current_objects.delete(observer)
@@ -190,6 +194,7 @@ def remove_current_observers_and_objects(observer)
190194
# Hyperstack.on_client? is true WITH ONE EXCEPTION:
191195
# observers can indicate that they need immediate updates in
192196
# case that the object being updated is themselves.
197+
193198
def delay_updates?(object)
194199
@bulk_update_flag ||
195200
(Hyperstack.on_client? &&
@@ -211,21 +216,36 @@ def delay_updates?(object)
211216
# If an object changes state again then the Set will be reinitialized, and all
212217
# the observers that might have been on a previous exclusion list, will now be
213218
# notified.
219+
214220
def schedule_delayed_updater(object)
215221
update_exclusions[object] = Set.new
216-
@delayed_updater ||= after(0) do
217-
current_update_exclusions = @update_exclusions
218-
@update_exclusions = @delayed_updater = nil
219-
observers_to_update(current_update_exclusions).each do |observer, objects|
220-
observer.mutations objects
221-
end
222+
@delayed_updater ||= after(0) { run_delayed_updater }
223+
end
224+
225+
# run_delayed_updater will call the mutations method for each observer passing
226+
# the entire list of objects that changed while waiting for the delay except
227+
# those that the observer has already seen (the exclusion list). The observers
228+
# mutation method may cause some other observer already on the observers_to_update
229+
# list to be removed. To prevent these observers from receiving mutations we keep a
230+
# temporary set of removed_observers. This is initialized before the mutations,
231+
# and then cleared as soon as we are done.
232+
233+
def run_delayed_updater
234+
current_update_exclusions = @update_exclusions
235+
@update_exclusions = @delayed_updater = nil
236+
@removed_observers = Set.new
237+
observers_to_update(current_update_exclusions).each do |observer, objects|
238+
observer.mutations objects unless @removed_observers.include? observer
222239
end
240+
ensure
241+
@removed_observers = nil
223242
end
224243

225244
# observers_to_update returns a hash with observers as keys, and lists of objects
226245
# as values. The hash is built by filtering the current_observers list
227246
# including only observers that have mutated objects, that are not on the exclusion
228247
# list.
248+
229249
def observers_to_update(exclusions)
230250
Hash.new { |hash, key| hash[key] = Array.new }.tap do |updates|
231251
exclusions.each do |object, excluded_observers|

ruby/hyper-state/lib/hyperstack/state/observer.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def update_objects_to_observe
1010
end
1111

1212
def remove
13-
Internal::State::Mapper.remove
13+
Internal::State::Mapper.remove(self)
1414
end
1515
end
1616
end

ruby/hyper-store/lib/hyperstack/internal/store/instance_methods.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module Store
44
module InstanceMethods
55
def init_store
66
return if @__hyperstack_store_initialized
7+
@__hyperstack_store_initialized = true
78
self.class.__instance_states.each do |instance_state|
89
# If the scope is shared then we initialize at the class level
910
next if instance_state[1][:scope] == :shared
@@ -21,7 +22,6 @@ def init_store
2122
block_value = instance_eval(&instance_state[1][:block])
2223
mutate.__send__(:"#{instance_state[0]}", block_value)
2324
end
24-
@__hyperstack_store_initialized = true
2525
end
2626

2727
def state

ruby/hyper-store/lib/hyperstack/internal/store/mutator_wrapper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class << self
77
def add_method(klass, method_name, opts = {})
88
define_method(:"#{method_name}") do |*args|
99
from = opts[:scope] == :shared ? klass.state.__from__ : __from__
10+
from.init_store if from.respond_to? :init_store
1011
current_value = State.get_state(from, method_name.to_s)
1112

1213
if args.count > 0

ruby/hyper-store/lib/hyperstack/internal/store/state.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ def legacy_map
99

1010
def get_state(obj, name)
1111
map_object = legacy_map[obj][name]
12-
map_object[0].tap { Hyperstack::Internal::State::Mapper.observed!(map_object.object_id) }
12+
Hyperstack::Internal::State::Mapper.observed!(map_object.object_id)
13+
map_object[0]
1314
end
1415

1516
def set_state(obj, name, value)
1617
map_object = legacy_map[obj][name]
17-
(map_object[0] = value).tap { Hyperstack::Internal::State::Mapper.mutated!(map_object.object_id) }
18+
map_object[0] = value
19+
Hyperstack::Internal::State::Mapper.mutated!(map_object.object_id)
20+
value
1821
end
1922
end
2023
end

0 commit comments

Comments
 (0)