hyper-store 0.99.6 → 1.0.alpha1
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/Gemfile +2 -2
- data/hyper-store.gemspec +11 -6
- data/lib/hyper-store.rb +21 -16
- data/lib/hyperstack/internal/store/class_methods.rb +36 -0
- data/lib/hyperstack/internal/store/dispatch_receiver.rb +41 -0
- data/lib/hyperstack/internal/store/instance_methods.rb +45 -0
- data/lib/hyperstack/internal/store/mutator_wrapper.rb +84 -0
- data/lib/hyperstack/internal/store/observable.rb +33 -0
- data/lib/hyperstack/internal/store/state.rb +174 -0
- data/lib/hyperstack/internal/store/state_wrapper.rb +116 -0
- data/lib/hyperstack/internal/store/state_wrapper/argument_validator.rb +93 -0
- data/lib/hyperstack/legacy/store.rb +23 -0
- data/lib/hyperstack/legacy/store/version.rb +7 -0
- metadata +81 -28
- data/DOCS.md +0 -312
- data/README.md +0 -70
- data/lib/hyper-store/class_methods.rb +0 -32
- data/lib/hyper-store/dispatch_receiver.rb +0 -38
- data/lib/hyper-store/instance_methods.rb +0 -40
- data/lib/hyper-store/mutator_wrapper.rb +0 -71
- data/lib/hyper-store/state_wrapper.rb +0 -107
- data/lib/hyper-store/state_wrapper/argument_validator.rb +0 -91
- data/lib/hyper-store/version.rb +0 -3
- data/lib/hyperloop/application/boot.rb +0 -34
- data/lib/hyperloop/store.rb +0 -12
- data/lib/hyperloop/store/mixin.rb +0 -21
- data/lib/react/observable.rb +0 -29
- data/lib/react/state.rb +0 -157
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 77cc0e86a8c00fb007cbb5db453a451b789d4e76b2ede5a65e1f2087bd8900e9
         | 
| 4 | 
            +
              data.tar.gz: 95a9f7383681b7ac4d17300ddecdd941cbed5bb2b3956d2cfa2fc41f137ecefb
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 0c6278fda522efd1cc951a9f445ef604f5c4b198b28ec24b5b177a6eea2e7ffb13903d15a67174aadb851d1606645f922ce77614f7e6d3d521d63f7a0c0124e0
         | 
| 7 | 
            +
              data.tar.gz: 209a0ba1e9bcb7a5ac685f0b37c277ad92c697553acde01fd6f48dae9897895c6d7bc8f1248af6d42085e638c6871537abdb3ab98e41efccf91b6a926889356e
         | 
    
        data/Gemfile
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            source 'https://rubygems.org'
         | 
| 2 | 
            -
            #gem "opal-jquery", git: "https://github.com/opal/opal-jquery.git", branch: "master"
         | 
| 3 2 | 
             
            gem 'hyper-spec',       path: '../hyper-spec'
         | 
| 4 | 
            -
            gem ' | 
| 3 | 
            +
            gem 'hyperstack-config', path: '../hyperstack-config'
         | 
| 5 4 | 
             
            gem 'hyper-component',  path: '../hyper-component'
         | 
| 5 | 
            +
            gem 'hyper-state', path: '../hyper-state'
         | 
| 6 6 | 
             
            gemspec
         | 
    
        data/hyper-store.gemspec
    CHANGED
    
    | @@ -1,11 +1,11 @@ | |
| 1 1 | 
             
            # coding: utf-8
         | 
| 2 2 | 
             
            lib = File.expand_path('../lib', __FILE__)
         | 
| 3 3 | 
             
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         | 
| 4 | 
            -
            require ' | 
| 4 | 
            +
            require 'hyperstack/legacy/store/version'
         | 
| 5 5 |  | 
| 6 6 | 
             
            Gem::Specification.new do |spec|
         | 
| 7 7 | 
             
              spec.name          = 'hyper-store'
         | 
| 8 | 
            -
              spec.version       =  | 
| 8 | 
            +
              spec.version       = Hyperstack::Legacy::Store::VERSION
         | 
| 9 9 | 
             
              spec.authors       = ['Mitch VanDuyn', 'Adam Creekroad', 'Jan Biedermann']
         | 
| 10 10 | 
             
              spec.email         = ['mitch@catprint.com', 'jan@kursator.com']
         | 
| 11 11 | 
             
              spec.summary       = 'Flux Stores and more for Hyperloop'
         | 
| @@ -21,23 +21,28 @@ Gem::Specification.new do |spec| | |
| 21 21 | 
             
              spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
         | 
| 22 22 | 
             
              spec.require_paths = ['lib']
         | 
| 23 23 |  | 
| 24 | 
            -
              spec.add_dependency ' | 
| 24 | 
            +
              spec.add_dependency 'hyperstack-config', Hyperstack::Legacy::Store::VERSION
         | 
| 25 | 
            +
              spec.add_dependency 'hyper-state', Hyperstack::Legacy::Store::VERSION
         | 
| 25 26 | 
             
              spec.add_dependency 'opal', '>= 0.11.0', '< 0.12.0'
         | 
| 26 27 | 
             
              spec.add_development_dependency 'bundler'
         | 
| 27 28 | 
             
              spec.add_development_dependency 'chromedriver-helper'
         | 
| 28 | 
            -
              spec.add_development_dependency 'hyper-component',  | 
| 29 | 
            -
              spec.add_development_dependency 'hyper-spec',  | 
| 29 | 
            +
              spec.add_development_dependency 'hyper-component', Hyperstack::Legacy::Store::VERSION
         | 
| 30 | 
            +
              spec.add_development_dependency 'hyper-spec', Hyperstack::Legacy::Store::VERSION
         | 
| 30 31 | 
             
              spec.add_development_dependency 'listen'
         | 
| 31 | 
            -
              spec.add_development_dependency 'mini_racer', '~> 0. | 
| 32 | 
            +
              spec.add_development_dependency 'mini_racer', '~> 0.1.15'
         | 
| 32 33 | 
             
              spec.add_development_dependency 'opal-browser', '~> 0.2.0'
         | 
| 33 34 | 
             
              spec.add_development_dependency 'opal-rails', '~> 0.9.4'
         | 
| 34 35 | 
             
              spec.add_development_dependency 'pry-byebug'
         | 
| 36 | 
            +
              spec.add_development_dependency 'pry-rescue'
         | 
| 35 37 | 
             
              spec.add_development_dependency 'puma'
         | 
| 36 38 | 
             
              spec.add_development_dependency 'rails', '>= 4.0.0'
         | 
| 37 39 | 
             
              spec.add_development_dependency 'rake'
         | 
| 38 40 | 
             
              spec.add_development_dependency 'react-rails', '>= 2.4.0', '< 2.5.0'
         | 
| 39 41 | 
             
              spec.add_development_dependency 'rspec', '~> 3.7.0'
         | 
| 42 | 
            +
              spec.add_development_dependency 'rspec-rails'
         | 
| 40 43 | 
             
              spec.add_development_dependency 'rspec-steps', '~> 2.1.1'
         | 
| 41 44 | 
             
              spec.add_development_dependency 'rubocop', '~> 0.51.0'
         | 
| 42 45 | 
             
              spec.add_development_dependency 'sqlite3'
         | 
| 46 | 
            +
              spec.add_development_dependency 'timecop', '~> 0.8.1'
         | 
| 47 | 
            +
             | 
| 43 48 | 
             
            end
         | 
    
        data/lib/hyper-store.rb
    CHANGED
    
    | @@ -1,24 +1,29 @@ | |
| 1 1 | 
             
            require 'set'
         | 
| 2 | 
            -
            require ' | 
| 3 | 
            -
             | 
| 2 | 
            +
            require 'hyperstack-config'
         | 
| 3 | 
            +
            require 'hyper-state'
         | 
| 4 | 
            +
            Hyperstack.import 'hyper-store'
         | 
| 4 5 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
            module  | 
| 7 | 
            -
             | 
| 6 | 
            +
            module Hyperstack
         | 
| 7 | 
            +
              module Internal
         | 
| 8 | 
            +
                module Store
         | 
| 9 | 
            +
                  # allows us to easily turn off BasicObject for debug
         | 
| 10 | 
            +
                  class BaseStoreClass < BasicObject
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
                end
         | 
| 8 13 | 
             
              end
         | 
| 9 14 | 
             
            end
         | 
| 10 15 |  | 
| 11 | 
            -
            require ' | 
| 12 | 
            -
            require ' | 
| 13 | 
            -
            require ' | 
| 14 | 
            -
            require ' | 
| 15 | 
            -
            require ' | 
| 16 | 
            -
            require ' | 
| 17 | 
            -
            require ' | 
| 18 | 
            -
            require ' | 
| 19 | 
            -
             | 
| 20 | 
            -
            require ' | 
| 21 | 
            -
            require ' | 
| 16 | 
            +
            require 'hyperstack/internal/store/class_methods'
         | 
| 17 | 
            +
            require 'hyperstack/internal/store/dispatch_receiver'
         | 
| 18 | 
            +
            require 'hyperstack/internal/store/instance_methods'
         | 
| 19 | 
            +
            require 'hyperstack/internal/store/mutator_wrapper'
         | 
| 20 | 
            +
            require 'hyperstack/internal/store/observable'
         | 
| 21 | 
            +
            require 'hyperstack/internal/store/state_wrapper/argument_validator'
         | 
| 22 | 
            +
            require 'hyperstack/internal/store/state_wrapper'
         | 
| 23 | 
            +
            require 'hyperstack/internal/store/state'
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            require 'hyperstack/legacy/store'
         | 
| 26 | 
            +
            require 'hyperstack/legacy/store/version'
         | 
| 22 27 |  | 
| 23 28 | 
             
            if RUBY_ENGINE != 'opal'
         | 
| 24 29 | 
             
              require 'opal'
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            module Hyperstack
         | 
| 2 | 
            +
              module Internal
         | 
| 3 | 
            +
                module Store
         | 
| 4 | 
            +
                  module ClassMethods
         | 
| 5 | 
            +
                    attr_accessor :__shared_states, :__class_states, :__instance_states
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                    def state(*args, &block)
         | 
| 8 | 
            +
                      # If we're passing in any arguments then we are calling the macro to define a state
         | 
| 9 | 
            +
                      if args.count > 0
         | 
| 10 | 
            +
                        singleton_class.__state_wrapper.class_state_wrapper
         | 
| 11 | 
            +
                                       .define_state_methods(self, *args, &block)
         | 
| 12 | 
            +
                      # Otherwise we are just accessing it
         | 
| 13 | 
            +
                      else
         | 
| 14 | 
            +
                        @state ||= singleton_class.__state_wrapper.class_state_wrapper.new(self)
         | 
| 15 | 
            +
                      end
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    def mutate
         | 
| 19 | 
            +
                      @mutate ||= singleton_class.__state_wrapper.class_mutator_wrapper.new(self)
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    def __shared_states
         | 
| 23 | 
            +
                      @__shared_states ||= []
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    def __class_states
         | 
| 27 | 
            +
                      @__class_states ||= []
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    def __instance_states
         | 
| 31 | 
            +
                      @__instance_states ||= []
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
            end
         | 
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            module Hyperstack
         | 
| 2 | 
            +
              module Internal
         | 
| 3 | 
            +
                module Store
         | 
| 4 | 
            +
                  module DispatchReceiver
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                    attr_accessor :params
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                    def receives(*args, &block)
         | 
| 9 | 
            +
                      # Format the callback to be Proc or Nil
         | 
| 10 | 
            +
                      callback = format_callback(args)
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                      if args.empty?
         | 
| 13 | 
            +
                        message = 'At least one operation must be passed in to the \'receives\' macro'
         | 
| 14 | 
            +
                        raise Legacy::Store::InvalidOperationError, message
         | 
| 15 | 
            +
                      end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                      # Loop through receivers and call callback and block on dispatch
         | 
| 18 | 
            +
                      args.each do |operation|
         | 
| 19 | 
            +
                        operation.on_dispatch do |params|
         | 
| 20 | 
            +
                          @params = params
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                          callback.call if callback
         | 
| 23 | 
            +
                          yield params if block
         | 
| 24 | 
            +
                        end
         | 
| 25 | 
            +
                      end
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    private
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    def format_callback(args)
         | 
| 31 | 
            +
                      if args.last.is_a?(Symbol)
         | 
| 32 | 
            +
                        method_name = args.pop
         | 
| 33 | 
            +
                        -> { send(:"#{method_name}") }
         | 
| 34 | 
            +
                      elsif args.last.is_a?(Proc)
         | 
| 35 | 
            +
                        args.pop
         | 
| 36 | 
            +
                      end
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            module Hyperstack
         | 
| 2 | 
            +
              module Internal
         | 
| 3 | 
            +
                module Store
         | 
| 4 | 
            +
                  module InstanceMethods
         | 
| 5 | 
            +
                    def init_store
         | 
| 6 | 
            +
                      return if @__hyperstack_store_initialized
         | 
| 7 | 
            +
                      @__hyperstack_store_initialized = true
         | 
| 8 | 
            +
                      self.class.__instance_states.each do |instance_state|
         | 
| 9 | 
            +
                        # If the scope is shared then we initialize at the class level
         | 
| 10 | 
            +
                        next if instance_state[1][:scope] == :shared
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                        # TODO: Figure out exactly how we're going to handle passing in procs and blocks together
         | 
| 13 | 
            +
                        # But for now...just do the proc first then the block
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                        # First initialize value from initializer Proc
         | 
| 16 | 
            +
                        proc_value = initializer_value(instance_state[1][:initializer])
         | 
| 17 | 
            +
                        mutate.__send__(:"#{instance_state[0]}", proc_value)
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                        # Then call the block if a block is passed
         | 
| 20 | 
            +
                        next unless instance_state[1][:block]
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                        block_value = instance_eval(&instance_state[1][:block])
         | 
| 23 | 
            +
                        mutate.__send__(:"#{instance_state[0]}", block_value)
         | 
| 24 | 
            +
                      end
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    def state
         | 
| 28 | 
            +
                      @state ||= self.class.singleton_class.__state_wrapper.instance_state_wrapper.new(self)
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    def mutate
         | 
| 32 | 
            +
                      @mutate ||= self.class.singleton_class.__state_wrapper.instance_mutator_wrapper.new(self)
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    private
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    def initializer_value(initializer)
         | 
| 38 | 
            +
                      # We gotta check the arity because a Proc passed in directly from initializer has no args,
         | 
| 39 | 
            +
                      # but if we created one then we might have wanted self
         | 
| 40 | 
            +
                      initializer.arity > 0 ? initializer.call(self) : initializer.call
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
            end
         | 
| @@ -0,0 +1,84 @@ | |
| 1 | 
            +
            module Hyperstack
         | 
| 2 | 
            +
              module Internal
         | 
| 3 | 
            +
                module Store
         | 
| 4 | 
            +
                  class MutatorWrapper < BaseStoreClass # < BasicObject
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                    class << self
         | 
| 7 | 
            +
                      def add_method(klass, method_name, opts = {})
         | 
| 8 | 
            +
                        define_method(:"#{method_name}") do |*args|
         | 
| 9 | 
            +
                          from = opts[:scope] == :shared ? klass.state.__from__ : __from__
         | 
| 10 | 
            +
                          from.init_store if from.respond_to? :init_store
         | 
| 11 | 
            +
                          current_value = State.get_state(from, method_name.to_s)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                          if args.count > 0
         | 
| 14 | 
            +
                            State.set_state(from, method_name.to_s, args[0])
         | 
| 15 | 
            +
                            current_value
         | 
| 16 | 
            +
                          else
         | 
| 17 | 
            +
                            State.set_state(from, method_name.to_s, current_value)
         | 
| 18 | 
            +
                            Observable.new(current_value) do |update|
         | 
| 19 | 
            +
                              State.set_state(from, method_name.to_s, update)
         | 
| 20 | 
            +
                            end
         | 
| 21 | 
            +
                          end
         | 
| 22 | 
            +
                        end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                        initialize_values(klass, method_name, opts) if initialize_values?(opts)
         | 
| 25 | 
            +
                      end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                      def initialize_values?(opts)
         | 
| 28 | 
            +
                        [:class, :shared].include?(opts[:scope]) && (opts[:initializer] || opts[:block])
         | 
| 29 | 
            +
                      end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                      def initialize_values(klass, name, opts)
         | 
| 32 | 
            +
                        initializer = initializer_proc(opts[:initializer], klass, name) if opts[:initializer]
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                        if initializer && opts[:block]
         | 
| 35 | 
            +
                          klass.receives(Hyperstack::Application::Boot, initializer) do
         | 
| 36 | 
            +
                            klass.mutate.__send__(:"#{name}", opts[:block].call)
         | 
| 37 | 
            +
                          end
         | 
| 38 | 
            +
                        elsif initializer
         | 
| 39 | 
            +
                          klass.receives(Hyperstack::Application::Boot, initializer)
         | 
| 40 | 
            +
                        elsif opts[:block]
         | 
| 41 | 
            +
                          klass.receives(Hyperstack::Application::Boot) do
         | 
| 42 | 
            +
                            klass.mutate.__send__(:"#{name}", opts[:block].call)
         | 
| 43 | 
            +
                          end
         | 
| 44 | 
            +
                        end
         | 
| 45 | 
            +
                      end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                      private
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                      def initializer_proc(initializer, klass, name)
         | 
| 50 | 
            +
                        # We gotta check the arity because a Proc passed in directly from initializer has no args,
         | 
| 51 | 
            +
                        # but if we created one then we might have wanted the class
         | 
| 52 | 
            +
                        if initializer.arity > 0
         | 
| 53 | 
            +
                          -> { klass.mutate.__send__(:"#{name}", initializer.call(klass)) }
         | 
| 54 | 
            +
                        else
         | 
| 55 | 
            +
                          -> { klass.mutate.__send__(:"#{name}", initializer.call) }
         | 
| 56 | 
            +
                        end
         | 
| 57 | 
            +
                      end
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    attr_accessor :__from__
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    def self.new(from)
         | 
| 63 | 
            +
                      instance = allocate
         | 
| 64 | 
            +
                      instance.__from__ = from
         | 
| 65 | 
            +
                      instance
         | 
| 66 | 
            +
                    end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                    def []=(name, value)
         | 
| 69 | 
            +
                      __send__(name, value)
         | 
| 70 | 
            +
                    end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    def [](name)
         | 
| 73 | 
            +
                      __send__(name)
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                    # Any method_missing call will create a state and accessor with that name
         | 
| 77 | 
            +
                    def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissing
         | 
| 78 | 
            +
                      (class << self; self end).add_method(nil, name)
         | 
| 79 | 
            +
                      __send__(name, *args, &block)
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            module Hyperstack
         | 
| 2 | 
            +
              module Internal
         | 
| 3 | 
            +
                module Store
         | 
| 4 | 
            +
                  class Observable
         | 
| 5 | 
            +
                    def initialize(value, on_change = nil, &block)
         | 
| 6 | 
            +
                      @value = value
         | 
| 7 | 
            +
                      @on_change = on_change || block
         | 
| 8 | 
            +
                    end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    def method_missing(method_sym, *args, &block)
         | 
| 11 | 
            +
                      @value.send(method_sym, *args, &block).tap { |result| @on_change.call @value }
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    def respond_to?(method, *args)
         | 
| 15 | 
            +
                      if [:call, :to_proc].include? method
         | 
| 16 | 
            +
                        true
         | 
| 17 | 
            +
                      else
         | 
| 18 | 
            +
                        @value.respond_to? method, *args
         | 
| 19 | 
            +
                      end
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    def call(new_value)
         | 
| 23 | 
            +
                      @on_change.call new_value
         | 
| 24 | 
            +
                      @value = new_value
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    def to_proc
         | 
| 28 | 
            +
                      lambda { |arg = @value| @on_change.call arg }
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -0,0 +1,174 @@ | |
| 1 | 
            +
            module Hyperstack
         | 
| 2 | 
            +
              module Internal
         | 
| 3 | 
            +
                module Store
         | 
| 4 | 
            +
                  class State
         | 
| 5 | 
            +
                    class << self
         | 
| 6 | 
            +
                      def legacy_map
         | 
| 7 | 
            +
                        @legacy_map ||= Hash.new { |h, k| h[k] = Hash.new { |h2, k2| h2[k2] = Array.new }}
         | 
| 8 | 
            +
                      end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                      def get_state(obj, name)
         | 
| 11 | 
            +
                        map_object = legacy_map[obj][name]
         | 
| 12 | 
            +
                        Hyperstack::Internal::State::Mapper.observed!(map_object.object_id)
         | 
| 13 | 
            +
                        map_object[0]
         | 
| 14 | 
            +
                      end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                      def set_state(obj, name, value)
         | 
| 17 | 
            +
                        map_object = legacy_map[obj][name]
         | 
| 18 | 
            +
                        map_object[0] = value
         | 
| 19 | 
            +
                        Hyperstack::Internal::State::Mapper.mutated!(map_object.object_id)
         | 
| 20 | 
            +
                        value
         | 
| 21 | 
            +
                      end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                      def observed?(obj, name)
         | 
| 24 | 
            +
                        map_object = legacy_map[obj][name]
         | 
| 25 | 
            +
                        Hyperstack::Internal::State::Mapper.observed?(map_object.object_id)
         | 
| 26 | 
            +
                      end
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    # ALWAYS_UPDATE_STATE_AFTER_RENDER = Hyperstack.on_client? # if on server then we don't wait to update the state
         | 
| 34 | 
            +
                    # @rendering_level = 0
         | 
| 35 | 
            +
                    #
         | 
| 36 | 
            +
                    # class << self
         | 
| 37 | 
            +
                    #   attr_reader :current_observer
         | 
| 38 | 
            +
                    #
         | 
| 39 | 
            +
                    #   def has_observers?(object, name)
         | 
| 40 | 
            +
                    #     !observers_by_name[object][name].empty?
         | 
| 41 | 
            +
                    #   end
         | 
| 42 | 
            +
                    #
         | 
| 43 | 
            +
                    #   def bulk_update
         | 
| 44 | 
            +
                    #     saved_bulk_update_flag = @bulk_update_flag
         | 
| 45 | 
            +
                    #     @bulk_update_flag = true
         | 
| 46 | 
            +
                    #     yield
         | 
| 47 | 
            +
                    #   ensure
         | 
| 48 | 
            +
                    #     @bulk_update_flag = saved_bulk_update_flag
         | 
| 49 | 
            +
                    #   end
         | 
| 50 | 
            +
                    #
         | 
| 51 | 
            +
                    #   def set_state2(object, name, value, updates, exclusions = nil)
         | 
| 52 | 
            +
                    #     # set object's name state to value, tell all observers it has changed.
         | 
| 53 | 
            +
                    #     # Observers must implement update_react_js_state
         | 
| 54 | 
            +
                    #     object_needs_notification = object.respond_to?(:update_react_js_state)
         | 
| 55 | 
            +
                    #     observers_by_name[object][name].dup.each do |observer|
         | 
| 56 | 
            +
                    #       next if exclusions && exclusions.include?(observer)
         | 
| 57 | 
            +
                    #       updates[observer] += [object, name, value]
         | 
| 58 | 
            +
                    #       object_needs_notification = false if object == observer
         | 
| 59 | 
            +
                    #     end
         | 
| 60 | 
            +
                    #     updates[object] += [nil, name, value] if object_needs_notification
         | 
| 61 | 
            +
                    #   end
         | 
| 62 | 
            +
                    #
         | 
| 63 | 
            +
                    #   def initialize_states(object, initial_values) # initialize objects' name/value pairs
         | 
| 64 | 
            +
                    #     states[object].merge!(initial_values || {})
         | 
| 65 | 
            +
                    #   end
         | 
| 66 | 
            +
                    #
         | 
| 67 | 
            +
                    #   def get_state(object, name, current_observer = @current_observer)
         | 
| 68 | 
            +
                    #     # get current value of name for object, remember that the current object depends on this state,
         | 
| 69 | 
            +
                    #     # current observer can be overriden with last param
         | 
| 70 | 
            +
                    #     if current_observer && !new_observers[current_observer][object].include?(name)
         | 
| 71 | 
            +
                    #       new_observers[current_observer][object] << name
         | 
| 72 | 
            +
                    #     end
         | 
| 73 | 
            +
                    #     if @delayed_updates && @delayed_updates[object][name]
         | 
| 74 | 
            +
                    #       @delayed_updates[object][name][1] << current_observer
         | 
| 75 | 
            +
                    #     end
         | 
| 76 | 
            +
                    #     states[object][name]
         | 
| 77 | 
            +
                    #   end
         | 
| 78 | 
            +
                    #
         | 
| 79 | 
            +
                    #   # ReactDOM.unstable_batchedUpdates does not seem to improve things here, ill leave it here commented for reference
         | 
| 80 | 
            +
                    #   # and later investigation
         | 
| 81 | 
            +
                    #   #if `ReactDOM.unstable_batchedUpdates !== undefined`
         | 
| 82 | 
            +
                    #   #  %x{
         | 
| 83 | 
            +
                    #   #    ReactDOM.unstable_batchedUpdates(function(){
         | 
| 84 | 
            +
                    #   #      #{updates.each { |observer, args| observer.update_react_js_state(*args) }}
         | 
| 85 | 
            +
                    #   #    });
         | 
| 86 | 
            +
                    #   #  }
         | 
| 87 | 
            +
                    #   #else # run the other one
         | 
| 88 | 
            +
                    #   def set_state(object, name, value, delay=ALWAYS_UPDATE_STATE_AFTER_RENDER)
         | 
| 89 | 
            +
                    #     states[object][name] = value
         | 
| 90 | 
            +
                    #     delay = false if object.respond_to?(:set_state_synchronously?) && object.set_state_synchronously?
         | 
| 91 | 
            +
                    #     if delay || @bulk_update_flag
         | 
| 92 | 
            +
                    #       @delayed_updates ||= Hash.new { |h, k| h[k] = {} }
         | 
| 93 | 
            +
                    #       @delayed_updates[object][name] = [value, Set.new]
         | 
| 94 | 
            +
                    #       @delayed_updater ||= after(0.001) do
         | 
| 95 | 
            +
                    #         delayed_updates = @delayed_updates
         | 
| 96 | 
            +
                    #         @delayed_updates = Hash.new { |h, k| h[k] = {} } # could this be nil???
         | 
| 97 | 
            +
                    #         @delayed_updater = nil
         | 
| 98 | 
            +
                    #         updates = Hash.new { |hash, key| hash[key] = Array.new }
         | 
| 99 | 
            +
                    #         delayed_updates.each do |object, name_hash|
         | 
| 100 | 
            +
                    #           name_hash.each do |name, value_and_set|
         | 
| 101 | 
            +
                    #             set_state2(object, name, value_and_set[0], updates, value_and_set[1])
         | 
| 102 | 
            +
                    #           end
         | 
| 103 | 
            +
                    #         end
         | 
| 104 | 
            +
                    #         updates.each { |observer, args| observer.update_react_js_state(*args) }
         | 
| 105 | 
            +
                    #       end
         | 
| 106 | 
            +
                    #     elsif @rendering_level == 0
         | 
| 107 | 
            +
                    #       updates = Hash.new { |hash, key| hash[key] = Array.new }
         | 
| 108 | 
            +
                    #       set_state2(object, name, value, updates)
         | 
| 109 | 
            +
                    #       updates.each { |observer, args| observer.update_react_js_state(*args) }
         | 
| 110 | 
            +
                    #     end
         | 
| 111 | 
            +
                    #     value
         | 
| 112 | 
            +
                    #   end
         | 
| 113 | 
            +
                    #
         | 
| 114 | 
            +
                    #   def will_be_observing?(object, name, current_observer)
         | 
| 115 | 
            +
                    #     current_observer && new_observers[current_observer][object].include?(name)
         | 
| 116 | 
            +
                    #   end
         | 
| 117 | 
            +
                    #
         | 
| 118 | 
            +
                    #   def is_observing?(object, name, current_observer)
         | 
| 119 | 
            +
                    #     current_observer && observers_by_name[object][name].include?(current_observer)
         | 
| 120 | 
            +
                    #   end
         | 
| 121 | 
            +
                    #
         | 
| 122 | 
            +
                    #   def update_states_to_observe(current_observer = @current_observer)  # should be called after the last after_render callback, currently called after components render method
         | 
| 123 | 
            +
                    #     raise "update_states_to_observer called outside of watch block" unless current_observer
         | 
| 124 | 
            +
                    #     current_observers[current_observer].each do |object, names|
         | 
| 125 | 
            +
                    #       names.each do |name|
         | 
| 126 | 
            +
                    #         observers_by_name[object][name].delete(current_observer)
         | 
| 127 | 
            +
                    #       end
         | 
| 128 | 
            +
                    #     end
         | 
| 129 | 
            +
                    #     observers = current_observers[current_observer] = new_observers[current_observer]
         | 
| 130 | 
            +
                    #     new_observers.delete(current_observer)
         | 
| 131 | 
            +
                    #     observers.each do |object, names|
         | 
| 132 | 
            +
                    #       names.each do |name|
         | 
| 133 | 
            +
                    #         observers_by_name[object][name] << current_observer
         | 
| 134 | 
            +
                    #       end
         | 
| 135 | 
            +
                    #     end
         | 
| 136 | 
            +
                    #   end
         | 
| 137 | 
            +
                    #
         | 
| 138 | 
            +
                    #   def remove # call after component is unmounted
         | 
| 139 | 
            +
                    #     raise "remove called outside of watch block" unless @current_observer
         | 
| 140 | 
            +
                    #     current_observers[@current_observer].each do |object, names|
         | 
| 141 | 
            +
                    #       names.each do |name|
         | 
| 142 | 
            +
                    #         observers_by_name[object][name].delete(@current_observer)
         | 
| 143 | 
            +
                    #       end
         | 
| 144 | 
            +
                    #     end
         | 
| 145 | 
            +
                    #     current_observers.delete(@current_observer)
         | 
| 146 | 
            +
                    #   end
         | 
| 147 | 
            +
                    #
         | 
| 148 | 
            +
                    #   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
         | 
| 149 | 
            +
                    #     saved_current_observer = @current_observer
         | 
| 150 | 
            +
                    #     @current_observer = observer
         | 
| 151 | 
            +
                    #     @rendering_level += 1 if rendering
         | 
| 152 | 
            +
                    #     return_value = yield
         | 
| 153 | 
            +
                    #     return_value
         | 
| 154 | 
            +
                    #   ensure
         | 
| 155 | 
            +
                    #     @current_observer = saved_current_observer
         | 
| 156 | 
            +
                    #     @rendering_level -= 1 if rendering
         | 
| 157 | 
            +
                    #     return_value
         | 
| 158 | 
            +
                    #   end
         | 
| 159 | 
            +
                    #
         | 
| 160 | 
            +
                    #   def states
         | 
| 161 | 
            +
                    #     @states ||= Hash.new { |h, k| h[k] = {} }
         | 
| 162 | 
            +
                    #   end
         | 
| 163 | 
            +
                    #
         | 
| 164 | 
            +
                    #   [:new_observers, :current_observers, :observers_by_name].each do |method_name|
         | 
| 165 | 
            +
                    #     define_method(method_name) do
         | 
| 166 | 
            +
                    #       instance_variable_get("@#{method_name}") ||
         | 
| 167 | 
            +
                    #       instance_variable_set("@#{method_name}", Hash.new { |h, k| h[k] = Hash.new { |h, k| h[k] = [] } })
         | 
| 168 | 
            +
                    #     end
         | 
| 169 | 
            +
            #           end
         | 
| 170 | 
            +
            #         end
         | 
| 171 | 
            +
            #       end
         | 
| 172 | 
            +
            #     end
         | 
| 173 | 
            +
            #   end
         | 
| 174 | 
            +
            # end
         |