hyper-store 0.99.6 → 1.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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