hyper-state 0.1

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