This repository was 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
Expand file tree
/
Copy pathindex.html
More file actions
995 lines (808 loc) · 54.4 KB
/
index.html
File metadata and controls
995 lines (808 loc) · 54.4 KB
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
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv='X-UA-Compatible' content='IE=edge;chrome=1' />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="google-site-verification" content="qjqVOvRhHfsuDbLOvaAnj2mkUZJ9Xwzg84TMVXduvUc" />
<meta charset="UTF-8">
<title>Hyperloop - Technology Showcase Tutorial</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://unpkg.com/react@15/dist/react.min.js"></script>
<script src="https://unpkg.com/react-dom@15/dist/react-dom.min.js"></script>
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.5/marked.min.js"></script>
<!-- Opal and Hyperloop -->
<script src="http://cdn.opalrb.org/opal/current/opal.min.js"></script>
<script src="https://rawgit.com/ruby-hyperloop/hyperloop-js/master/dist/opal.min.js"></script>
<script src="https://rawgit.com/ruby-hyperloop/hyperloop-js/master/dist/hyperloop.min.js"></script>
<script src="https://rawgit.com/ruby-hyperloop/hyperloop-js/master/dist/hyperloop-compiler.min.js"></script>
<!-- If you want local copies... -->
<!-- <script src="../../javascripts/opal-compiler.js"></script> -->
<!-- <script src="../../javascripts/hyperloop.js"></script> -->
<script src="https://npmcdn.com/tether@1.2.4/dist/js/tether.min.js"></script>
<script src="../../javascripts/bootstrap.min.js"></script>
<script src="../../javascripts/codemirror.js"></script>
<script src="../../javascripts/ruby.js"></script>
<script src="../../javascripts/matchbrackets.js"></script>
<script src="../../javascripts/react_player.js"></script>
<script src="../../javascripts/highlight.pack.js"></script>
<!-- Components are compiled by Hyperloop Express -->
<script type="text/ruby">
class CodeMirror < Hyperloop::Component
param :code, type: String
param :heading, default: "Code"
param :rows, type: Integer, default: 0
param :top_level_component, type: String
before_mount do
r=rand(2**256).to_s(36)[0..7]
@div_code = "code_#{r}"
@div_result = "result_#{r}"
end
after_mount do
# puts params.code
@editor = `CodeMirror(document.getElementById(#{@div_code}), {
value: #{params.code.to_s},
mode: 'text/x-ruby',
matchBrackets: true,
lineNumbers: false,
indentUnit: 2,
theme: 'github'
});`
`#{@editor}.on('change', #{lambda {on_change} })`
`#{@editor}.setSize(null, #{@editor}.defaultTextHeight()*#{params.rows})` unless params.rows == 0
execute_code
end
render(DIV) do
div.card {
div.card_header { params.heading }
div(id: @div_code)
div.card_header do
img(src: '../../images/hyperloop-logo-small-white.png' , width:'25')
span { ' ' }
'Live editor results'
end
div.card_block(id: @div_result)
}
end
def on_change
execute_code
end
def execute_code
begin
code = `#{@editor}.getValue()`
# puts code
code += "\nElement['##{@div_result}'].render(#{params.top_level_component})"
compiled_code = Opal::Compiler.new(code).compile
`ReactDOM.unmountComponentAtNode(document.getElementById(#{@div_result}));`
# Dispatchers and Receivers example works but Steps example breaks
# Hyperloop::Context.reset!
# `eval(#{compiled_code})`
# Hyperloop::Application::Boot.run()
# Steps example works but Dispatchers and Receivers example breaks
`eval(#{compiled_code})`
Hyperloop::Context.reset!
Hyperloop::Application::Boot.run()
component = Module.const_get params.top_level_component
# we need to see if the component is valid - try checking if it can render static markup
# the following line generates: `undefined method to_n for SimpleComponent`
# if React.render_to_static_markup( component ).empty?
# invalid_component_message
# end
rescue Exception => e
@time_out = after(0.1) do
unable_to_compile_message e.message
end
end
end
def invalid_component_message
message = div.text_danger do
h3.text_danger {"Oops, invalid Component..."}
p { "Your Component has been rejected by React. A valid Component must have a render macro and return just one HTML element." }
end
Element["##{@div_result}"].render{ message }
end
def unable_to_compile_message reason
message = div.text_danger do
h3.text_danger {"Can't compile..."}
p { reason }
end
Element["##{@div_result}"].render{ message }
end
end
</script>
<script type="text/ruby">
Document.ready? do
Element.find('div.codemirror-live-edit').each do |mount_point|
heading = mount_point.attr('data-heading')
rows = mount_point.attr('data-rows')
top_level_component = mount_point.attr('data-top-level-component')
code = Element[mount_point].find('pre').text.strip
params = {code: code, top_level_component: top_level_component}
params = params.merge({heading: heading}) if heading
params = params.merge({rows: rows.to_i}) if rows
codemirror_component = Object.const_get('CodeMirror')
React.render(React.create_element(codemirror_component, params ), mount_point)
end
end
</script>
<script type="text/ruby">
class ToggleCodemirror < Hyperloop::Component
param :code, type: String
param :heading, default: "Code"
param :rows, type: Integer, default: 0
param :top_level_component, type: String
param :show_code
before_mount do
r=rand(2**256).to_s(36)[0..7]
@div_code = "code_#{r}"
@div_result = "result_#{r}"
end
after_mount do
# puts params.code
@editor = `CodeMirror(document.getElementById(#{@div_code}), {
value: #{params.code.to_s},
mode: 'text/x-ruby',
matchBrackets: true,
lineNumbers: false,
indentUnit: 2,
readOnly: true,
theme: 'github'
});`
`#{@editor}.setSize(null, #{@editor}.defaultTextHeight()*#{params.rows})` unless params.rows == 0
Element['.codediv'].hide('')
mutate.show_code false
end
render(DIV) do
div.card {
div.card_header do
div(class: 'row') do
div(class: 'col-md-6') do
img(src: '../../images/hyperloop-logo-small-white.png' , width:'25')
span { ' ' }
span { params.heading }
end
div(class: 'col-md-6 align-right') do
toggle_link
end
end
end
div(id: @div_code, class: 'codediv')
}
end
def toggle_link
BUTTON(class: 'btn btn-info btn-sm') do
state.show_code ? "Click to hide code" : "Click to show code"
end.on(:click) do |ev|
mutate.show_code !state.show_code
state.show_code ? Element["##{@div_code}"].show('') : Element["##{@div_code}"].hide('')
end
end
end
</script>
<script type="text/ruby">
Document.ready? do
Element.find('div.togglecode').each do |mount_point|
heading = mount_point.attr('data-heading')
rows = mount_point.attr('data-rows')
top_level_component = mount_point.attr('data-top-level-component')
code = Element[mount_point].find('pre').text.strip
params = {code: code, top_level_component: top_level_component}
params = params.merge({heading: heading}) if heading
params = params.merge({rows: rows.to_i}) if rows
codemirror_component = Object.const_get('ToggleCodemirror')
React.render(React.create_element(codemirror_component, params ), mount_point)
end
end
</script>
<link href="../../stylesheets/bootstrap.min.css" rel="stylesheet" />
<link href="../../stylesheets/typography.css" rel="stylesheet" />
<link href="../../stylesheets/override.css" rel="stylesheet" />
<link href="../../stylesheets/divtable.css" rel="stylesheet" />
<!-- <link href="../../stylesheets/code.css" rel="stylesheet" /> -->
<link href="../../stylesheets/github.css" rel="stylesheet" />
<link href="../../stylesheets/highlighting.css" rel="stylesheet" />
<link href="../../stylesheets/codemirror.css" rel="stylesheet" />
<link href="../../stylesheets/monokai-sublime.css" rel="stylesheet" />
<link rel="alternate" type="application/atom+xml" title="Atom Feed" href="/feed.xml" />
<!-- Favicons -->
<link rel="apple-touch-icon" sizes="180x180" href="../../images/apple-touch-icon.png">
<link rel="icon" type="image/png" href="../../images/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="../../images/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="/images/manifest.json">
<link rel="mask-icon" href="../../images/safari-pinned-tab.svg" color="#e81176">
<meta name="theme-color" content="#ffffff">
</head>
<body>
<div class="navbarbackgroundcolor">
<div class="container">
<nav class="navbar navbar-ligh">
<button class="navbar-toggler hidden-sm-up" type="button" data-toggle="collapse" data-target="#exCollapsingNavbar2" aria-controls="exCollapsingNavbar2" aria-expanded="false" aria-label="Toggle navigation">
☰
</button>
<div class="collapse navbar-toggleable-xs" id="exCollapsingNavbar2">
<ul class="nav navbar-nav">
<li class="nav-item">
<a href="/" class="nav-link"><span class='navfirstletter'>H</span>yperloop</a>
</li>
<li class="nav-item">
<a href="/start/components" class="nav-link active"><span class='navfirstletter'>S</span>tart</a>
</li>
<li class="nav-item">
<a href="/installation" class="nav-link"><span class='navfirstletter'>I</span>nstallation</a>
</li>
<li class="nav-item">
<a href="/tutorials" class="nav-link"><span class='navfirstletter'>T</span>utorials</a>
</li>
<li class="nav-item">
<a href="/gems" class="nav-link"><span class='navfirstletter'>G</span>ems</a>
</li>
<li class="nav-item">
<a href="https://github.com/ruby-hyperloop" class="nav-link"><span class='navfirstletter'>G</span>ithub</a>
</li>
<li class="nav-item">
<a href="/tools" class="nav-link"><span class='navfirstletter'>T</span>ools</a>
</li>
<li class="nav-item">
<a href="/docs/architecture" class="nav-link"><span class='navfirstletter'>D</span>ocs</a>
</li>
<li class="nav-item">
<a href="/help" class="nav-link"><span class='navfirstletter'>H</span>elp</a>
</li>
<li class="nav-item">
<a href="/blog" class="nav-link"><span class='navfirstletter'>B</span>log</a>
</li>
<!-- <li class="nav-item">
<input type="text" name="search" />
</li> -->
</ul>
</div>
</nav>
</div>
</div>
<div class="jumbotron page-header">
<div class="container">
<div class="row hidden-sm-down">
<div class="col-md-2">
<div class="hyperlooplogo">
</div>
</div>
<div class="col-md-8">
<h1 class="display-4 project-name">Hyperloop</h1>
<h4 class="display-7 project-tagline">
Tutorials, screencasts & videos
</h4>
</div>
</div>
<div class="row hidden-md-up">
<div class="col-md-3">
<div class="hyperlooplogo">
</div>
</div>
<div class="col-md-9">
<h1 class="h1 project-name center-text">Hyperloop</h1>
</br>
<h2 class="h5 project-tagline center-text">
Tutorials, screencasts & videos
</h2>
</div>
</div>
</div>
</div>
<div class="page-header-underline">
<div class="container">
</div>
</div>
<div class="container">
<div class="row">
<div class="col-md-3 sidenavcol">
<div>
<p>Your website or applications use <b>Hyperloop</b>, don't hesitate to tell us!</p>
<button type="button" class="btn btn-primary btn-lg btn-hyperloopgitter" onclick="location.href='https://gitter.im/ruby-hyperloop/chat';">Gitter Chat</button>
</div>
<div class="getstarted">
<br>
<hr>
<p>NEWS: RubyHyperloop is to be renamed Hyperstack. Come and see the work-in-progress site: </p>
<!-- <button type="button" class="btn btn-primary btn-lg btn-hyperlooppink" onclick="https://hyperstack.org';">https://hyperstack.org</button> -->
<a href='https://hyperstack.org'>https://hyperstack.org</a>
</div>
<br>
</div>
<div class="col-md-9 main-content">
<h2 id="hyperloop-technology-showcase-tutorial">Hyperloop Technology Showcase Tutorial</h2>
<p><strong>Please note: This tutorial was written prior to the Reactrb gem being renamed HyperReact. Additionaly, the section at the end of the tutorial on ReactiveRecord and Synchromesh is out of date as these gems have been merged into a new gem - HyperMesh. All of this will be updated soon.</strong></p>
<p><a href="https://github.com/barriehadfield/reactrb-showcase">The source code to this tutorial is here</a></p>
<p>This tutorial builds a simple Rails application showcasing Reactrb, Opal, NPM, Webpack, React Bootstrap, Reactive Record, Synchromesh, Rearcrb Router and other associated technologies. This showcase is intended to be a companion project to the excellent Reactrb tutorials already written which you can find in <a href="#further-reading">Further Reading</a> below.</p>
<p>This Showcase application will mix native React and Reactrb components, be styled by Bootstrap CSS (using ReactBootstrap), display a video (using a native React component) and use Reactrb Reactive Record and Synchromesh to handle data for a Post and Comments feed.</p>
<p>The Showcase application will look like this:</p>
<p><img src="../../images/screen.png" alt="Screen" /></p>
<h3 id="technologies-highlighted-in-this-showcase-tutorial">Technologies highlighted in this Showcase Tutorial</h3>
<ul>
<li>For the backend we are using Rails 4.2.6 with Ruby 2.3.1</li>
<li>NPM and Webpack to manage front end assets</li>
<li><a href="https://github.com/reactjs/react-rails">React Rails</a> to use React with Rails</li>
<li><a href="https://github.com/ruby-hyperloop/hyper-react">HyperReact</a> to write reactive UI components with Ruby's elegance</li>
<li><a href="https://react-bootstrap.github.io/">React Bootstrap</a> to show how to use native React components in Reactrb</li>
<li><a href="https://github.com/fkchang/opal-hot-reloader">Reactrb Hot-Reloader and Opal IRB</a> for programmer joy and hot-loading with developing</li>
</ul>
<h3 id="using-npm-and-webpack-alongside-rails">Using NPM and Webpack alongside Rails</h3>
<p>Ruby libraries are distributed as gems, and are managed in your Rails app using the Gemfile and bundler.</p>
<p>In the Javascript world things are still evolving but I have found that the easiest way to manage Javascript libraries is using NPM (Node Package Manager) and Webpack. Pretty much every front end library is packaged with NPM these days so it is easy to get help and most things just work.</p>
<p>Happily NPM, Webpack, Rails, and Reactrb can all play together very nicely.</p>
<ul>
<li><a href="https://www.npmjs.com/">NPM</a></li>
<li><a href="https://www.npmjs.com/package/webpack">Webpack</a></li>
</ul>
<p>This tutorial requires that Ruby, Rails, NPM and Webpack are installed. Please see their websites for installation instructions.</p>
<h2 id="setup">Setup</h2>
<h3 id="step-1-creating-a-new-rails-application">Step 1: Creating a new Rails application</h3>
<pre class="highlight plaintext"><code>rails new reactrb-showcase
cd reactrb-showcase
bundle install
</code></pre>
<p>You should have a empty Rails application</p>
<pre class="highlight plaintext"><code>bundle exec rails s
</code></pre>
<p>And in your browser</p>
<pre class="highlight plaintext"><code>http://localhost:3000/
</code></pre>
<p>You should be seeing the Rails Welcome aboard page. Great, Rails is now installed. Lets get started with the interesting stuff.</p>
<h3 id="step-2-adding-hyperreact">Step 2: Adding HyperReact</h3>
<p><a href="https://github.com/ruby-hyperloop/reactrb-rails-generator/tree/hyper-rails">We will use the HyperRails Generator Gem</a></p>
<p>In your <code>Gemfile</code> under the development group add</p>
<pre class="highlight plaintext"><code>gem "hyper-rails"
</code></pre>
<p>then</p>
<pre class="highlight plaintext"><code>bundle install
bundle exec rails g hyperloop:install
bundle update
</code></pre>
<p>At this stage HyperReact is installed but we don't have any components yet. Lets create one via the generator:</p>
<pre class="highlight plaintext"><code>rails g hyperloop:component Home::Show
</code></pre>
<p>This will add a new Component at app/views/components/home/show.rb</p>
<pre class="highlight ruby"><code><span class="k">module</span> <span class="nn">Components</span>
<span class="k">module</span> <span class="nn">Home</span>
<span class="k">class</span> <span class="nc">Show</span> <span class="o"><</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="c1"># param :my_param</span>
<span class="c1"># param param_with_default: "default value"</span>
<span class="c1"># param :param_with_default2, default: "default value" # alternative syntax</span>
<span class="c1"># param :param_with_type, type: Hash</span>
<span class="c1"># param :array_of_hashes, type: [Hash]</span>
<span class="c1"># collect_all_other_params_as :attributes #collects all other params into a hash</span>
<span class="c1"># The following are the most common lifecycle call backs,</span>
<span class="c1"># the following are the most common lifecycle call backs</span>
<span class="c1"># delete any that you are not using.</span>
<span class="c1"># call backs may also reference an instance method i.e. before_mount :my_method</span>
<span class="n">before_mount</span> <span class="k">do</span>
<span class="c1"># any initialization particularly of state variables goes here.</span>
<span class="c1"># this will execute on server (prerendering) and client.</span>
<span class="k">end</span>
<span class="n">after_mount</span> <span class="k">do</span>
<span class="c1"># any client only post rendering initialization goes here.</span>
<span class="c1"># i.e. start timers, HTTP requests, and low level jquery operations etc.</span>
<span class="k">end</span>
<span class="n">before_update</span> <span class="k">do</span>
<span class="c1"># called whenever a component will be re-rerendered</span>
<span class="k">end</span>
<span class="n">before_unmount</span> <span class="k">do</span>
<span class="c1"># cleanup any thing (i.e. timers) before component is destroyed</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">render</span>
<span class="n">div</span> <span class="k">do</span>
<span class="s2">"Home::Show"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Have a look at this component as the generator creates a boilerplate component and instructions for using the most common Reactrb macros. Note that Reactrb components normally inherit from class <code>React::Component::Base</code> but you are free to <code>include React::Component</code> instead if you need your component to inherit from some other class.</p>
<p>Also note how <code>params</code> are declared and how <code>before_mount</code> macros (and friends) macros are used. Finally note that every component must have one <code>render</code> method which must return just one DOM <code>element</code> which in this example case is a <code>div</code>.</p>
<p>Next let's get this simple component rendering on a page. For that we will need a rails controller and a route.</p>
<pre class="highlight plaintext"><code>rails g controller home
</code></pre>
<p>And add a route to your <code>routes.rb</code></p>
<pre class="highlight plaintext"><code>root 'home#show'
</code></pre>
<p>And a <code>show</code> method in the HomeController which will render the component using the <code>render_component</code> helper.</p>
<pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">HomeController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
<span class="k">def</span> <span class="nf">show</span>
<span class="n">render_component</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Fire up the server with <code>bundle exec rails s</code>, refresh your browser and if all has gone well, you should be rewarded with <code>Home::Show</code> in your browser.</p>
<p>If you open your JavaScript console you can also check which version of React has been loaded.</p>
<pre class="highlight plaintext"><code>React.version
</code></pre>
<p>Remember this value, as we will need to use it later.</p>
<h3 id="step-3-webpack-for-managing-front-end-assets">Step 3: Webpack for managing front-end assets</h3>
<p>There are three parts to this step: </p>
<ul>
<li>Setting up NPM (node package manager) for the project</li>
<li>Setting up Webpack</li>
<li>Updating the rails asset pipeline to use the bundles generated by Webpack</li>
</ul>
<p>This is just a matter of adding 4 boiler plate files, and updating two of your rails files.</p>
<p>First add a package.json file to your root directory (same place as your Gemfile) like this:</p>
<pre class="highlight plaintext"><code>// package.json
{
"name": "reactrb-showcase",
"version": "0.0.1",
"dependencies": {
"bootstrap": "^3.3.6",
"react": "^0.14.2",
"react-dom": "^0.14.2",
"react-bootstrap": "^0.29.5",
"webpack": "^1.13.1"
},
"devDependencies": {
}
}
</code></pre>
<p>Notice how similar this is to your Gemfile.</p>
<p>Now run <code>npm install</code> which will make sure you have all these packages.</p>
<p>So that we can run Webpack from the command line do a <code>npm install webpack -g</code></p>
<p>Now that we have Webpack, we need to add 3 boiler plate files to configure it. As you add more javascript packages you will be updating these files. Again this is similar to updating your Gemfile when you add new gems to a project.</p>
<p>Add webpack.config.js to the root of your project:</p>
<pre class="highlight javascript"><code><span class="kd">var</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">"path"</span><span class="p">);</span>
<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">context</span><span class="p">:</span> <span class="nx">__dirname</span><span class="p">,</span>
<span class="na">entry</span><span class="p">:</span> <span class="p">{</span>
<span class="na">client_only</span><span class="p">:</span> <span class="s2">"./webpack/client_only.js"</span><span class="p">,</span>
<span class="na">client_and_server</span><span class="p">:</span> <span class="s2">"./webpack/client_and_server.js"</span>
<span class="p">},</span>
<span class="na">output</span><span class="p">:</span> <span class="p">{</span>
<span class="na">path</span><span class="p">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">'app'</span><span class="p">,</span> <span class="s1">'assets'</span><span class="p">,</span> <span class="s1">'javascripts'</span><span class="p">,</span> <span class="s1">'webpack'</span><span class="p">),</span>
<span class="na">filename</span><span class="p">:</span> <span class="s2">"[name].js"</span><span class="p">,</span>
<span class="na">publicPath</span><span class="p">:</span> <span class="s2">"/webpack/"</span>
<span class="p">},</span>
<span class="na">module</span><span class="p">:</span> <span class="p">{</span>
<span class="na">loaders</span><span class="p">:</span> <span class="p">[</span>
<span class="c1">// add any loaders here</span>
<span class="p">]</span>
<span class="p">},</span>
<span class="na">resolve</span><span class="p">:</span> <span class="p">{</span>
<span class="na">root</span><span class="p">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">'..'</span><span class="p">,</span> <span class="s1">'webpack'</span><span class="p">)</span>
<span class="p">},</span>
<span class="p">};</span>
</code></pre>
<p>and create a folder called <code>webpack</code> and add the following two files:</p>
<pre class="highlight javascript"><code><span class="c1">// webpack/client_only.js</span>
<span class="c1">// any packages that depend specifically on the DOM go here</span>
<span class="c1">// for example the webpack css loader generates code that will break prerendering</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'client_only.js loaded'</span><span class="p">);</span>
</code></pre><pre class="highlight javascript"><code><span class="c1">// webpack/client_and_server.js</span>
<span class="c1">// all other packages that you can run on both server (prerendering) and client go here</span>
<span class="c1">// most well behaved packages can be required here</span>
<span class="nx">ReactDOM</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'react-dom'</span><span class="p">)</span>
<span class="nx">React</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'react'</span><span class="p">)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'client_and_server.js loaded'</span><span class="p">)</span>
</code></pre>
<p>Now run <code>webpack</code> from the command line. This will grab all necessary dependencies and package them up into the <code>client_and_server.js</code> and <code>client_only.js</code> bundles. If you look in the <code>app/assets/javascripts/webpack</code> directory you should see the two files there.</p>
<p>Finally we need to require these two bundles into our rails asset pipeline.</p>
<p>Edit <code>app/assets/javascripts/application.js</code> and add</p>
<pre class="highlight javascript"><code><span class="c1">//= require 'webpack/client_only'</span>
</code></pre>
<p>just <em>above</em> the line that reads <code>Opal.load('components');</code>. This will pull in any webpack assets that can only run on the client.</p>
<p>Then edit <code>app/views/components.rb</code> and replace the <code>require 'react'</code> line with</p>
<pre class="highlight ruby"><code><span class="nb">require</span> <span class="s1">'webpack/client_and_server.js'</span>
</code></pre>
<p>In otherwords instead of pulling in react from the react-rails gem, we are going to pull in react <em>and</em> any other javascript packages we want from our webpack bundle.</p>
<p>Reactrb can automatically access our components loaded by Webpack, but we have to opt in to this behavior. Edit <code>app/views/components.rb</code> and add</p>
<pre class="highlight ruby"><code><span class="nb">require</span> <span class="s1">'reactrb/auto-import'</span>
</code></pre>
<p>immediately after <code>require 'reactrb'</code> (which is right near the top of the file.) Auto-import will now search the javascript name space, and import into ruby any components that are referenced by your Reactrb components.</p>
<p>Now run <code>bundle exec rails s</code> and refresh the browser. Look at the console and you should see something like this:</p>
<pre class="highlight plaintext"><code>client_and_server.js loaded
client_only.js loaded
client_and_server.js loaded
************************ React Prerendering Context Initialized Show ***********************
************************ React Browser Context Initialized ****************************
Reactive record prerendered data being loaded: [Object]
</code></pre>
<p>Congratulations you are setup and ready to begin adding javascript packages to your application.</p>
<h2 id="working-with-native-react-components">Working with native React components</h2>
<p>It is time to reap some of the rewards from all the hard work above. We have everything setup so we can easily add front end components and work with them in Reactrb. Lets jump in and add a native React component that plays a video.</p>
<p><a href="https://github.com/CookPete/rplayr">We are going to use Pete Cook's React rplayr</a></p>
<p>First let's install the component via NPM:</p>
<pre class="highlight plaintext"><code>npm install react-player --save
</code></pre>
<p>Next we need to <code>require</code> it in <code>webpack/client_and_server.js</code></p>
<pre class="highlight javascript"><code><span class="nx">ReactPlayer</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'react-player'</span><span class="p">)</span>
</code></pre>
<p>Next run webpack so it can be bundled</p>
<pre class="highlight plaintext"><code>webpack
</code></pre>
<p>And then finally let's add it to our Show component:</p>
<pre class="highlight ruby"><code><span class="k">def</span> <span class="nf">render</span>
<span class="n">div</span> <span class="k">do</span>
<span class="no">ReactPlayer</span><span class="p">(</span><span class="ss">url: </span><span class="s1">'https://www.youtube.com/embed/FzCsDVfPQqk'</span><span class="p">,</span>
<span class="ss">playing: </span><span class="kp">true</span>
<span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Refresh your browser and you should have a video. How simple was that!</p>
<h2 id="working-with-react-bootstrap">Working with React Bootstrap</h2>
<p><a href="https://react-bootstrap.github.io/">We will be using React Bootstrap which is a native React library</a></p>
<p>The main purpose for React Bootstrap is that it abstracts away verbose HTML & CSS code into React components which makes it a lot cleaner for React JSX developers. One of the very lovely things about Reactrb is that we already work in beautiful Ruby. To emphasise this point, consider the following:</p>
<p>Sample 1 - In HTML (without React Bootstrap):</p>
<pre class="highlight plaintext"><code><button id="something-btn" type="button" class="btn btn-success btn-sm">
Something
</button>
$('#something-btn').click(someCallback);
</code></pre>
<p>Sample 2 - In JSX (with React Bootstrap components):</p>
<pre class="highlight plaintext"><code><Button bsStyle="success" bsSize="small" onClick={someCallback}>
Something
</Button>
</code></pre>
<p>Sample 3 - In Reactrb (without React Bootstrap):</p>
<pre class="highlight plaintext"><code>button.btn_success.btn_sm {'Something'}.on(:click) do
someMethod
end
</code></pre>
<p>Sample 4 - In Reactrb (with React Bootstrap):</p>
<pre class="highlight plaintext"><code>Bs.Button(bsStyle: :success, bsSize: :small) {'Something'}.on(:click) do
someMethod
end
</code></pre>
<p>As you can see, sample 3 & 4 are not that different and as a Reactrb developer, I actually prefer sample 3. If I were a JavaScript or JSX developer I would completely understand the advantage of abstracting Bootstrap CSS into React Components so I don't have to work directly with CSS and JavaScript but this is not the case with Reactrb as CSS classes are added to HTML elements with simple dot notation:</p>
<pre class="highlight plaintext"><code>span.pull_right {}
</code></pre>
<p>compiles to (note the conversion from _ to -)</p>
<pre class="highlight plaintext"><code><span class='pull-right'></span>
</code></pre>
<p>So I hear you ask: why if I prefer the non-React Bootstrap syntax why am worrying about React Bootstrap? For one very simple reason: components like Navbar and Modal that requires <code>bootstrap.js</code> will not work with React on it's own so without the React Bootstrap project you would need to implement all that functionality yourself. The React Bootstrap project has re-implemented all this functionality as React components.</p>
<p>Lets implement a Navbar in this project using React Bootstrap in Reactrb. First, we need to install Bootstrap and React Bootstrap:</p>
<pre class="highlight plaintext"><code>npm install bootstrap react-bootstrap --save
</code></pre>
<p>Note: The <code>--save</code> option will update the package.json file.</p>
<p>And then we need to <code>require</code> it in <code>webpack/client_and_server.js</code> by adding this line:</p>
<pre class="highlight javascript"><code><span class="nx">ReactBootstrap</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'react-bootstrap'</span><span class="p">)</span>
</code></pre>
<p>Run the <code>webpack</code> command again, and restart your rails server.</p>
<p>If you refresh your browser now and open the JavaScript console we will be able to interact with React Bootstrap by typing:</p>
<p>In the JavaScript console type: <code>ReactBootstrap</code></p>
<p>and you will see the ReactBootstrap object with all its components like Accordion, Alert, Badge, Breadcrumb, etc. This is great news, React Bootstrap is installed and ready to use. Accessing the JavaScript object in this way is a really great way to see what you have to work with. Sometimes the documentation of a component is not as accurate as actually seeing what you have in the component itself.</p>
<p>To make sure everything is working lets add a <em>Button</em> to our our Show component like this:</p>
<pre class="highlight ruby"><code><span class="k">module</span> <span class="nn">Components</span>
<span class="k">module</span> <span class="nn">Home</span>
<span class="k">class</span> <span class="nc">Show</span> <span class="o"><</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="k">def</span> <span class="nf">render</span>
<span class="no">ReactBootstrap</span><span class="o">::</span><span class="no">Button</span><span class="p">(</span><span class="ss">bsStyle: </span><span class="s1">'success'</span><span class="p">,</span> <span class="ss">bsSize: </span><span class="s2">"small"</span><span class="p">)</span> <span class="k">do</span>
<span class="s1">'Success'</span>
<span class="k">end</span><span class="p">.</span><span class="nf">on</span><span class="p">(</span><span class="ss">:click</span><span class="p">)</span> <span class="k">do</span>
<span class="n">alert</span><span class="p">(</span><span class="s1">'you clicked me!'</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Notice that we reference <code>ReactBoostrap</code> in ruby using the same identifer that was in the require statement in our <code>client_and_server.js</code> webpack bundle. The first time Reactrb hits the <code>ReactBootstrap</code> constant it will not be defined. This triggers a search of the javascript name space for something that looks either like a component or library of components. It then defines the appropriate module or component class wrapper in ruby.</p>
<p>Visit your page and if all is well you will see a clickable button. However it will not have any styles. This is because ReactBootstrap does not automatically depend on any particular style sheet, so we will have to supply one. An easy way to do this is to just copy the css file from the bootstrap repo, and stuff it our rails assets directory, however with a little upfront work we can setup webpack to do it all for us.</p>
<p>First lets add four webpack <em>loaders</em> using npm:</p>
<pre class="highlight plaintext"><code>npm install css-loader file-loader style-loader url-loader --save-dev
</code></pre>
<p>Notice we use <code>--save-dev</code> instead of just <code>--save</code> as these packages are only used in the development process.</p>
<p>Now edit your <code>webpack.config.js</code> file, and update the loaders section so it looks like this:</p>
<pre class="highlight javascript"><code><span class="kd">var</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">"path"</span><span class="p">);</span>
<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
<span class="p">...</span>
<span class="na">module</span><span class="p">:</span> <span class="p">{</span>
<span class="na">loaders</span><span class="p">:</span> <span class="p">[</span>
<span class="p">{</span> <span class="na">test</span><span class="p">:</span> <span class="sr">/</span><span class="se">\.</span><span class="sr">css$/</span><span class="p">,</span>
<span class="na">loader</span><span class="p">:</span> <span class="s2">"style-loader!css-loader"</span>
<span class="p">},</span>
<span class="p">{</span> <span class="na">test</span><span class="p">:</span> <span class="sr">/</span><span class="se">\.(</span><span class="sr">woff|woff2</span><span class="se">)(\?</span><span class="sr">v=</span><span class="se">\d</span><span class="sr">+</span><span class="se">\.\d</span><span class="sr">+</span><span class="se">\.\d</span><span class="sr">+</span><span class="se">)?</span><span class="sr">$/</span><span class="p">,</span>
<span class="na">loader</span><span class="p">:</span> <span class="s1">'url?limit=10000&mimetype=application/font-woff'</span>
<span class="p">},</span>
<span class="p">{</span> <span class="na">test</span><span class="p">:</span> <span class="sr">/</span><span class="se">\.</span><span class="sr">ttf</span><span class="se">(\?</span><span class="sr">v=</span><span class="se">\d</span><span class="sr">+</span><span class="se">\.\d</span><span class="sr">+</span><span class="se">\.\d</span><span class="sr">+</span><span class="se">)?</span><span class="sr">$/</span><span class="p">,</span>
<span class="na">loader</span><span class="p">:</span> <span class="s1">'url?limit=10000&mimetype=application/octet-stream'</span>
<span class="p">},</span>
<span class="p">{</span> <span class="na">test</span><span class="p">:</span> <span class="sr">/</span><span class="se">\.</span><span class="sr">eot</span><span class="se">(\?</span><span class="sr">v=</span><span class="se">\d</span><span class="sr">+</span><span class="se">\.\d</span><span class="sr">+</span><span class="se">\.\d</span><span class="sr">+</span><span class="se">)?</span><span class="sr">$/</span><span class="p">,</span>
<span class="na">loader</span><span class="p">:</span> <span class="s1">'file'</span>
<span class="p">},</span>
<span class="p">{</span> <span class="na">test</span><span class="p">:</span> <span class="sr">/</span><span class="se">\.</span><span class="sr">svg</span><span class="se">(\?</span><span class="sr">v=</span><span class="se">\d</span><span class="sr">+</span><span class="se">\.\d</span><span class="sr">+</span><span class="se">\.\d</span><span class="sr">+</span><span class="se">)?</span><span class="sr">$/</span><span class="p">,</span>
<span class="na">loader</span><span class="p">:</span> <span class="s1">'url?limit=10000&mimetype=image/svg+xml'</span>
<span class="p">}</span>
<span class="p">]</span>
<span class="p">},</span>
<span class="p">...</span>
<span class="p">};</span>
</code></pre>
<p>We have set webpack up so that when a css file is required it uses the style loader to process the file. Because the bootstrap css file will require font face files, we also have 4 font loaders. All this will package up everything when we require any css file.</p>
<p>Now we are ready to require CSS files, and have webpack build a complete bundle including the css and any fonts referenced.</p>
<p>To bundle in the bootstrap css file add this line to <code>webpack/client_only.js</code></p>
<pre class="highlight javascript"><code><span class="nx">require</span><span class="p">(</span><span class="s1">'bootstrap/dist/css/bootstrap.css'</span><span class="p">);</span>
</code></pre>
<p>And install the bootstrap package</p>
<pre class="highlight plaintext"><code>npm install bootstrap --save
</code></pre>
<p>Now run <code>webpack</code> to update our bundles, and restart your server. Now our button is properly styled you should be rewarded with a nice Bootstrap styled green Success Button.</p>
<p>Now that everything is loaded, lets update our component to use a few more of the Bootstrap components. Update your Show component so that it looks like this:</p>
<pre class="highlight ruby"><code><span class="k">module</span> <span class="nn">Components</span>
<span class="k">module</span> <span class="nn">Home</span>
<span class="k">class</span> <span class="nc">Show</span> <span class="o"><</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="k">def</span> <span class="nf">say_hello</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
<span class="n">alert</span> <span class="s2">"Hello from number </span><span class="si">#{</span><span class="n">i</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">render</span>
<span class="n">div</span> <span class="k">do</span>
<span class="no">ReactBootstrap</span><span class="o">::</span><span class="no">Navbar</span><span class="p">(</span><span class="ss">bsStyle: :inverse</span><span class="p">)</span> <span class="k">do</span>
<span class="no">ReactBootstrap</span><span class="o">::</span><span class="no">Nav</span><span class="p">()</span> <span class="k">do</span>
<span class="no">ReactBootstrap</span><span class="o">::</span><span class="no">NavbarBrand</span><span class="p">()</span> <span class="k">do</span>
<span class="n">a</span><span class="p">(</span><span class="ss">href: </span><span class="s1">'#'</span><span class="p">)</span> <span class="p">{</span> <span class="s1">'Reactrb Showcase'</span> <span class="p">}</span>
<span class="k">end</span>
<span class="no">ReactBootstrap</span><span class="o">::</span><span class="no">NavDropdown</span><span class="p">(</span>
<span class="ss">eventKey: </span><span class="mi">1</span><span class="p">,</span>
<span class="ss">title: </span><span class="s1">'Things'</span><span class="p">,</span>
<span class="ss">id: :drop_down</span>
<span class="p">)</span> <span class="k">do</span>
<span class="p">(</span><span class="mi">1</span><span class="p">.</span><span class="nf">.</span><span class="mi">5</span><span class="p">).</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">n</span><span class="o">|</span>
<span class="no">ReactBootstrap</span><span class="o">::</span><span class="no">MenuItem</span><span class="p">(</span><span class="ss">href: </span><span class="s1">'#'</span><span class="p">,</span>
<span class="ss">key: </span><span class="n">n</span><span class="p">,</span>
<span class="ss">eventKey: </span><span class="s2">"1.</span><span class="si">#{</span><span class="n">n</span><span class="si">}</span><span class="s2">"</span>
<span class="p">)</span> <span class="k">do</span>
<span class="s2">"Number </span><span class="si">#{</span><span class="n">n</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span><span class="p">.</span><span class="nf">on</span><span class="p">(</span><span class="ss">:click</span><span class="p">)</span> <span class="p">{</span> <span class="n">say_hello</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">div</span><span class="p">.</span><span class="nf">container</span> <span class="k">do</span>
<span class="no">ReactPlayer</span><span class="p">(</span><span class="ss">url: </span><span class="s1">'https://www.youtube.com/embed/FzCsDVfPQqk'</span><span class="p">,</span>
<span class="ss">playing: </span><span class="kp">true</span>
<span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>A few things to notice in the code above:</p>
<p>We add React Bootstrap components simply by <code>ReactBootstrap::Name</code> where <code>Name</code> is the JavaScriot component you want to render. All the components are documented in the React Bootstrap <a href="https://react-bootstrap.github.io/components.html">documentation</a></p>
<p>See with <code>div.container</code> we are mixing in CSS style which will compile into <code><div class='container'></code></p>
<p>Also notice how I have added an <code>.on(:click)</code> event handler to the <code>MenuItem</code> component while setting <code>href: '#'</code> as this will allow us to handle the event instead of navigating to a new page.</p>
<p>So far we have a very basic application which is looking OK and showing a video. Time to do something a little more interesting. How about if we add Post and Comment functionality which will let us explore Reactive Record!</p>
<h2 id="reactrb-hot-reloader-and-opal-irb">Reactrb Hot-reloader and Opal IRB</h2>
<p>Before we go any further, let's install too fantastic tools written by Forrest Chang:</p>
<ul>
<li><a href="https://github.com/fkchang/opal-hot-reloader">Opal Hot Reloader</a></li>
<li><a href="https://github.com/fkchang/opal-console">Opal Console</a></li>
</ul>
<p>Opal Hot Loader is for pure programmer joy (not having to reload the page to compile your source) and the Opal console is incredibly useful to test how Ruby code compiles to JavaScript.</p>
<p>We are also going to add the Foreman gem to run our Rails server and the Hot Loader service for us.</p>
<p>Add the following lines to your <code>gemfile</code> and run <code>bundle</code>:</p>
<pre class="highlight ruby"><code><span class="n">gem</span> <span class="s1">'opal_hot_reloader'</span><span class="p">,</span> <span class="ss">git: </span><span class="s1">'https://github.com/fkchang/opal-hot-reloader.git'</span>
<span class="n">gem</span> <span class="s1">'foreman'</span>
</code></pre>
<p><code>bundle install</code></p>
<p>Modify your <code>components.rb</code>, adding the following lines inside the if statement so they only run on the client and not as part of the server pre-rendering process:</p>
<pre class="highlight ruby"><code><span class="nb">require</span> <span class="s1">'opal_hot_reloader'</span>
<span class="no">OpalHotReloader</span><span class="p">.</span><span class="nf">listen</span><span class="p">(</span><span class="mi">25222</span><span class="p">)</span>
</code></pre>
<p>Then modify your <code>procfile</code> so that the Hot Loader service will start whenever you start your server:</p>
<pre class="highlight plaintext"><code>rails: bundle exec rails server
hotloader: opal-hot-reloader -p 25222 -d app/views/components
</code></pre>
<p>To start both servers:</p>
<p><code>foreman start</code></p>
<p>Refresh your browser for the last time and try modifying your <code>show.rb</code> component and you should see your changes appearing magically in your browser as you save. Pure joy. </p>
<h2 id="further-reading">Further reading</h2>
<h3 id="other-reactrb-tutorials-and-examples">Other Reactrb tutorials and examples</h3>
<ul>
<li><a href="https://github.com/loicboutet/reactrb_tutorial">Getting started with Reactrb and Rails</a></li>
<li><a href="https://github.com/reactrb/reactrb.github.io/blob/master/docs/tutorial.md">ChatRB Demo App</a></li>
<li><a href="https://github.com/loicboutet/reactivetodo">Reactive Record sample ToDo app</a></li>
<li><a href="https://github.com/reactrb/reactrb.github.io/wiki/Sending-data-from-deeply-nested-components">Flux pattern in Reactrb</a></li>
<li><a href="https://github.com/fkchang/getting-started-reactrb-webpack">Getting with Reactrb, React Bootstrap and Webpack</a></li>
</ul>
<h3 id="other-reactrb-resources">Other Reactrb resources</h3>
<ul>
<li><a href="http://reactrb.org/">Reactrb website</a></li>
<li><a href="https://github.com/reactrb/reactrb">Reactrb GitHub site</a></li>
</ul>
<h3 id="reactrb-is-powered-by-react">Reactrb is powered by React</h3>
<p>Reactrb and friends are in most cases simple DSL Ruby wrappers to the underlying native JavaScript libraries and React Components. It is really important to have a solid grip on how these technologies work to complement your understanding of Reactrb. Most searches for help on Google will take you to examples written in JSX or ES6 JavaScript but you will learn over time to transalte this to Reactrb equivalents. To make headway with Reactrb you do need a solid understanding of the underlying philosophy of React and its component based architecture. The 'Thinking in React' tutorial below is an excellent place to start. (Make sure you see the Flux pattern in Reactrb above for an example of how to communicate between grandparent and child components). </p>
<ul>
<li><a href="https://facebook.github.io/react/docs/thinking-in-react.html">Thinking in React</a></li>
<li><a href="https://facebook.github.io/react/docs/getting-started.html">React</a></li>
<li><a href="https://github.com/reactjs/react-router">React Router</a></li>
<li><a href="https://react-bootstrap.github.io/">React Bootstrap</a></li>
</ul>
<h3 id="opal-under-the-covers">Opal under the covers</h3>
<p>Reactrb is a DSL wrapper of React which uses Opal to compile Ruby code to ES5 native JavaScript. If you have not used Opal before then you should at a minimum read the excellent guides as they will teach you enough to get you started with Reactrb.</p>
<ul>
<li><a href="http://opalrb.com/">Opal</a></li>
<li><a href="http://opalrb.com/docs/guides/v0.9.2/index.html">Opal Guides</a></li>
<li><a href="https://www.youtube.com/watch?v=vhIrrlcWphU">To see the full power of Opal in action watch this video</a></li>
</ul>
</div>
</div>
</div>
<div class="page-footer-upperline">
<div class="container">
</div>
</div>
<footer class="nav-footer">
<section class="sitemap">
<a href="/" class="nav-home"></a>
<div>
<h6><a href="/start/components" class="hyperloop-white">Start</a></h6>
<a href="/start/components">Components</a>
<a href="/start/stores">Stores</a>
<a href="/start/models">Models</a>
<a href="/start/operations">Operations</a>
<a href="/start/policies">Policies</a>
<a href="/start/pragmatic">Pragmatic Thinking</a>
</div>
<div>
<h6><a href="/tutorials" class="hyperloop-white">Tutorials</a></h6>
<a href="/tutorials/hyperloopcomps">Hyperloop COMPS</a>
<a href="/tutorials/hyperloopjs">Hyperloop.js</a>
<a href="/tutorials/hyperlooprails">Hyperloop and Rails</a>
<a href="/tutorials/hyperloopdeploy">Hyperloop deployment</a>
<a href="/tutorials/opal">Opal</a>
<a href="/tutorials/videos">Videos</a>
</div>
<div>
<h6><a href="/installation" class="hyperloop-white">Installation</a></h6>
<a href="/installation#opal-playground">Hyperloop.js</a>
<a href="/installation#ror">With Ruby On Rails</a>
<a href="/installation#with-sinatra">With Sinatra</a>
<a href="/installation#deployment">Deployment</a>
</div>
<div>
<h6><a href="/gems" class="hyperloop-white">Gems</a></h6>
<a href="https://github.com/ruby-hyperloop/hyperloop">hyperloop</a>
<a href="https://github.com/ruby-hyperloop/hyperloop-js">hyperloop-js</a>
<a href="https://github.com/ruby-hyperloop/hyper-react">hyper-component</a>
<a href="https://github.com/ruby-hyperloop/hyper-store">hyper-store</a>
<a href="https://github.com/ruby-hyperloop/hyper-mesh">hyper-model</a>
<a href="https://github.com/ruby-hyperloop/hyper-operation">hyper-operation</a>
<a href="https://github.com/ruby-hyperloop/reactrb-router/tree/v2-4-0">hyper-router</a>
<a href="https://github.com/ruby-hyperloop/hyper-spec">hyper-spec</a>
<a href="https://github.com/ruby-hyperloop/hyper-trace">hyper-trace</a>
</div>
<div>
<h6><a href="/tools" class="hyperloop-white">Tools</a></h6>
<a href="#testing">Testing</a>
<a href="#tools">Tools</a>
<a href="#debugging">Debugging</a>
</div>
<div>
<h6><a href="/docs/architecture" class="hyperloop-white">Docs</a></h6>
<a href="/docs/architecture">Architecture</a>
<a href="/docs/components/docs#components-dsl-overview">Components</a>
<a href="/docs/stores/docs">Stores</a>
<a href="/docs/models/docs">Models</a>
<a href="/docs/operations/docs">Operations</a>
<a href="/docs/policies/docs">Policies</a>
</div>
</section>
<section class="otherlinks">
<a href="https://github.com/ruby-hyperloop" class="hyperloop-white">Github</a>
<a href="/support" class="hyperloop-white">Help</a>
<a href="/blog" class="hyperloop-white">Blog</a>
</section>
<section class="copyright">
Copyright © 2018 Hyperloop.
</section>
</footer>
<script>
(function(Opal) {
var self = Opal.top, $scope = Opal, nil = Opal.nil, $breaker = Opal.breaker, $slice = Opal.slice;
Opal.add_stubs(['$puts']);
return self.$puts("Opal ok")
})(Opal);
</script>
<!-- <script src="http://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script> -->
<script type="text/ruby">puts "Hyperloop JS ok"</script>
</body>
</html>