hyper-state 0.1

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.
@@ -0,0 +1,76 @@
1
+ module Hyperstack
2
+ module State
3
+ module Observable
4
+ def self.bulk_update(&block)
5
+ Internal::State::Mapper.bulk_update(&block)
6
+ end
7
+
8
+ def self.included(base)
9
+ base.include Internal::AutoUnmount
10
+ %i[singleton_method method].each do |kind|
11
+ base.send(:"define_#{kind}", :receives) do |*args, &block|
12
+ Internal::Receiver.mount(self, *args, &block)
13
+ end
14
+ base.send(:"define_#{kind}", :observe) do |&block|
15
+ result = block.call if block
16
+ Internal::State::Mapper.observed! self
17
+ result
18
+ end
19
+ base.send(:"define_#{kind}", :mutate) do |*_args, &block|
20
+ # any args will be ignored thus allowing us to say `mutate @foo = 123, @bar[:x] = 7` etc
21
+ result = block.call if block
22
+ Internal::State::Mapper.mutated! self
23
+ result
24
+ end
25
+ if RUBY_ENGINE == 'opal'
26
+ base.send(:"define_#{kind}", :set) do |var|
27
+ lambda do |val|
28
+ `self[#{var}] = #{val}`
29
+ mutate if var =~ /^[a-z]/
30
+ end
31
+ end
32
+ else
33
+ base.send(:"define_#{kind}", :set) do |var|
34
+ lambda do |val|
35
+ instance_variable_set(:"@#{var}", val)
36
+ mutate if var =~ /^[a-z]/
37
+ end
38
+ end
39
+ end
40
+ base.singleton_class.send(:"define_#{kind}", :observer) do |name, &block|
41
+ define_method(name) do |*args|
42
+ instance_exec(*args, &block).tap do
43
+ Internal::State::Mapper.observed! self
44
+ end
45
+ end
46
+ end
47
+ base.singleton_class.send(:"define_#{kind}", :mutator) do |name, &block|
48
+ define_method(name) do |*args|
49
+ instance_exec(*args, &block).tap do
50
+ Internal::State::Mapper.mutated! self
51
+ end
52
+ end
53
+ end
54
+ base.singleton_class.send(:"define_#{kind}", :state_reader) do |*names|
55
+ names.each do |name|
56
+ define_method(name) do
57
+ instance_variable_get(:"@#{name}").tap { Internal::State::Mapper.observed!(self) }
58
+ end
59
+ end
60
+ end
61
+ base.singleton_class.send(:"define_#{kind}", :state_writer) do |*names|
62
+ names.each do |name|
63
+ define_method(:"#{name}=") do |x|
64
+ instance_variable_set(:"@#{name}", x).tap { Internal::State::Mapper.mutated!(self) }
65
+ end
66
+ end
67
+ end
68
+ base.singleton_class.send(:"define_#{kind}", :state_accessor) do |*names|
69
+ state_reader(*names)
70
+ state_writer(*names)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,17 @@
1
+ module Hyperstack
2
+ module State
3
+ module Observer
4
+ def observing(immediate_update: false, rendering: false, update_objects: false, &block)
5
+ Internal::State::Mapper.observing(self, immediate_update, rendering, update_objects, &block)
6
+ end
7
+
8
+ def update_objects_to_observe
9
+ Internal::State::Mapper.update_objects_to_observe(self)
10
+ end
11
+
12
+ def remove
13
+ Internal::State::Mapper.remove(self)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ module Hyperstack
2
+ module State
3
+ VERSION = '0.1'
4
+ end
5
+ end
@@ -0,0 +1,104 @@
1
+ typically we see this:
2
+
3
+ `State.get_state(obj_or_class, key)` and `State.set_state(obj_or_class, key, val)`
4
+
5
+ we would like to see this:
6
+
7
+ `obj.key.state` and `obj.key.state = val`
8
+
9
+ plus `obj.key.mutate` which is short for
10
+ `obj.key.state.tap { |val| val... some mutation; val.key.state = val}`
11
+
12
+ problems:
13
+
14
+ 1. creates a state object for each key - the only purpose this serves is preventing accidental application of `key = ...` instead of `self.key = `
15
+
16
+ 2. difficult to use application defined keys like Model attributes - have to call the `state` macro for each key before use.
17
+
18
+ 3. many of the objects have only a single state, so `obj.my_only_state.state` is redundant.
19
+
20
+ what do we want to say?
21
+
22
+ `obj.states[key]` or `obj.states[key] = val` or `obj.mutates[key]`
23
+
24
+ and in the case of an object with a single state:
25
+
26
+ `obj.state # reader` and
27
+ `obj.state = val # setter - careful of self.state = problem` and
28
+ `obj.mutate`
29
+
30
+ for the first case we could say:
31
+
32
+ `state :states, hash: :mutates`
33
+
34
+ for the second case it would be implemented like this:
35
+
36
+ ```ruby
37
+ state :state_obj
38
+ def state
39
+ state_obj.state
40
+ end
41
+
42
+ def state=(val)
43
+ state_obj.state = val
44
+ end
45
+
46
+ def mutate
47
+ state_obj.mutate
48
+ end
49
+ ```
50
+
51
+ and perhaps this would be done by using a subclass instead of include?
52
+
53
+ ```ruby
54
+ class MyStatefulClass < Hyperstack::State::Base
55
+ #... how to do it the class level?
56
+ end
57
+ ```
58
+
59
+
60
+ MORE THOUGHTS ON SINGLE STATE OBJECTS
61
+
62
+ typically we have some instance variable that is the
63
+ reactive state. We may want to read and change that variable
64
+ internally without reaction
65
+
66
+ so one approach would be to just provide two methods: `observed!` and `mutated!`
67
+ ```ruby
68
+ def observed!
69
+ State.get_state(self, self)
70
+ end
71
+
72
+ def mutated!
73
+ State.set_state(self, self, self)
74
+ end
75
+ ```
76
+
77
+ ### So here is the deal
78
+
79
+ If you want to make some arbitrary piece of data *stateful* then you have to deal with arrays, or at least some kind of data that can be addressed.
80
+
81
+ hmmm not exactly right... the thing is we want to build state objects out of state variables and variables can be arrays (or other addressable primitive structures).
82
+
83
+ so lets say we want to build a reactive hash class, but we want each key to be its own reactive variable.
84
+
85
+ We could create the reactive hash class with an internal class called ReactiveHashValue, where each reactive_hash_value knows its key, its value, and inherits from Observable.
86
+
87
+ http://opalrb.com/try/?code:class%20Foo%0A%20%20def%20initialize(x)%0A%20%20%20%20%40x%20%3D%20x%0A%20%20end%0A%20%20def%20meth%0A%20%20%20%20%40x%0A%20%20end%0Aend%0A%0Aputs%20Foo.new(12).meth%0A%0Aclass%20Bar%0A%20%20def%20self.new(x)%0A%20%20%20%20return%20%60%7B%24meth%3A%20function()%20%7B%20return%20(x)%20%7D%20%7D%60%0A%20%20end%0Aend%0A%0Aputs%20Bar.new(12).meth%0A%0Adef%20speedy(x)%0A%20%20return%20%60%7B%24meth%3A%20function()%20%7Breturn%20(x)%20%7D%20%7D%60%0Aend%0A%0Aputs%20speedy(12).meth%0A%0A%0Adef%20timeit(x)%20%0A%20%20start_time%20%3D%20Time.now.to_f%0A%20%20x.times.each%20%7B%20yield%20%7D%0A%20%20(Time.now.to_f%20-%20start_time)%20%2F%20x%0Aend%0A%0Aputs%20timeit(1_000_000)%20%7B%20Foo.new(12).meth%20%7D%0Aputs%20timeit(1_000_000)%20%7B%20speedy(12).meth%20%7D%0Aputs%20timeit(1_000_000)%20%7B%20Bar.new(12).meth%20%7D
88
+
89
+
90
+ ```ruby
91
+ module Observable
92
+ # adds state methods to a class
93
+ # adds the mutated! and observed! methods to instance and class
94
+ # which can be used to notify of changes to the instance or classes state
95
+ end
96
+
97
+ class ObservableState
98
+ # contains a single state value methods such as state, state= and mutate
99
+ end
100
+
101
+ # Observable uses ObservableState for each state variable, and also for the single class and
102
+ # instance state variables representing the state of the class as a whole.
103
+
104
+ ```
metadata ADDED
@@ -0,0 +1,383 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hyper-state
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Mitch VanDuyn
8
+ - Adam Creekroad
9
+ - Jan Biedermann
10
+ autorequire:
11
+ bindir: exe
12
+ cert_chain: []
13
+ date: 2018-11-10 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hyperstack-config
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - '='
20
+ - !ruby/object:Gem::Version
21
+ version: '0.1'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - '='
27
+ - !ruby/object:Gem::Version
28
+ version: '0.1'
29
+ - !ruby/object:Gem::Dependency
30
+ name: opal
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 0.11.0
36
+ - - "<"
37
+ - !ruby/object:Gem::Version
38
+ version: 0.12.0
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 0.11.0
46
+ - - "<"
47
+ - !ruby/object:Gem::Version
48
+ version: 0.12.0
49
+ - !ruby/object:Gem::Dependency
50
+ name: bundler
51
+ requirement: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: chromedriver-helper
65
+ requirement: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ - !ruby/object:Gem::Dependency
78
+ name: hyper-component
79
+ requirement: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - '='
82
+ - !ruby/object:Gem::Version
83
+ version: '0.1'
84
+ type: :development
85
+ prerelease: false
86
+ version_requirements: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - '='
89
+ - !ruby/object:Gem::Version
90
+ version: '0.1'
91
+ - !ruby/object:Gem::Dependency
92
+ name: hyper-spec
93
+ requirement: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - '='
96
+ - !ruby/object:Gem::Version
97
+ version: '0.1'
98
+ type: :development
99
+ prerelease: false
100
+ version_requirements: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - '='
103
+ - !ruby/object:Gem::Version
104
+ version: '0.1'
105
+ - !ruby/object:Gem::Dependency
106
+ name: listen
107
+ requirement: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ type: :development
113
+ prerelease: false
114
+ version_requirements: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ - !ruby/object:Gem::Dependency
120
+ name: mini_racer
121
+ requirement: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: 0.1.15
126
+ type: :development
127
+ prerelease: false
128
+ version_requirements: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: 0.1.15
133
+ - !ruby/object:Gem::Dependency
134
+ name: opal-browser
135
+ requirement: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: 0.2.0
140
+ type: :development
141
+ prerelease: false
142
+ version_requirements: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - "~>"
145
+ - !ruby/object:Gem::Version
146
+ version: 0.2.0
147
+ - !ruby/object:Gem::Dependency
148
+ name: opal-rails
149
+ requirement: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - "~>"
152
+ - !ruby/object:Gem::Version
153
+ version: 0.9.4
154
+ type: :development
155
+ prerelease: false
156
+ version_requirements: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - "~>"
159
+ - !ruby/object:Gem::Version
160
+ version: 0.9.4
161
+ - !ruby/object:Gem::Dependency
162
+ name: pry-byebug
163
+ requirement: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ type: :development
169
+ prerelease: false
170
+ version_requirements: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ - !ruby/object:Gem::Dependency
176
+ name: pry-rescue
177
+ requirement: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ - !ruby/object:Gem::Dependency
190
+ name: puma
191
+ requirement: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - ">="
194
+ - !ruby/object:Gem::Version
195
+ version: '0'
196
+ type: :development
197
+ prerelease: false
198
+ version_requirements: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - ">="
201
+ - !ruby/object:Gem::Version
202
+ version: '0'
203
+ - !ruby/object:Gem::Dependency
204
+ name: rails
205
+ requirement: !ruby/object:Gem::Requirement
206
+ requirements:
207
+ - - ">="
208
+ - !ruby/object:Gem::Version
209
+ version: 4.0.0
210
+ type: :development
211
+ prerelease: false
212
+ version_requirements: !ruby/object:Gem::Requirement
213
+ requirements:
214
+ - - ">="
215
+ - !ruby/object:Gem::Version
216
+ version: 4.0.0
217
+ - !ruby/object:Gem::Dependency
218
+ name: rake
219
+ requirement: !ruby/object:Gem::Requirement
220
+ requirements:
221
+ - - ">="
222
+ - !ruby/object:Gem::Version
223
+ version: '0'
224
+ type: :development
225
+ prerelease: false
226
+ version_requirements: !ruby/object:Gem::Requirement
227
+ requirements:
228
+ - - ">="
229
+ - !ruby/object:Gem::Version
230
+ version: '0'
231
+ - !ruby/object:Gem::Dependency
232
+ name: react-rails
233
+ requirement: !ruby/object:Gem::Requirement
234
+ requirements:
235
+ - - ">="
236
+ - !ruby/object:Gem::Version
237
+ version: 2.4.0
238
+ - - "<"
239
+ - !ruby/object:Gem::Version
240
+ version: 2.5.0
241
+ type: :development
242
+ prerelease: false
243
+ version_requirements: !ruby/object:Gem::Requirement
244
+ requirements:
245
+ - - ">="
246
+ - !ruby/object:Gem::Version
247
+ version: 2.4.0
248
+ - - "<"
249
+ - !ruby/object:Gem::Version
250
+ version: 2.5.0
251
+ - !ruby/object:Gem::Dependency
252
+ name: rspec
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - "~>"
256
+ - !ruby/object:Gem::Version
257
+ version: 3.7.0
258
+ type: :development
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - "~>"
263
+ - !ruby/object:Gem::Version
264
+ version: 3.7.0
265
+ - !ruby/object:Gem::Dependency
266
+ name: rspec-rails
267
+ requirement: !ruby/object:Gem::Requirement
268
+ requirements:
269
+ - - ">="
270
+ - !ruby/object:Gem::Version
271
+ version: '0'
272
+ type: :development
273
+ prerelease: false
274
+ version_requirements: !ruby/object:Gem::Requirement
275
+ requirements:
276
+ - - ">="
277
+ - !ruby/object:Gem::Version
278
+ version: '0'
279
+ - !ruby/object:Gem::Dependency
280
+ name: rspec-steps
281
+ requirement: !ruby/object:Gem::Requirement
282
+ requirements:
283
+ - - "~>"
284
+ - !ruby/object:Gem::Version
285
+ version: 2.1.1
286
+ type: :development
287
+ prerelease: false
288
+ version_requirements: !ruby/object:Gem::Requirement
289
+ requirements:
290
+ - - "~>"
291
+ - !ruby/object:Gem::Version
292
+ version: 2.1.1
293
+ - !ruby/object:Gem::Dependency
294
+ name: rubocop
295
+ requirement: !ruby/object:Gem::Requirement
296
+ requirements:
297
+ - - "~>"
298
+ - !ruby/object:Gem::Version
299
+ version: 0.51.0
300
+ type: :development
301
+ prerelease: false
302
+ version_requirements: !ruby/object:Gem::Requirement
303
+ requirements:
304
+ - - "~>"
305
+ - !ruby/object:Gem::Version
306
+ version: 0.51.0
307
+ - !ruby/object:Gem::Dependency
308
+ name: sqlite3
309
+ requirement: !ruby/object:Gem::Requirement
310
+ requirements:
311
+ - - ">="
312
+ - !ruby/object:Gem::Version
313
+ version: '0'
314
+ type: :development
315
+ prerelease: false
316
+ version_requirements: !ruby/object:Gem::Requirement
317
+ requirements:
318
+ - - ">="
319
+ - !ruby/object:Gem::Version
320
+ version: '0'
321
+ - !ruby/object:Gem::Dependency
322
+ name: timecop
323
+ requirement: !ruby/object:Gem::Requirement
324
+ requirements:
325
+ - - "~>"
326
+ - !ruby/object:Gem::Version
327
+ version: 0.8.1
328
+ type: :development
329
+ prerelease: false
330
+ version_requirements: !ruby/object:Gem::Requirement
331
+ requirements:
332
+ - - "~>"
333
+ - !ruby/object:Gem::Version
334
+ version: 0.8.1
335
+ description:
336
+ email:
337
+ - mitch@catprint.com
338
+ - jan@kursator.com
339
+ executables: []
340
+ extensions: []
341
+ extra_rdoc_files: []
342
+ files:
343
+ - ".gitignore"
344
+ - ".travis.yml"
345
+ - DOCS.md
346
+ - Gemfile
347
+ - README.md
348
+ - Rakefile
349
+ - hyper-state.gemspec
350
+ - lib/hyper-state.rb
351
+ - lib/hyperstack/internal/auto_unmount.rb
352
+ - lib/hyperstack/internal/callbacks.rb
353
+ - lib/hyperstack/internal/receiver.rb
354
+ - lib/hyperstack/internal/state/mapper.rb
355
+ - lib/hyperstack/state/observable.rb
356
+ - lib/hyperstack/state/observer.rb
357
+ - lib/hyperstack/state/version.rb
358
+ - notes.md
359
+ homepage: https://ruby-hyperloop.org
360
+ licenses:
361
+ - MIT
362
+ metadata: {}
363
+ post_install_message:
364
+ rdoc_options: []
365
+ require_paths:
366
+ - lib
367
+ required_ruby_version: !ruby/object:Gem::Requirement
368
+ requirements:
369
+ - - ">="
370
+ - !ruby/object:Gem::Version
371
+ version: '0'
372
+ required_rubygems_version: !ruby/object:Gem::Requirement
373
+ requirements:
374
+ - - ">="
375
+ - !ruby/object:Gem::Version
376
+ version: '0'
377
+ requirements: []
378
+ rubyforge_project:
379
+ rubygems_version: 2.7.8
380
+ signing_key:
381
+ specification_version: 4
382
+ summary: Flux Stores and more for Hyperloop
383
+ test_files: []