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.
@@ -1,40 +0,0 @@
1
- module HyperStore
2
- module InstanceMethods
3
- def init_store
4
- self.class.__instance_states.each do |instance_state|
5
- # If the scope is shared then we initialize at the class level
6
- next if instance_state[1][:scope] == :shared
7
-
8
- # TODO: Figure out exactly how we're going to handle passing in procs and blocks together
9
- # But for now...just do the proc first then the block
10
-
11
- # First initialize value from initializer Proc
12
- proc_value = initializer_value(instance_state[1][:initializer])
13
- mutate.__send__(:"#{instance_state[0]}", proc_value)
14
-
15
- # Then call the block if a block is passed
16
- next unless instance_state[1][:block]
17
-
18
- block_value = instance_eval(&instance_state[1][:block])
19
- mutate.__send__(:"#{instance_state[0]}", block_value)
20
- end
21
-
22
- end
23
-
24
- def state
25
- @state ||= self.class.singleton_class.__state_wrapper.instance_state_wrapper.new(self)
26
- end
27
-
28
- def mutate
29
- @mutate ||= self.class.singleton_class.__state_wrapper.instance_mutator_wrapper.new(self)
30
- end
31
-
32
- private
33
-
34
- def initializer_value(initializer)
35
- # We gotta check the arity because a Proc passed in directly from initializer has no args,
36
- # but if we created one then we might have wanted self
37
- initializer.arity > 0 ? initializer.call(self) : initializer.call
38
- end
39
- end
40
- end
@@ -1,71 +0,0 @@
1
- module HyperStore
2
- class MutatorWrapper < BaseStoreClass # < BasicObject
3
-
4
- class << self
5
- def add_method(klass, method_name, opts = {})
6
- define_method(:"#{method_name}") do |*args|
7
- from = opts[:scope] == :shared ? klass.state.__from__ : __from__
8
- current_value = React::State.get_state(from, method_name.to_s)
9
-
10
- if args.count > 0
11
- React::State.set_state(from, method_name.to_s, args[0])
12
- current_value
13
- else
14
- React::State.set_state(from, method_name.to_s, current_value)
15
- React::Observable.new(current_value) do |update|
16
- React::State.set_state(from, method_name.to_s, update)
17
- end
18
- end
19
- end
20
-
21
- initialize_values(klass, method_name, opts) if initialize_values?(opts)
22
- end
23
-
24
- def initialize_values?(opts)
25
- [:class, :shared].include?(opts[:scope]) && (opts[:initializer] || opts[:block])
26
- end
27
-
28
- def initialize_values(klass, name, opts)
29
- initializer = initializer_proc(opts[:initializer], klass, name) if opts[:initializer]
30
-
31
- if initializer && opts[:block]
32
- klass.receives(Hyperloop::Application::Boot, initializer) do
33
- klass.mutate.__send__(:"#{name}", opts[:block].call)
34
- end
35
- elsif initializer
36
- klass.receives(Hyperloop::Application::Boot, initializer)
37
- elsif opts[:block]
38
- klass.receives(Hyperloop::Application::Boot) do
39
- klass.mutate.__send__(:"#{name}", opts[:block].call)
40
- end
41
- end
42
- end
43
-
44
- private
45
-
46
- def initializer_proc(initializer, klass, name)
47
- # We gotta check the arity because a Proc passed in directly from initializer has no args,
48
- # but if we created one then we might have wanted the class
49
- if initializer.arity > 0
50
- -> { klass.mutate.__send__(:"#{name}", initializer.call(klass)) }
51
- else
52
- -> { klass.mutate.__send__(:"#{name}", initializer.call) }
53
- end
54
- end
55
- end
56
-
57
- attr_accessor :__from__
58
-
59
- def self.new(from)
60
- instance = allocate
61
- instance.__from__ = from
62
- instance
63
- end
64
-
65
- # Any method_missing call will create a state and accessor with that name
66
- def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissing
67
- (class << self; self end).add_method(nil, name)
68
- __send__(name, *args, &block)
69
- end
70
- end
71
- end
@@ -1,107 +0,0 @@
1
- module HyperStore
2
- class StateWrapper < BaseStoreClass
3
- extend ArgumentValidator
4
-
5
- class << self
6
- attr_reader :instance_state_wrapper, :class_state_wrapper,
7
- :instance_mutator_wrapper, :class_mutator_wrapper,
8
- :wrappers
9
-
10
- def inherited(subclass)
11
- subclass.add_class_instance_vars(subclass) if self == StateWrapper
12
- end
13
-
14
- def add_class_instance_vars(subclass)
15
- @shared_state_wrapper = subclass
16
- @instance_state_wrapper = Class.new(@shared_state_wrapper)
17
- @class_state_wrapper = Class.new(@shared_state_wrapper)
18
-
19
- @shared_mutator_wrapper = Class.new(MutatorWrapper)
20
- @instance_mutator_wrapper = Class.new(@shared_mutator_wrapper)
21
- @class_mutator_wrapper = Class.new(@shared_mutator_wrapper)
22
-
23
- @wrappers = [@instance_state_wrapper, @instance_mutator_wrapper,
24
- @class_state_wrapper, @class_mutator_wrapper]
25
- end
26
-
27
- def define_state_methods(klass, *args, &block)
28
- return self if args.empty?
29
-
30
- name, opts = validate_args!(klass, *args, &block)
31
-
32
- add_readers(klass, name, opts)
33
- klass.singleton_class.state.add_error_methods(name, opts)
34
- klass.singleton_class.state.add_methods(klass, name, opts)
35
- klass.singleton_class.state.remove_methods(name, opts)
36
- klass.send(:"__#{opts[:scope]}_states") << [name, opts]
37
- end
38
-
39
- def add_readers(klass, name, opts)
40
- return unless opts[:reader]
41
-
42
- if [:instance, :shared].include?(opts[:scope])
43
- klass.class_eval do
44
- define_method(:"#{opts[:reader]}") { state.__send__(:"#{name}") }
45
- end
46
- end
47
-
48
- if [:class, :shared].include?(opts[:scope])
49
- klass.define_singleton_method(:"#{opts[:reader]}") { state.__send__(:"#{name}") }
50
- end
51
- end
52
-
53
- def add_error_methods(name, opts)
54
- return if opts[:scope] == :shared
55
-
56
- [@shared_state_wrapper, @shared_mutator_wrapper].each do |klass|
57
- klass.define_singleton_method(:"#{name}") do
58
- 'nope!'
59
- end
60
- end
61
- end
62
-
63
- def add_methods(klass, name, opts)
64
- instance_variable_get("@#{opts[:scope]}_state_wrapper").add_method(klass, name, opts)
65
- instance_variable_get("@#{opts[:scope]}_mutator_wrapper").add_method(klass, name, opts)
66
- end
67
-
68
- def add_method(klass, method_name, opts = {})
69
- define_method(:"#{method_name}") do
70
- from = opts[:scope] == :shared ? klass.state.__from__ : @__from__
71
- React::State.get_state(from, method_name.to_s)
72
- end
73
- end
74
-
75
- def remove_methods(name, opts)
76
- return unless opts[:scope] == :shared
77
-
78
- wrappers.each do |wrapper|
79
- wrapper.send(:remove_method, :"#{name}") if wrapper.respond_to?(:"#{name}")
80
- end
81
- end
82
-
83
- def default_scope(klass)
84
- if self == klass.singleton_class.__state_wrapper.class_state_wrapper
85
- :instance
86
- else
87
- :class
88
- end
89
- end
90
- end
91
-
92
- attr_accessor :__from__
93
-
94
- def self.new(from)
95
- instance = allocate
96
- instance.__from__ = from
97
- instance
98
- end
99
-
100
- # Any method_missing call will create a state and accessor with that name
101
- def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissing
102
- $method_missing = [name, *args]
103
- (class << self; self end).add_method(nil, name) #(class << self; self end).superclass.add_method(nil, name)
104
- __send__(name, *args, &block)
105
- end
106
- end
107
- end
@@ -1,91 +0,0 @@
1
- module HyperStore
2
- class StateWrapper < BaseStoreClass
3
- module ArgumentValidator
4
- class InvalidOptionError < StandardError; end
5
-
6
- def validate_args!(klass, *args, &block)
7
- name, initial_value, opts = parse_arguments(*args, &block)
8
-
9
- opts[:scope] ||= default_scope(klass)
10
- opts[:initializer] = validate_initializer(initial_value, klass, opts)
11
- opts[:block] = block if block
12
-
13
- if opts[:reader]
14
- opts[:reader] = opts[:reader] == true ? name : opts[:reader]
15
- end
16
-
17
- [name, opts]
18
- end
19
-
20
- private
21
-
22
- def invalid_option(message)
23
- raise InvalidOptionError, message
24
- end
25
-
26
- # Parses the arguments given to get the name, initial_value (if any), and options
27
- def parse_arguments(*args)
28
- # If the only argument is a hash, the first key => value is name => inital_value
29
- if args.first.is_a?(Hash)
30
- # If the first key passed in is not the name, raise an error
31
- if [:reader, :initializer, :scope].include?(args.first.keys.first.to_sym)
32
- message = 'The name of the state must be specified first as '\
33
- "either 'state :name' or 'state name: nil'"
34
- invalid_option(message)
35
- end
36
-
37
- name, initial_value = args[0].shift
38
- # Otherwise just the name is passed in by itself first
39
- else
40
- name = args.shift
41
- end
42
-
43
- # [name, initial_value (can be nil), args (if nil then return an empty hash)]
44
- [name, initial_value, args[0] || {}]
45
- end
46
-
47
- # Converts the initialize option to a Proc
48
- def validate_initializer(initial_value, klass, opts) # rubocop:disable Metrics/MethodLength
49
- # If we pass in the name as a hash with a value ex: state foo: :bar,
50
- # we just put that value inside a Proc and return that
51
- if initial_value != nil
52
- dup_or_return_intial_value(initial_value)
53
- # If we pass in the initialize option
54
- elsif opts[:initializer]
55
- # If it's a Symbol we convert to to a Proc that calls the method on the instance
56
- if [Symbol, String].include?(opts[:initializer].class)
57
- method_name = opts[:initializer]
58
- if [:class, :shared].include?(opts[:scope])
59
- -> { klass.send(:"#{method_name}") }
60
- else
61
- ->(instance) { instance.send(:"#{method_name}") }
62
- end
63
- # If it is already a Proc we do nothing and just return what was given
64
- elsif opts[:initializer].is_a?(Proc)
65
- opts[:initializer]
66
- # If it's not a Proc or a String we raise an error and return an empty Proc
67
- else
68
- invalid_option("'state' option 'initialize' must either be a Symbol or a Proc")
69
- -> {}
70
- end
71
- # Otherwise if it's not specified we just return an empty Proc
72
- else
73
- -> {}
74
- end
75
- end
76
-
77
- # Dup the initial value if possible, otherwise just return it
78
- # Ruby has no nice way of doing this...
79
- def dup_or_return_intial_value(value)
80
- value =
81
- begin
82
- value.dup
83
- rescue
84
- value
85
- end
86
-
87
- -> { value }
88
- end
89
- end
90
- end
91
- end
@@ -1,3 +0,0 @@
1
- module HyperStore
2
- VERSION = '0.99.6'
3
- end
@@ -1,34 +0,0 @@
1
- module Hyperloop
2
- # insure at least a stub of operation is defined. If
3
- # Hyperloop::Operation is already loaded it will have
4
- # defined these.
5
- class Operation
6
- class << self
7
- def on_dispatch(&block)
8
- receivers << block
9
- end unless method_defined? :on_dispatch
10
-
11
- def receivers
12
- # use the force: true option so that system code needing to receive
13
- # boot will NOT be erased on the next Hyperloop::Context.reset!
14
- Hyperloop::Context.set_var(self, :@receivers, force: true) { [] }
15
- end unless method_defined? :receivers
16
- end
17
- end
18
- class Application
19
- class Boot < Operation
20
- class ReactDummyParams
21
- attr_reader :context
22
- def initialize(context)
23
- @context = context
24
- end
25
- end
26
- def self.run(context: nil)
27
- params = ReactDummyParams.new(context)
28
- receivers.each do |receiver|
29
- receiver.call params
30
- end
31
- end
32
- end unless defined? Boot
33
- end
34
- end
@@ -1,12 +0,0 @@
1
- module Hyperloop
2
- class Store
3
- class << self
4
- def inherited(child)
5
- child.include(Mixin)
6
- end
7
- end
8
- def initialize
9
- init_store
10
- end
11
- end
12
- end
@@ -1,21 +0,0 @@
1
- module Hyperloop
2
- class Store
3
- module Mixin
4
- class << self
5
- def included(base)
6
- base.include(HyperStore::InstanceMethods)
7
- base.extend(HyperStore::ClassMethods)
8
- base.extend(HyperStore::DispatchReceiver)
9
-
10
- base.singleton_class.define_singleton_method(:__state_wrapper) do
11
- @__state_wrapper ||= Class.new(HyperStore::StateWrapper)
12
- end
13
-
14
- base.singleton_class.define_singleton_method(:state) do |*args, &block|
15
- __state_wrapper.define_state_methods(base, *args, &block)
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,29 +0,0 @@
1
- module React
2
- class Observable
3
- def initialize(value, on_change = nil, &block)
4
- @value = value
5
- @on_change = on_change || block
6
- end
7
-
8
- def method_missing(method_sym, *args, &block)
9
- @value.send(method_sym, *args, &block).tap { |result| @on_change.call @value }
10
- end
11
-
12
- def respond_to?(method, *args)
13
- if [:call, :to_proc].include? method
14
- true
15
- else
16
- @value.respond_to? method, *args
17
- end
18
- end
19
-
20
- def call(new_value)
21
- @on_change.call new_value
22
- @value = new_value
23
- end
24
-
25
- def to_proc
26
- lambda { |arg = @value| @on_change.call arg }
27
- end
28
- end
29
- end
@@ -1,157 +0,0 @@
1
- module React
2
- class State
3
-
4
- ALWAYS_UPDATE_STATE_AFTER_RENDER = Hyperloop.on_client? # if on server then we don't wait to update the state
5
- @rendering_level = 0
6
-
7
- class << self
8
- attr_reader :current_observer
9
-
10
- def has_observers?(object, name)
11
- !observers_by_name[object][name].empty?
12
- end
13
-
14
- def bulk_update
15
- saved_bulk_update_flag = @bulk_update_flag
16
- @bulk_update_flag = true
17
- yield
18
- ensure
19
- @bulk_update_flag = saved_bulk_update_flag
20
- end
21
-
22
- # React already will batch together updates inside of event handlers
23
- # so we don't have to, and having Hyperstack batch them outside of the
24
- # event handler causes INPUT/TEXT/SELECT s not to work properly.
25
- # This method is called by the Component event wrapper.
26
- def ignore_bulk_updates(*args)
27
- saved_ignore_bulk_update_flag = @ignore_bulk_update_flag
28
- @ignore_bulk_update_flag = true
29
- yield(*args)
30
- ensure
31
- @ignore_bulk_update_flag = saved_ignore_bulk_update_flag
32
- end
33
-
34
- def set_state2(object, name, value, updates, exclusions = nil)
35
- # set object's name state to value, tell all observers it has changed.
36
- # Observers must implement update_react_js_state
37
- object_needs_notification = object.respond_to?(:update_react_js_state)
38
- observers_by_name[object][name].dup.each do |observer|
39
- next if exclusions && exclusions.include?(observer)
40
- updates[observer] += [object, name, value]
41
- object_needs_notification = false if object == observer
42
- end
43
- updates[object] += [nil, name, value] if object_needs_notification
44
- end
45
-
46
- def initialize_states(object, initial_values) # initialize objects' name/value pairs
47
- states[object].merge!(initial_values || {})
48
- end
49
-
50
- def get_state(object, name, current_observer = @current_observer)
51
- # get current value of name for object, remember that the current object depends on this state,
52
- # current observer can be overriden with last param
53
- if current_observer && !new_observers[current_observer][object].include?(name)
54
- new_observers[current_observer][object] << name
55
- end
56
- if @delayed_updates && @delayed_updates[object][name]
57
- @delayed_updates[object][name][1] << current_observer
58
- end
59
- states[object][name]
60
- end
61
-
62
- # ReactDOM.unstable_batchedUpdates does not seem to improve things here, ill leave it here commented for reference
63
- # and later investigation
64
- #if `ReactDOM.unstable_batchedUpdates !== undefined`
65
- # %x{
66
- # ReactDOM.unstable_batchedUpdates(function(){
67
- # #{updates.each { |observer, args| observer.update_react_js_state(*args) }}
68
- # });
69
- # }
70
- #else # run the other one
71
- def set_state(object, name, value, delay=ALWAYS_UPDATE_STATE_AFTER_RENDER)
72
- states[object][name] = value
73
- delay = false if object.respond_to?(:set_state_synchronously?) && object.set_state_synchronously?
74
- if !@ignore_bulk_update_flag && (delay || @bulk_update_flag)
75
- @delayed_updates ||= Hash.new { |h, k| h[k] = {} }
76
- @delayed_updates[object][name] = [value, Set.new]
77
- @delayed_updater ||= after(0.001) do
78
- delayed_updates = @delayed_updates
79
- @delayed_updates = Hash.new { |h, k| h[k] = {} } # could this be nil???
80
- @delayed_updater = nil
81
- updates = Hash.new { |hash, key| hash[key] = Array.new }
82
- delayed_updates.each do |object, name_hash|
83
- name_hash.each do |name, value_and_set|
84
- set_state2(object, name, value_and_set[0], updates, value_and_set[1])
85
- end
86
- end
87
- updates.each { |observer, args| observer.update_react_js_state(*args) }
88
- end
89
- elsif @rendering_level == 0
90
- updates = Hash.new { |hash, key| hash[key] = Array.new }
91
- set_state2(object, name, value, updates)
92
- updates.each { |observer, args| observer.update_react_js_state(*args) }
93
- end
94
- value
95
- ensure
96
- 'Disabling JIT for Safari bug'
97
- end
98
-
99
- def will_be_observing?(object, name, current_observer)
100
- current_observer && new_observers[current_observer][object].include?(name)
101
- end
102
-
103
- def is_observing?(object, name, current_observer)
104
- current_observer && observers_by_name[object][name].include?(current_observer)
105
- end
106
-
107
- def update_states_to_observe(current_observer = @current_observer) # should be called after the last after_render callback, currently called after components render method
108
- raise "update_states_to_observer called outside of watch block" unless current_observer
109
- current_observers[current_observer].each do |object, names|
110
- names.each do |name|
111
- observers_by_name[object][name].delete(current_observer)
112
- end
113
- end
114
- observers = current_observers[current_observer] = new_observers[current_observer]
115
- new_observers.delete(current_observer)
116
- observers.each do |object, names|
117
- names.each do |name|
118
- observers_by_name[object][name] << current_observer
119
- end
120
- end
121
- end
122
-
123
- def remove # call after component is unmounted
124
- raise "remove called outside of watch block" unless @current_observer
125
- current_observers[@current_observer].each do |object, names|
126
- names.each do |name|
127
- observers_by_name[object][name].delete(@current_observer)
128
- end
129
- end
130
- current_observers.delete(@current_observer)
131
- end
132
-
133
- 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
134
- saved_current_observer = @current_observer
135
- @current_observer = observer
136
- @rendering_level += 1 if rendering
137
- return_value = yield
138
- return_value
139
- ensure
140
- @current_observer = saved_current_observer
141
- @rendering_level -= 1 if rendering
142
- return_value
143
- end
144
-
145
- def states
146
- @states ||= Hash.new { |h, k| h[k] = {} }
147
- end
148
-
149
- [:new_observers, :current_observers, :observers_by_name].each do |method_name|
150
- define_method(method_name) do
151
- instance_variable_get("@#{method_name}") ||
152
- instance_variable_set("@#{method_name}", Hash.new { |h, k| h[k] = Hash.new { |h, k| h[k] = [] } })
153
- end
154
- end
155
- end
156
- end
157
- end