This repository has been archived by the owner on Oct 19, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 18
/
feed.xml
781 lines (670 loc) · 58.4 KB
/
feed.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Blog Name</title>
<subtitle>Blog subtitle</subtitle>
<id>http://blog.url.com/</id>
<link href="http://blog.url.com/"/>
<link href="http://blog.url.com/feed.xml" rel="self"/>
<updated>2018-07-28T09:47:00+01:00</updated>
<author>
<name>Blog Author</name>
</author>
<entry>
<title>Statement of Direction</title>
<link rel="alternate" href="http://blog.url.com/blog/2018/07/28/Statement-of-Direction/"/>
<id>http://blog.url.com/blog/2018/07/28/Statement-of-Direction/</id>
<published>2018-07-28T09:47:00+01:00</published>
<updated>2018-07-27T12:49:31+01:00</updated>
<author>
<name>Article Author</name>
</author>
<content type="html"><p>We thought it would be useful to put a stake in the ground down for the project and also propose a few fundamental changes which have been in discussion in the background for some time but not decided on, which has caused some confusion and indeed a lack of clarity.</p>
<h3 id="1-0-release-goals">1.0 release goals</h3>
<ul>
<li>Webpack based build process (take advantage of the very best Webpack features including tree shaking, lazy loading, etc). Yarn and Webpack will be our build setup, recomendation and tutorials.</li>
<li>Remove dependency on sprockets and dependencies on gems that depend on sprockets</li>
<li>Remove dependency on Rails and React-rails gem</li>
<li>Create Install generators for Rails, Rack, Roda (our baseline installation will be Rack, with added configuration for Rails, Roda and other Rack-based frameworks)</li>
<li>Pre-rendering for Rack (and all Rack-based projects)</li>
<li>HyperReact and HyperComponent merge into one codebase</li>
<li>HyperMesh and HyperResource co-exist but to merge over time</li>
<li>Use Redis instead of AR for pub-sub if it is available</li>
<li>New website, new positioning, correct docs, tutorials and installation</li>
</ul>
<p>We also re-affirmed our core principles.</p>
<h3 id="core-principles">Core principles</h3>
<ul>
<li>Developer productivity as highest goal - creativity and productivity go together</li>
<li>Single language - which leads to &#39;whole application&#39; thinking</li>
<li>Convention over configuration</li>
<li>DRY APIs - no layer repetition</li>
<li>React based client-side DSL</li>
<li>Fun and pure joy to work with</li>
</ul>
<h3 id="name-change-ruby-hyperloop-org-to-hyperstack-org">Name change: Ruby-Hyperloop.org to Hyperstack.org</h3>
<p>We all like the Hyperloop name, and if we could we would continue to use just Hyperloop, but without the ruby prefix Hyperloop is lost. Hearin is the paradox. As much as we are focused on one language (ruby) today, we also believe we most likely to embrace more than one language in the future. Crystal looms large for us. Therefore, being tethered to a ruby prefix is just stacking up problems for the future where the cost of change is higher. We also do not want any radical changes after our 1.0 release milestone as this milestone is to indicate the stability and steadfastness of the project.</p>
<p>We, therefore, think it is better to bite the bullet and rename the project at this stage. We have acquired the hyperstack.org domain and secured hyperstack-org as a project name on Github.</p>
<p>We plan to leave the ruby-hyperloop.org website in place with a banner explanation, redirecting visitors to the new hyperstack.org website. Our 1.0 documentation, new tutorials and new positioning will all be published on the new website only.</p>
<p>This will be the 2nd rename this project has gone through (previously we were reactrb.org) so let&#39;s hope it is the last.</p>
<p>What remains to be said is to thank you all for your participation and collaboration in this community. Let&#39;s build something special together.</p>
</content>
</entry>
<entry>
<title>Hyperloop 2018 Update</title>
<link rel="alternate" href="http://blog.url.com/blog/2018/07/26/hyperloop-2018-update/"/>
<id>http://blog.url.com/blog/2018/07/26/hyperloop-2018-update/</id>
<published>2018-07-26T09:47:00+01:00</published>
<updated>2018-07-27T12:50:09+01:00</updated>
<author>
<name>Article Author</name>
</author>
<content type="html"><p>Summer 2018 and we are approaching a 1.0 Release of Hyperloop. Our gaols for our 1.0 release have been mainly bug fixing and performance improvements made throughout the year.</p>
<p>In true feature-creep fashion, we also took on a number of substantial changes which have delayed the 1.0 release somewhat and left us with a number of working branches of Hyperloop.</p>
<p>So, here is a quick summary of where things stand. At the moment there are 4 main branches:</p>
<p><code>lap28</code>, <code>pinta</code>, <code>edge</code> and <code>ulysses</code></p>
<p>The first three are coming together to form Hyperloop 1.0. Ulysses is the future, but a long way from being ready for production - think of it as Hyperloop 2.0</p>
<p>Our immediate task is to merge lap28, pinta and edge into master and release that as Hyperloop 1.0. This work is currently ongoing.</p>
</content>
</entry>
<entry>
<title>Spring 2017 COMPS Release</title>
<link rel="alternate" href="http://blog.url.com/blog/2017/02/28/spring-2017-comps-release/"/>
<id>http://blog.url.com/blog/2017/02/28/spring-2017-comps-release/</id>
<published>2017-02-28T00:00:00+00:00</published>
<updated>2018-07-26T09:39:17+01:00</updated>
<author>
<name>Article Author</name>
</author>
<content type="html"><p>Spring 2017 heralds a major Hyperloop release. This release will be the first where we have introduced the 5 architectural constructs focused on making it easier to write Isomorphic applications.</p>
<p>This release includes a new version and renaming of all of the Hyperloop gems as well as several new concepts and gems.</p>
<p>These release notes cover the following topics:</p>
<ul>
<li><a href="#release-overview">Release Overview</a></li>
<li><a href="#gem-changes">Gem changes</a></li>
<li><a href="#new-folder-layout">New folder layout</a></li>
<li><a href="#base-class-names">Base class names</a></li>
</ul>
<h2 id="release-overview">Release Overview</h2>
<p>This release consists of:</p>
<ul>
<li>Introduction of COMPS (Components, Operations, Models, Policies and Stores) architectural concepts</li>
<li>Introduction of Hyper-Operation gem</li>
<li>Introduction of Hyper-Store gem</li>
<li>Introduction of Hyper-Spec gem</li>
<li>Introduction of a centralized Hyperloop configuration gem</li>
<li>Renaming of HyperMesh gem to Hyper-Model</li>
<li>Renaming of Express gem to Hyperloop-JS</li>
<li>Changes to state syntax from bang(!) notation to mutate method</li>
<li>Changes to all base class names (Hyperloop::Component, Hyperloop::Model, etc) for consistency</li>
<li>Changes to the location of files in a Rails project</li>
<li>New Hyperloop JS based on latest gems</li>
<li>New HyperRails gem</li>
<li>New website documentation, lived-code editing, new styling and new branding</li>
</ul>
<h2 id="gem-changes">Gem changes</h2>
<h4 id="version-numbers-and-content">Version Numbers and Content</h4>
<table><thead>
<tr>
<th>gem</th>
<th>version</th>
<th>notes</th>
</tr>
</thead><tbody>
<tr>
<td>hyper-loop</td>
<td>0.8</td>
<td>initial release</td>
</tr>
<tr>
<td>hyper-store</td>
<td>0.2.2</td>
<td>initial release</td>
</tr>
<tr>
<td>hyper-operation</td>
<td>0.5.4</td>
<td>initial release</td>
</tr>
<tr>
<td>hyper-component</td>
<td>0.12.5</td>
<td>latest hyper-react + pending fixes + compatibility <code>requires</code> (see below)</td>
</tr>
<tr>
<td>hyper-model</td>
<td>0.6.0</td>
<td>hyper-mesh 0.5.x + fixes + dependence on hyper-store and hyper-operation gems</td>
</tr>
<tr>
<td>hyperloop-js</td>
<td>0.1</td>
<td>latest gems + decoupling of Hyperloop and Opal</td>
</tr>
</tbody></table>
<h4 id="hyper-component-compatibility">Hyper-Component compatibility</h4>
<p>The hyper-component gem will include 3 compatibility modes, determined by which file you require in <code>components.rb.</code></p>
<ul>
<li><strong>Hyperloop Standard</strong>: (<code>require &#39;hyper-component&#39;</code>) In this mode you will use the new hyperloop syntax for all names, macros etc. I.e. components are defined as subclasses of <code>Hyperloop::Component</code> or using <code>Hyperloop::Component::Mixin</code>. States are changed using the <code>mutate</code> objectrather than the exclamation notation. States are declared using the <code>state</code> macro.</li>
<li><strong>HyperReact Compatibility</strong>: (<code>require &#39;hyper-react&#39;</code>) In this mode you can use either syntax, but you will get deprecation warnings, as this mode <em>will</em> go away. This mode will be provided as a bridge so developers can use Operations and Stores without having to make changes to existing components.</li>
<li><strong>DSL Only</strong> (<code>require &#39;hyper-react-dsl&#39;</code>) In this mode you will use the new syntax, however, the DSL will be limited to the base feature set provided by react.js. This mainly applies to states acting as stores. The advantage will be smaller payload size. Initially, this mode not exist but the code will be set up to support it easily in the future</li>
</ul>
<p>In addition, we will make one more release to the hyper-react and hyper-mesh gems that simply provides the hyper-component and hyper-model functionality, plus a deprecation warning. The intent is that the next time you update these gems, you will get the warning, and will know to change to the new gem names.</p>
<h4 id="store-and-operation-interoperability">Store and Operation interoperability</h4>
<p>Stores depend on <code>Hyperloop::Application::Boot</code>, which is an operation defined in the Operation gem. So that you can use stores without operations, the store gem will define a very basic boot operation <em>unless</em> Hyperloop::Application::Boot is already defined.</p>
<h4 id="hyperloop-js">Hyperloop.JS</h4>
<p>Hyperloop.JS now supports Compoennts, Operations and Stores.</p>
<p>There is no gem here, just JavaScript files. We will have two: hyperloop.js which includes Components, Operations and Stores and opal-compiler.js which includes Opal and Opal Compiler.</p>
<h2 id="new-folder-layout">New folder layout</h2>
<p>There is a folder layout within a Rails project.</p>
<p>Old folder layout:</p>
<pre class="highlight plaintext"><code>/app/views/components &lt;-- HyperReact components
/app/models/public &lt;-- HyperMesh models
/app/models &lt;-- server-only models
/app/views/components.rb &lt;-- component manifest
/app/policies &lt;-- HyperMesh policies
</code></pre>
<p>New folder layout:</p>
<pre class="highlight plaintext"><code>/app/hyperloop/components &lt;-- components
/app/hyperloop/models &lt;-- isomorphic models
/app/models &lt;-- server-only models
/app/hyperloop/operations &lt;-- isomorphic operations
/app/operations &lt;-- server-only operations
/app/hyperloop/stores &lt;-- stores
/app/hyperloop/hyperloop.rb &lt;-- hyperloop manifest
/app/policies &lt;-- policies
</code></pre>
<h2 id="base-classes-and-mixins">Base classes and Mixins</h2>
<p>Hyperloop base classes follow a consistent naming convention:</p>
<ul>
<li><code>Hyperloop::Operation</code></li>
<li><code>Hyperloop::Store</code></li>
<li><code>Hyperloop::Policy</code></li>
</ul>
<p>You can inherit from the class:</p>
<pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">Cart</span> <span class="o">&lt;</span> <span class="no">Hyperloop</span><span class="o">::</span><span class="no">Store</span>
<span class="p">.</span><span class="nf">.</span><span class="p">.</span>
<span class="nf">end</span>
</code></pre>
<p>Or mixin the module:</p>
<pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">Cart</span>
<span class="kp">include</span> <span class="no">Hyperloop</span><span class="o">::</span><span class="no">Store</span><span class="o">::</span><span class="no">Mixin</span>
<span class="p">.</span><span class="nf">.</span><span class="p">.</span>
<span class="nf">end</span>
</code></pre>
<p>Mixins available:</p>
<ul>
<li><code>Hyperloop::Store::Mixin</code></li>
<li><code>Hyperloop::Policy::Mixin</code></li>
</ul>
</content>
</entry>
<entry>
<title>Editing Flux Loop verses Decoupling</title>
<link rel="alternate" href="http://blog.url.com/blog/2017/01/28/editing-flux-loop-verses-decoupling/"/>
<id>http://blog.url.com/blog/2017/01/28/editing-flux-loop-verses-decoupling/</id>
<published>2017-01-28T00:00:00+00:00</published>
<updated>2018-07-26T09:39:17+01:00</updated>
<author>
<name>Article Author</name>
</author>
<content type="html"><p>@catmando</p>
<p>This started as some thoughts about when to use notation like</p>
<pre class="highlight ruby"><code><span class="no">AddItemToCart</span><span class="p">(</span><span class="ss">item: </span><span class="n">sku</span><span class="p">,</span> <span class="ss">qty: </span><span class="mi">1</span><span class="p">)</span> <span class="c1"># Use an operation</span>
<span class="c1"># vs</span>
<span class="no">Cart</span><span class="p">.</span><span class="nf">addItem</span><span class="p">(</span><span class="ss">item: </span><span class="n">sku</span><span class="p">,</span> <span class="ss">qty: </span><span class="mi">1</span><span class="p">)</span> <span class="c1"># Use a method on the Store</span>
</code></pre>
<p>Which in thinking it through (the answer is &#39;always use the Operation&#39;, read on for details) led me to understand what I think is the real truth about the &quot;flux loop.&quot; And the answer to that is, it is nothing really to do with the &quot;data flow&quot; but with the coupling between parts of the system.</p>
<p>Actions (and Operations, Mutations, and to some extent decorators - maybe) provide a way to decouple elements of the system.</p>
<p>In the above example, why is the Operation better? Sometime in the future, you may want to note that the current user showed interest in an SKU whenever that SKU gets added to the cart. Where does this additional code go? If you have gone down the path of directly calling <code>Cart.addItem</code> you have no place to logically add this code. You can add it the Cart, but this now couples the Cart to some other model like UserInterests. The two are pretty unrelated. So you would end up moving the logic upwards and that puts it where it belonged in the first place: the AddItemToCart Operation.</p>
<p>Having Operations (which are basically the same as Actions + Action Creators + the Dispatcher) and using them <em>whenever data is mutated</em> is a really good rule of thumb which is simple to understand, helps structure the code in a way that leaves it more maintainable, less brittle, and more reusable.</p>
<p>It also creates a &quot;one-way data flow&quot; but the problem is that I can create a system with one-way data flow that does not provide me with good decoupling between parts of the system. I can also in perfectly good flux architecture still make dumb design decisions.</p>
<p>Here are three good things that having a central point like the Dispatcher or Operations solves:</p>
<ol>
<li><strong>Decoupling Interface from Implementation</strong>
The flux Action paradigm decouples the Action protocol from the implementation completely. An Action is a separate object from the Store receiving the action. Some event handler calls the action, and the Store registers with the action. In fact, you can have multiple Stores respond to the same Action. Cool!</li>
</ol>
<p>But even without a Dispatcher you get all the biggest benefit which is the decoupling. So I think its important to understand the first goal is to give a separate name to the Action (or Operation) and which can then be associated whatever Stores need to be updated.</p>
<ol>
<li><p><strong>Debuggability</strong>*
Running everything through the Action-Dispatcher (or an Operation base class) means that you can easily trace all actions/operations. If you are using immutable data you can have even more fun. This is good!</p></li>
<li><p><strong>Keeping Store Concerns Clean</strong>
Without some entity such as Actions to decouple Stores from <em>each other</em> you end up with Store A, knowing too much about Store B. So to emphasize the earlier example: we have a cart, we want to add an item. Great. But now you also want to update a &quot;User Interest List&quot; with any item a user has added to a cart. So the naive implementation would probably have the Cart &quot;add item&quot; mechanism call some method on the UserInterestList Store. Now the Cart which seems like the more &quot;fundamental&quot; class, is linked to the UserInterestList, and the spagetti begins to tangle. </p></li>
</ol>
<p>This is a huge problem everywhere. The &quot;Action&quot; solution is a simplified version of the TrailBlazer Operation, which itself is derived from the Mutation gem. So the problem has been around for a while, and the solutions that work are similiar.</p>
<p>And here is and example of something Actions or Operations and having a central dispatcher does not solve:</p>
<p><strong>Bad class protocol design</strong><br>
We can describe how to &quot;kill&quot; a role playing character many ways. </p>
<pre class="highlight ruby"><code><span class="no">Person</span><span class="p">.</span><span class="nf">set_is_alive</span><span class="p">(</span><span class="nb">id</span><span class="p">,</span> <span class="n">boolean</span><span class="p">)</span> <span class="c1"># normal method call</span>
<span class="p">{</span><span class="ss">type: :set_is_alive</span><span class="p">,</span> <span class="ss">payload: </span><span class="p">{</span><span class="ss">id: </span><span class="nb">id</span><span class="p">,</span> <span class="ss">boolean: </span><span class="n">boolean</span><span class="p">}}</span> <span class="c1"># flux action</span>
<span class="no">SetIsAlive</span><span class="p">(</span><span class="nb">id</span><span class="p">,</span> <span class="n">boolean</span><span class="p">)</span> <span class="c1"># Operation / Action Creator</span>
<span class="c1"># BAD! what if u change "alive-ness" to be a scale instead of yes/no?</span>
<span class="no">Person</span><span class="p">.</span><span class="nf">set_life_level</span><span class="p">(</span><span class="nb">id</span><span class="p">,</span> <span class="n">integer</span><span class="p">)</span> <span class="c1"># normal method call</span>
<span class="p">{</span><span class="ss">type: :set_life_level</span><span class="p">,</span> <span class="ss">payload: </span><span class="p">{</span><span class="ss">id: </span><span class="nb">id</span><span class="p">,</span> <span class="ss">level: </span><span class="n">level</span><span class="p">}}</span> <span class="c1"># flux action</span>
<span class="no">SetLifeLevel</span><span class="p">(</span><span class="nb">id</span><span class="p">,</span> <span class="n">level</span><span class="p">)</span> <span class="c1"># Operation / Action Creator</span>
<span class="c1"># STILL BAD! Its less brittle but it still reveals too much implemenation</span>
<span class="no">Person</span><span class="p">.</span><span class="nf">kill</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
<span class="p">{</span><span class="ss">type: :kill</span><span class="p">,</span> <span class="ss">data: </span><span class="p">{</span><span class="ss">id: </span><span class="nb">id</span><span class="p">}}</span>
<span class="no">Kill</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span> <span class="c1"># Operation / Action Creator</span>
<span class="c1"># This is a much better protocol!!!</span>
</code></pre>
<p>Regardless of whether I think of my system in terms of Classes and methods, actions, or operations, I can build good protocols or bad protocols. Just declaring that I use &quot;actions&quot; to define my system does not solve this problem. People must realize that &quot;Actions&quot; are just another way to describe messages to move data between elements of the system. Just changing terminology from methods, classes or procedure calls to &#39;Actions&#39; and &#39;Stores&#39; solves nothing.</p>
<p>So there are three good reasons to use an architecture that centralizes the mutation of stores to a single point (or a single class) plus one thing such an architecture does not solve. <strong>But note: No place in that discussion did we say anything about one-way data flow.</strong> That is a side effect and frankly a distraction I think. There are going to be times where its best to violate the &quot;one-way data flow&quot; but that does not mean you have to in any way give up good design principles.</p>
<p>I think its much easier and clearer to think in terms of who mutates the stores. Providing an answer like &quot;in general it should be the Operations&quot;, is a good starting point to discovering the best way to decouple the system. I don&#39;t think saying &quot;make the data flow one way&quot; is as helpful.</p>
<h4 id="how-is-this-going-to-work-in-hyperloop">How is this going to work in Hyperloop</h4>
<p>Here is the basic approach:</p>
<pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">AddItemToCart</span> <span class="o">&lt;</span> <span class="no">HyperOperation</span>
<span class="n">param</span> <span class="ss">:sku</span>
<span class="n">param</span> <span class="ss">qty: </span><span class="mi">1</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Cart</span> <span class="o">&lt;</span> <span class="no">HyperStore</span>
<span class="n">state_reader</span> <span class="ss">items: </span><span class="no">Hash</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="o">|</span><span class="n">h</span><span class="p">,</span> <span class="n">k</span><span class="o">|</span> <span class="n">h</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span> <span class="p">},</span> <span class="ss">scope: :class</span>
<span class="n">receives</span> <span class="no">AddItemToCart</span><span class="p">,</span> <span class="ss">scope: :class</span> <span class="k">do</span>
<span class="n">state</span><span class="p">.</span><span class="nf">items!</span><span class="p">[</span><span class="n">params</span><span class="p">.</span><span class="nf">sku</span><span class="p">]</span> <span class="o">+=</span> <span class="n">params</span><span class="p">.</span><span class="nf">qty</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>(+) Nice and easy<br>
(-) Adds (maybe) 2 lines to every mutator (<code>class ... end</code>)<br>
(+) Allows for other stores to participate in the Operation<br>
(+) Clearly corresponds to the Flux model (i.e. Operation == Action + Action Creator + Dispatcher) </p>
<h3 id="improving-on-the-above">Improving on the above</h3>
<p>In many cases there is a &quot;default&quot; association between the Operation and the Store. You can see this in the names <code>Cart</code> and <code>AddItemToCart</code>. This is very common in real world examples. Given this it makes sense to namespace the actions with the store:</p>
<pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">Cart</span> <span class="o">&lt;</span> <span class="no">HyperStore</span>
<span class="k">class</span> <span class="nc">AddItem</span> <span class="o">&lt;</span> <span class="no">HyperOperation</span>
<span class="n">param</span> <span class="ss">:sku</span>
<span class="n">param</span> <span class="ss">qty: </span><span class="mi">1</span>
<span class="k">end</span>
<span class="n">state_reader</span> <span class="ss">items: </span><span class="no">Hash</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="o">|</span><span class="n">h</span><span class="p">,</span> <span class="n">k</span><span class="o">|</span> <span class="n">h</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span> <span class="p">},</span> <span class="ss">scope: :class</span>
<span class="n">receives</span> <span class="no">AddItem</span><span class="p">,</span> <span class="ss">scope: :class</span> <span class="k">do</span>
<span class="p">.</span><span class="nf">.</span><span class="p">.</span>
<span class="nf">end</span>
<span class="k">end</span>
</code></pre>
<p>We have not changed much, but things look much logical. You would say:</p>
<pre class="highlight ruby"><code> <span class="no">Cart</span><span class="p">.</span><span class="nf">items</span> <span class="c1"># works just like a scope</span>
<span class="no">Cart</span><span class="o">::</span><span class="no">AddItem</span><span class="p">(.</span><span class="nf">.</span><span class="o">.</span><span class="p">)</span> <span class="c1"># stands out!!! must be a mutator</span>
</code></pre>
<p>You can still have other unrelated Stores receive AddItem:</p>
<pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">UserInterestList</span> <span class="o">&lt;</span> <span class="no">HyperStore</span>
<span class="n">receives</span> <span class="no">Cart</span><span class="o">::</span><span class="no">AddItem</span><span class="p">,</span> <span class="ss">scope: :class</span> <span class="k">do</span>
<span class="p">.</span><span class="nf">.</span><span class="p">.</span>
<span class="nf">end</span>
<span class="k">end</span>
</code></pre>
<p>And because we know that Cart is by default related to AddItem, we can make sure that Cart always receives AddItem first, thus doing away with a common reason for needing to explicitly specify the order that Stores should receive an action.</p>
<p>If it&#39;s not obvious which class the Operation belongs (you can probably see it right in the name) to then it really is its own thing and should be placed in its own namespace. So for example:
<code>ruby
class ResetToDefaults &lt; HyperOperation
end
</code>
Clearly there is no associated Store, so ResetToDefaults stands alone.</p>
<p>While it&#39;s a little more typing (2 lines) you now can give a robust specification to the parameters coming into the Operation. This seems important if the rule of thumb is that Operations are controlling mutations of our Stores</p>
<pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">Cart</span> <span class="o">&lt;</span> <span class="no">HyperStore</span>
<span class="k">class</span> <span class="nc">AddItem</span> <span class="o">&lt;</span> <span class="no">HyperOperation</span>
<span class="n">param</span> <span class="ss">:sku</span><span class="p">,</span> <span class="ss">type: </span><span class="no">String</span><span class="p">,</span> <span class="ss">matches: </span><span class="no">SKU_PATTERN</span>
<span class="n">param</span> <span class="ss">qty: </span><span class="mi">1</span><span class="p">,</span> <span class="ss">type: </span><span class="no">Numeric</span><span class="p">,</span> <span class="ss">minimum: </span><span class="mi">1</span>
<span class="k">end</span>
<span class="p">.</span><span class="nf">.</span><span class="p">.</span>
<span class="nf">end</span>
</code></pre>
<p>Finally note that nesting the declaration of the Operation inside a Store, does not prevent you from adding special logic not related to the Store elsewhere:</p>
<pre class="highlight ruby"><code><span class="c1"># some where else in the code:</span>
<span class="k">class</span> <span class="nc">Cart</span><span class="o">::</span><span class="no">AddItem</span> <span class="o">&lt;</span> <span class="no">HyperOperation</span>
<span class="k">def</span> <span class="nf">execute</span>
<span class="no">ConfirmItemAvailability</span><span class="p">(</span><span class="ss">sku: </span><span class="n">sku</span><span class="p">).</span><span class="nf">then</span> <span class="p">{</span> <span class="k">super</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Other questions:</p>
<ul>
<li><p><strong>Can Stores Invoke Operations</strong>
In general no. Stores should be kept as simple as possible. If possible move invocation of the Operation upwards into another Operation&#39;s execute method. The obvious exception would be if the Store is providing a stream of data from an asynchronous source. In this case, a Store&#39;s &#39;getter&#39; is going to detect the Data has run out, and can invoke an Operation to get more. The Operation will be asynchronous and when it resolves can inform the Store that it can update its state with new data. The way Operations, states, and promises work together make this straight forward to do.</p></li>
<li><p><strong>Can Operations Read From Stores</strong>
Yes. Often an Operation will read from one Store to determine if it should update another store. </p></li>
<li><p><strong>Can Operations Invoke Other Operations</strong>
Yes. Note that Operations return promises, so asynchronous operation is assumed, Operations can be easily chained.</p></li>
</ul>
</content>
</entry>
<entry>
<title>Comparing Redux with Hyperloop</title>
<link rel="alternate" href="http://blog.url.com/blog/2017/01/17/comparing-redux-with-hyperloop/"/>
<id>http://blog.url.com/blog/2017/01/17/comparing-redux-with-hyperloop/</id>
<published>2017-01-17T00:00:00+00:00</published>
<updated>2018-07-26T09:39:17+01:00</updated>
<author>
<name>Article Author</name>
</author>
<content type="html"><p>@catmando</p>
<p>In trying to find how Hyperloop models and flux-stores relate, I was rereading the Redux tutorials. After having been away from that for a while I was amazed how clean Hyperloop&#39;s HyperReact DSL is compared to the typical JSX code.</p>
<p>For example here is a comparison of <a href="https://github.com/reactjs/redux/tree/master/examples/todomvc">Redux TodoMVC</a> and <a href="https://github.com/ruby-hyperloop/todo-tutorial">Hyperloop TodoMVC</a> which provide the same Todo UI and function. (* <em>note that the code presented is slightly different from the linked Hyperloop tutorial as it uses the most recent version of the DSL.</em>)</p>
<p>Here are the component code files, which are roughly divided the same way between the two apps.</p>
<table><thead>
<tr>
<th>JS files</th>
<th>React/Redux size</th>
<th>Hyperloop size</th>
<th>Ruby Files</th>
</tr>
</thead><tbody>
<tr>
<td>Footer.js</td>
<td>71</td>
<td>29</td>
<td>footer_link.rb, footer.rb</td>
</tr>
<tr>
<td>Header.js, MainSection.js</td>
<td>103</td>
<td>25</td>
<td>index.rb</td>
</tr>
<tr>
<td>TodoItem.js</td>
<td>65</td>
<td>21</td>
<td>todo_item.rb</td>
</tr>
<tr>
<td>TodoTextInput.js</td>
<td>53</td>
<td>20</td>
<td>edit_item.rb</td>
</tr>
<tr>
<td>Total</td>
<td>292</td>
<td>95</td>
<td></td>
</tr>
</tbody></table>
<p>In addition there are the following &quot;store/action/model&quot; definition files.</p>
<table><thead>
<tr>
<th>JS files</th>
<th>React/Redux size</th>
<th>Hyperloop size</th>
<th>Ruby Files</th>
</tr>
</thead><tbody>
<tr>
<td>action/index.js</td>
<td>8</td>
<td></td>
<td></td>
</tr>
<tr>
<td>constants/...</td>
<td>9</td>
<td></td>
<td></td>
</tr>
<tr>
<td>reducers/todos.js</td>
<td>55</td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td>4</td>
<td>models/public/todo.rb</td>
</tr>
<tr>
<td>total</td>
<td>72</td>
<td>4</td>
<td></td>
</tr>
</tbody></table>
<table><thead>
<tr>
<th></th>
<th>React/Redux</th>
<th>Hyperloop</th>
</tr>
</thead><tbody>
<tr>
<td>Total</td>
<td>364</td>
<td>99</td>
</tr>
</tbody></table>
<p>Note that not only is the Hyperloop version less than 1/3 the size, it is persisting and synchronizing the todo list across multiple browsers! </p>
<p>There is nothing wrong with more lines of code, as long as the extra code is adding extra comprehension and making the code easier to maintain. Unfortunately, I would say this is not the case!</p>
<p>I looked specifically at the TodoItem.js (65 SLOC) file and compared it to todo_item.rb (21 SLOC) file.</p>
<p>First, there is a preamble in the JS file (4 lines) which does not exist in the ruby file.</p>
<pre class="highlight javascript"><code><span class="kr">import</span> <span class="nx">React</span><span class="p">,</span> <span class="p">{</span> <span class="nx">Component</span><span class="p">,</span> <span class="nx">PropTypes</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'react'</span>
<span class="kr">import</span> <span class="nx">classnames</span> <span class="nx">from</span> <span class="s1">'classnames'</span>
<span class="kr">import</span> <span class="nx">TodoTextInput</span> <span class="nx">from</span> <span class="s1">'./TodoTextInput'</span>
</code></pre>
<p>Then we have the class wrapper which is essentially the same 2 lines in JS vs Ruby:</p>
<pre class="highlight javascript"><code><span class="kr">export</span> <span class="k">default</span> <span class="kr">class</span> <span class="nx">TodoItem</span> <span class="kr">extends</span> <span class="nx">Component</span> <span class="p">{</span>
<span class="p">...</span>
<span class="p">}</span>
</code></pre><pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">TodoItem</span> <span class="o">&lt;</span> <span class="no">React</span><span class="o">::</span><span class="no">Component</span><span class="o">::</span><span class="no">Base</span>
<span class="p">.</span><span class="nf">.</span><span class="p">.</span>
<span class="nf">end</span>
</code></pre>
<p>Then we define the properties, and state (11 lines in JSX vs 3 in Ruby)</p>
<pre class="highlight javascript"><code> <span class="kr">static</span> <span class="nx">propTypes</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">todo</span><span class="p">:</span> <span class="nx">PropTypes</span><span class="p">.</span><span class="nx">object</span><span class="p">.</span><span class="nx">isRequired</span><span class="p">,</span>
<span class="na">editTodo</span><span class="p">:</span> <span class="nx">PropTypes</span><span class="p">.</span><span class="nx">func</span><span class="p">.</span><span class="nx">isRequired</span><span class="p">,</span>
<span class="na">deleteTodo</span><span class="p">:</span> <span class="nx">PropTypes</span><span class="p">.</span><span class="nx">func</span><span class="p">.</span><span class="nx">isRequired</span><span class="p">,</span>
<span class="na">completeTodo</span><span class="p">:</span> <span class="nx">PropTypes</span><span class="p">.</span><span class="nx">func</span><span class="p">.</span><span class="nx">isRequired</span>
<span class="p">}</span>
<span class="nx">state</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">editing</span><span class="p">:</span> <span class="kc">false</span>
<span class="p">}</span>
</code></pre><pre class="highlight ruby"><code> <span class="n">param</span> <span class="ss">:todo</span><span class="p">,</span> <span class="ss">type: </span><span class="no">Todo</span>
<span class="n">define_state</span> <span class="ss">editing: </span><span class="kp">false</span>
</code></pre>
<p>The JS version is simply more verbose. In addition the JS code has an additional 3 declarations for the <code>deleteTodo</code>, <code>editTodo</code> and <code>completeTodo</code> params. Because Hyperloop uses ActiveRecord, reactive (read flux) methods like <code>delete</code> and the <code>complete</code> accessor are built into the <code>Todo</code> model - no extra charge! </p>
<p>In the JS file we now have 2 helper methods (13 SLOC) which don&#39;t exist in the Ruby version:</p>
<pre class="highlight javascript"><code> <span class="nx">handleDoubleClick</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span> <span class="na">editing</span><span class="p">:</span> <span class="kc">true</span> <span class="p">})</span>
<span class="p">}</span>
<span class="nx">handleSave</span> <span class="o">=</span> <span class="p">(</span><span class="nx">id</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">text</span><span class="p">.</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">deleteTodo</span><span class="p">(</span><span class="nx">id</span><span class="p">)</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">editTodo</span><span class="p">(</span><span class="nx">id</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span> <span class="na">editing</span><span class="p">:</span> <span class="kc">false</span> <span class="p">})</span>
<span class="p">}</span>
</code></pre>
<p>These methods are defined in blocks directly in the Ruby render method, so there is a bit of a stylistic choice here. If we had pulled them out as methods <code>handleDoubleClick</code> would also be three lines long, but <code>handleSave</code> would only be four lines, as once again ActiveRecord is going to make handling the Todo&#39;s internal state easier.</p>
<p>Finally we get to the <code>render</code> method. In React/Redux it looks like this:
```javascript
render() {
const { todo, completeTodo, deleteTodo } = this.props</p>
<pre class="highlight plaintext"><code>let element
if (this.state.editing) {
element = (
&lt;TodoTextInput text={todo.text}
editing={this.state.editing}
onSave={(text) =&gt; this.handleSave(todo.id, text)} /&gt;
)
} else {
element = (
&lt;div className="view"&gt;
&lt;input className="toggle"
type="checkbox"
checked={todo.completed}
onChange={() =&gt; completeTodo(todo.id)} /&gt;
&lt;label onDoubleClick={this.handleDoubleClick}&gt;
{todo.text}
&lt;/label&gt;
&lt;button className="destroy"
onClick={() =&gt; deleteTodo(todo.id)} /&gt;
&lt;/div&gt;
)
}
return (
&lt;li className={classnames({
completed: todo.completed,
editing: this.state.editing
})}&gt;
{element}
&lt;/li&gt;
)
</code></pre>
<p>}
```</p>
<p>Before we look at the details of these 34 lines (vs 15 in Ruby) there are some JS statements which are simply not needed in Ruby, and which really clutter up reading the code. These are:</p>
<pre class="highlight javascript"><code> <span class="kr">const</span> <span class="p">{</span> <span class="nx">todo</span><span class="p">,</span> <span class="nx">completeTodo</span><span class="p">,</span> <span class="nx">deleteTodo</span> <span class="p">}</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span>
<span class="kd">let</span> <span class="nx">element</span>
<span class="p">...</span>
<span class="nx">element</span> <span class="o">=</span> <span class="p">(</span>
<span class="p">...</span>
<span class="p">)</span>
<span class="p">...</span>
<span class="nx">element</span> <span class="o">=</span> <span class="p">(</span>
<span class="p">...</span>
<span class="p">)</span>
<span class="p">...</span>
<span class="k">return</span> <span class="p">(</span>
<span class="p">...</span>
<span class="p">)</span>
</code></pre>
<p>These 8 lines which are almost 25% of the JS render method, and add very little clarity to the method. What do these 8 lines do?</p>
<p>First we reassign the props to intermediate constants presumably to save a little time, and to make it so we can shorten <code>this.props[:todo]</code> to just <code>todo</code>. In Hyperloop you access props more directly using the <code>params</code> object which takes care of accessing and caching the property, so you would say <code>params.todo</code>. <em>A note: originally you could just say <code>todo</code> without the <code>params.</code> prefix, but it was determined that made the code harder to read. So this behavior is being deprecated. A case where more typing is helpful.</em></p>
<p>Then (for stylistic reasons I assume) we compute the child of the <code>li</code> element before actually generating the element. Perhaps the mix of JSX and JS code would quickly get confusing if nested too deeply?</p>
<p>Finally, you have to wrap the whole thing in a return statement, which is just an artifact of JS.</p>
<p>Basically what I see happening here is that JS/JSX is more verbose, so in order to add comprehension, the flow of the code is broken up, methods are added, and intermediate values are introduced. The result is a snowball effect.</p>
<p>Here is complete ruby class for comparison.
```ruby
class TodoItem &lt; React::Component::Base</p>
<p>param :todo, type: Todo
define_state editing: false</p>
<p>render(LI, class: &#39;todo-item&#39;) do
if state.editing
EditItem(todo: todo).
on(:save) do
todo.delete if todo.text.blank?
state.editing! false
end.
on(:cancel) { state.editing! false }
else
INPUT(class: :toggle, type: :checkbox, checked: params.todo.completed).
on(:click) { params.todo.update(completed: !params.todo.completed }
LABEL { params.todo.title }.on(:doubleClick) { state.editing! true }
A(class: :destroy).on(:click) { params.todo.destroy }
end
end
end
```</p>
<p>and here is the complete JSX class:
```jsx
import React, { Component, PropTypes } from &#39;react&#39;
import classnames from &#39;classnames&#39;
import TodoTextInput from &#39;./TodoTextInput&#39;</p>
<p>export default class TodoItem extends Component {
static propTypes = {
todo: PropTypes.object.isRequired,
editTodo: PropTypes.func.isRequired,
deleteTodo: PropTypes.func.isRequired,
completeTodo: PropTypes.func.isRequired
}</p>
<p>state = {
editing: false
}</p>
<p>handleDoubleClick = () =&gt; {
this.setState({ editing: true })
}</p>
<p>handleSave = (id, text) =&gt; {
if (text.length === 0) {
this.props.deleteTodo(id)
} else {
this.props.editTodo(id, text)
}
this.setState({ editing: false })
}</p>
<p>render() {
const { todo, completeTodo, deleteTodo } = this.props</p>
<pre class="highlight plaintext"><code>let element
if (this.state.editing) {
element = (
&lt;TodoTextInput text={todo.text}
editing={this.state.editing}
onSave={(text) =&gt; this.handleSave(todo.id, text)} /&gt;
)
} else {
element = (
&lt;div className="view"&gt;
&lt;input className="toggle"
type="checkbox"
checked={todo.completed}
onChange={() =&gt; completeTodo(todo.id)} /&gt;
&lt;label onDoubleClick={this.handleDoubleClick}&gt;
{todo.text}
&lt;/label&gt;
&lt;button className="destroy"
onClick={() =&gt; deleteTodo(todo.id)} /&gt;
&lt;/div&gt;
)
}
return (
&lt;li className={classnames({
completed: todo.completed,
editing: this.state.editing
})}&gt;
{element}
&lt;/li&gt;
)
</code></pre>
<p>}
}
```</p>
<p>I didn&#39;t intend this to be such a rant, and it probably sounds more negative than I intend this to be.</p>
<p>Hyperloop is built on top of React, which is a great library. The problem is that JS just doesn&#39;t have the expressive power especially when it comes to meta-programming and creating DSLs that Ruby does. Instead of a nice clean syntax the mix of HTML and JS presented by JSX is confusing, and to de-confuse things you add more code. Furthermore, because Hyperloop is also built on tried and true of ActiveRecord, again you have an increase in comprehension with a reduction in code.</p>
</content>
</entry>
<entry>
<title>Hyperloop is born</title>
<link rel="alternate" href="http://blog.url.com/blog/2016/09/08/Hyperloop-is-born/"/>
<id>http://blog.url.com/blog/2016/09/08/Hyperloop-is-born/</id>
<published>2016-09-08T06:50:00+01:00</published>
<updated>2018-07-26T09:39:17+01:00</updated>
<author>
<name>Article Author</name>
</author>
<content type="html"><p>Reactrb is being renamed Ruby Hyperloop to reflect the change in emphasis of the project. We have chosen Hyperloop as an umbrella name for the project as it is more expansive and allows us to build out Hyperloop as a web application framework.</p>
<p>React and Reactrb (being renamed HyperReact) remain fundamental parts of this project.</p>
<h2 id="gems">Gems</h2>
<p>All of the Hyperloop core gems will take on a Hyper-name. The naming convention will be HyperReact when discussion the gem and the actual gem will be <code>hyper-rect</code>. All of the gems will follow this convention.</p>
<ul>
<li>Reactrb becomes <strong>HyperReact</strong></li>
<li>Reactrb Router becomes <strong>HyperRouter</strong></li>
<li>Reactive Record and Synchromesh will be merged to become one gem, <strong>HyperMesh</strong></li>
<li>Reactrb Rails Generator becomes <strong>HyperRails</strong></li>
<li>Reactrb Express becomes <strong>Hyperloop Express</strong></li>
</ul>
<h2 id="website">Website</h2>
<ul>
<li>Reactrb.org is changing to <strong>ruby-hyperloop.io</strong></li>
<li>The goal of this refactor is to reposition Reactrb as an umbrella project for Reactrb and associated Isomorphic ruby technologies</li>
<li>The emphasis of the site will be to show how simple Reactrb is to use and also to show best practice for taking it further (Stores, etc). There will be a few tutorials.</li>
<li>The new Reactrb.org will not try to mirroring the React(JS) site – but will have its own identity and structure but it will use as much of the existing Reactrb content as possible</li>
<li>Remove all the original React(JS) text and structure (basically remove everything that comes from the original React site and does not pertain to Reactrb)</li>
<li>New fresh looking design</li>
<li>The new site documentation will include Architectural and + Pattern discussions, describing best practice when working with React like components and stores</li>
<li>There will be a section on Reactrb development tools (Opal Console) and techniques</li>
<li>All of the above Gem’s documentation should be on reactrb.org. The individual Gem’s Read-me’s should be minimal and refer to each Gem’s page on reactrb.org so we can emphasize that these gems are a part of the same family and explain how they work together include installation, usage and best practice instructions for use with:
<ul>
<li>Rails</li>
<li>Sinatra</li>
<li>Webpack &amp; NPM</li>
</ul></li>
<li>Will still include Live Ruby examples through Opal Playground</li>
<li>The site will continue to be hosted on Github pages but the underlying technology will change to:
<ul>
<li>Reactrb Express</li>
<li>Middleman</li>
</ul></li>
</ul>
<p>The changes will be made over time so some Gems, Docs and Tutorials might reference Reactrb or their previous names.</p>
</content>
</entry>
</feed>