module Hyperstack module Internal module State module Variable class << self def legacy_map @legacy_map ||= Hash.new { |h, k| h[k] = Hash.new { |h2, k2| h2[k2] = Array.new }} end def get(obj, name) map_object = legacy_map[obj][name] Hyperstack::Internal::State::Mapper.observed!(map_object.object_id) map_object[0] end def set(obj, name, value, _x = nil) # _x is some legacy function, which I think queued up state changes to the end # which is perhaps now the default. map_object = legacy_map[obj][name] map_object[0] = value Hyperstack::Internal::State::Mapper.mutated!(map_object.object_id) value end def observed?(obj, name) map_object = legacy_map[obj][name] Hyperstack::Internal::State::Mapper.observed?(map_object.object_id) end end end end end end # ALWAYS_UPDATE_STATE_AFTER_RENDER = Hyperstack.on_client? # if on server then we don't wait to update the state # @rendering_level = 0 # # class << self # attr_reader :current_observer # # def has_observers?(object, name) # !observers_by_name[object][name].empty? # end # # def bulk_update # saved_bulk_update_flag = @bulk_update_flag # @bulk_update_flag = true # yield # ensure # @bulk_update_flag = saved_bulk_update_flag # end # # def set_state2(object, name, value, updates, exclusions = nil) # # set object's name state to value, tell all observers it has changed. # # Observers must implement update_react_js_state # object_needs_notification = object.respond_to?(:update_react_js_state) # observers_by_name[object][name].dup.each do |observer| # next if exclusions && exclusions.include?(observer) # updates[observer] += [object, name, value] # object_needs_notification = false if object == observer # end # updates[object] += [nil, name, value] if object_needs_notification # end # # def initialize_states(object, initial_values) # initialize objects' name/value pairs # states[object].merge!(initial_values || {}) # end # # def get_state(object, name, current_observer = @current_observer) # # get current value of name for object, remember that the current object depends on this state, # # current observer can be overriden with last param # if current_observer && !new_observers[current_observer][object].include?(name) # new_observers[current_observer][object] << name # end # if @delayed_updates && @delayed_updates[object][name] # @delayed_updates[object][name][1] << current_observer # end # states[object][name] # end # # # ReactDOM.unstable_batchedUpdates does not seem to improve things here, ill leave it here commented for reference # # and later investigation # #if `ReactDOM.unstable_batchedUpdates !== undefined` # # %x{ # # ReactDOM.unstable_batchedUpdates(function(){ # # #{updates.each { |observer, args| observer.update_react_js_state(*args) }} # # }); # # } # #else # run the other one # def set_state(object, name, value, delay=ALWAYS_UPDATE_STATE_AFTER_RENDER) # states[object][name] = value # delay = false if object.respond_to?(:set_state_synchronously?) && object.set_state_synchronously? # if delay || @bulk_update_flag # @delayed_updates ||= Hash.new { |h, k| h[k] = {} } # @delayed_updates[object][name] = [value, Set.new] # @delayed_updater ||= after(0.001) do # delayed_updates = @delayed_updates # @delayed_updates = Hash.new { |h, k| h[k] = {} } # could this be nil??? # @delayed_updater = nil # updates = Hash.new { |hash, key| hash[key] = Array.new } # delayed_updates.each do |object, name_hash| # name_hash.each do |name, value_and_set| # set_state2(object, name, value_and_set[0], updates, value_and_set[1]) # end # end # updates.each { |observer, args| observer.update_react_js_state(*args) } # end # elsif @rendering_level == 0 # updates = Hash.new { |hash, key| hash[key] = Array.new } # set_state2(object, name, value, updates) # updates.each { |observer, args| observer.update_react_js_state(*args) } # end # value # end # # def will_be_observing?(object, name, current_observer) # current_observer && new_observers[current_observer][object].include?(name) # end # # def is_observing?(object, name, current_observer) # current_observer && observers_by_name[object][name].include?(current_observer) # end # # def update_states_to_observe(current_observer = @current_observer) # should be called after the last after_render callback, currently called after components render method # raise "update_states_to_observer called outside of watch block" unless current_observer # current_observers[current_observer].each do |object, names| # names.each do |name| # observers_by_name[object][name].delete(current_observer) # end # end # observers = current_observers[current_observer] = new_observers[current_observer] # new_observers.delete(current_observer) # observers.each do |object, names| # names.each do |name| # observers_by_name[object][name] << current_observer # end # end # end # # def remove # call after component is unmounted # raise "remove called outside of watch block" unless @current_observer # current_observers[@current_observer].each do |object, names| # names.each do |name| # observers_by_name[object][name].delete(@current_observer) # end # end # current_observers.delete(@current_observer) # end # # def set_state_context_to(observer, rendering = nil) # wrap all execution that may set or get states in a block so we know which observer is executing # saved_current_observer = @current_observer # @current_observer = observer # @rendering_level += 1 if rendering # return_value = yield # return_value # ensure # @current_observer = saved_current_observer # @rendering_level -= 1 if rendering # return_value # end # # def states # @states ||= Hash.new { |h, k| h[k] = {} } # end # # [:new_observers, :current_observers, :observers_by_name].each do |method_name| # define_method(method_name) do # instance_variable_get("@#{method_name}") || # instance_variable_set("@#{method_name}", Hash.new { |h, k| h[k] = Hash.new { |h, k| h[k] = [] } }) # end # end # end # end # end # end # end