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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 993335049ccaa5877cb790abaec2ab168f89e5bc1ec38d89616f6733cd05867f
4
- data.tar.gz: cb09118eb82ad5e8f9fa28f107b15e114eb3defc436702de3c883cca9933a869
3
+ metadata.gz: 77cc0e86a8c00fb007cbb5db453a451b789d4e76b2ede5a65e1f2087bd8900e9
4
+ data.tar.gz: 95a9f7383681b7ac4d17300ddecdd941cbed5bb2b3956d2cfa2fc41f137ecefb
5
5
  SHA512:
6
- metadata.gz: '0862df2d650305d95b9969041418a2ae426f25bb8636702644d2586fae982fbde183fdfcc243a595293635c056e39ab9487bdb4ae520f3843893755e00e21024'
7
- data.tar.gz: fc88e1de07631474812e52362a540413a5ffc62f9a2d45b0040258e5e0227052a4dc2edad45a75a56029bbfa26cc54410d9f67eb5c64a7cd9e63effb834a1602
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 'hyperloop-config', path: '../hyperloop-config'
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
@@ -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 'hyper-store/version'
4
+ require 'hyperstack/legacy/store/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'hyper-store'
8
- spec.version = HyperStore::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 'hyperloop-config', HyperStore::VERSION
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', HyperStore::VERSION
29
- spec.add_development_dependency 'hyper-spec', HyperStore::VERSION
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.2.4'
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
@@ -1,24 +1,29 @@
1
1
  require 'set'
2
- require 'hyperloop-config'
3
- Hyperloop.import 'hyper-store'
2
+ require 'hyperstack-config'
3
+ require 'hyper-state'
4
+ Hyperstack.import 'hyper-store'
4
5
 
5
-
6
- module HyperStore # allows us to easily turn off BasicObject for debug
7
- class BaseStoreClass < BasicObject
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 'hyper-store/class_methods'
12
- require 'hyper-store/dispatch_receiver'
13
- require 'hyper-store/instance_methods'
14
- require 'hyper-store/mutator_wrapper'
15
- require 'hyper-store/state_wrapper/argument_validator'
16
- require 'hyper-store/state_wrapper'
17
- require 'hyper-store/version'
18
- require 'hyperloop/store'
19
- require 'hyperloop/application/boot'
20
- require 'hyperloop/store/mixin'
21
- require 'react/state'
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