forked from jonnyreeves/jonnyreeves.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
922 lines (746 loc) · 72.4 KB
/
index.html
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
<!DOCTYPE html>
<html lang="en">
<head>
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700,400italic' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="http://jonnyreeves.co.uk/theme/css/style.min.css">
<link rel="stylesheet" type="text/css" href="http://jonnyreeves.co.uk/theme/css/pygments.min.css">
<link rel="stylesheet" type="text/css" href="http://jonnyreeves.co.uk/theme/css/font-awesome.min.css">
<link href="http://jonnyreeves.co.uk/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="Jonny Reeves Atom">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="robots" content="" />
<meta name="author" content="Jonny Reeves" />
<meta name="description" content="" />
<meta property="og:site_name" content="Jonny Reeves"/>
<meta property="og:type" content="blog"/>
<meta property="og:title" content="Jonny Reeves"/>
<meta property="og:description" content=""/>
<meta property="og:locale" content="en_US"/>
<meta property="og:url" content="http://jonnyreeves.co.uk"/>
<meta property="og:image" content="/images/avatar.jpg">
<title>Jonny Reeves</title>
</head>
<body>
<aside>
<div>
<a href="http://jonnyreeves.co.uk">
<img src="/images/avatar.jpg" alt="Jonny Reeves" title="Jonny Reeves">
</a>
<h1><a href="http://jonnyreeves.co.uk">Jonny Reeves</a></h1>
<p>code && coffee || music</p>
<nav>
<ul class="list">
</ul>
</nav>
<ul class="social">
<li><a class="sc-linkedin" href="https://br.linkedin.com/in/jonnyreeves/en" target="_blank"><i class="fa fa-linkedin"></i></a></li>
<li><a class="sc-google" href="https://plus.google.com/+JohnReeves" target="_blank"><i class="fa fa-google"></i></a></li>
<li><a class="sc-github" href="https://github.com/jonnyreeves" target="_blank"><i class="fa fa-github"></i></a></li>
</ul>
</div>
</aside>
<main>
<article>
<header>
<h2><a href="http://jonnyreeves.co.uk/2015/babel-npm/#babel-npm">Babel ❤ NPM</a></h2>
<p>
Posted on Sat 24 October 2015 in <a href="http://jonnyreeves.co.uk/category/javascript.html">JavaScript</a> •
</p>
</header>
<div>
<p>Once you go ES6, you never go back ;) In order to create ES5-backwards-compatible modules, there's are a few nuances that you need to be aware of:</p>
<h3>babelify</h3>
<p>Transpiling your NPM modules to ES5 with <a href="https://babeljs.io/">babel</a> will allow them to work in a wide variety of environments. Add a <code>prepublish</code> script to your <code>package.json</code>'s scripts block to automate this when you run <code>npm publish</code>. By convention <code>/lib</code> is a good place to output your ES5 artefacts to, and it's a good idea to produce a clean build each time (with the <a href="https://www.npmjs.com/package/rimraf">rimraf module</a> providing a cross platform implementation of <code>rm -rf</code>, windows users will thank you.)</p>
<h6><code>/package.json</code></h6>
<div class="highlight"><pre>{
"name": "my_pkg",
"scripts": {
"prepublish": "rimraf lib && babel --out-dir lib src"
}
}
</pre></div>
<h3>main</h3>
<p>Traditionally your <code>index.js</code> file would reside in the root of your module (this is where NPM will look for it by default); however as we are going to transpile to ES5 you'll want to put your index file, and all other ES6 sources under <code>/src</code>.</p>
<p>Your <code>package.json</code>'s <code>main</code> block should point to the transpiled <code>index.js</code> file in the <code>/lib</code> folder. If you're feeling adventurous you incude a <a href="https://github.com/caridy/es6-module-transpiler-npm-resolver"><code>jsnext:main</code></a> block and point to to your ES6 entry point.</p>
<h6><code>/package.json</code></h6>
<div class="highlight"><pre>{
"name": "my_pkg",
"main": "lib/index.js",
"jsnext:main": "src/index.js"
}
</pre></div>
<h2>Mocha</h2>
<p>Mocha does the job well and it's easy to configure for use with babel; just give it a <code>--compilers</code> flag when you invoke it:</p>
<h6><code>/package.json</code></h6>
<div class="highlight"><pre>{
"name": "my_pkg",
"scripts": {
"test": "mocha --compilers js:babel/register --recursive"
}
}
</pre></div>
<h2>.npmignore</h2>
<p>This one tripped me up; when I published my first NPM modules which made use of babel (<a href="https://github.com/jonnyreeves/redux-action-side-effects">redux-action-side-effects</a>) my <code>/lib</code> folder wasn't included in the final archived vended by NPM. Turns out that NPM will <a href="https://docs.npmjs.com/misc/developers#keeping-files-out-of-your-package">automatically exclude everything in your <code>.gitignore</code> file</a> (and as the <code>/lib</code> folder contains build artefacts I excluded it from source control). To override this add a <code>/.npmignore</code> file:</p>
<h5><code>/.npmignore</code></h5>
<div class="highlight"><pre># If this file is not present the contents of .gitignore are used
/.gitignore
</pre></div>
<p>Happy <code>npm publish</code>ing!</p>
</div>
<hr />
</article>
<article>
<header>
<h2><a href="http://jonnyreeves.co.uk/2015/mocking-xmlhttprequests-in-jasmine/#mocking-xmlhttprequests-in-jasmine">Mocking XMLHttpRequests in Jasmine</a></h2>
<p>
Posted on Mon 19 October 2015 in <a href="http://jonnyreeves.co.uk/category/javascript.html">javascript</a> •
</p>
</header>
<div>
<p>When writing integration tests, it's often useful to fake network requests so your tests can be predictable and execute quickly. Jasmine provides <a href="https://github.com/jasmine/jasmine-ajax"><code>jasmine-ajax</code></a> which will replace the <code>XMLHttpRequest</code> constructor on the global (eg: <code>window</code>).</p>
<p>Wiring up jasmine.Ajax into your test-suites' lifecycle is pretty straight forward:</p>
<div class="highlight"><pre><span class="kr">import</span> <span class="s1">'jasmine-ajax'</span><span class="p">;</span>
<span class="nx">describe</span><span class="p">(</span><span class="s1">'myTestSuite'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">beforeEach</span><span class="p">(()</span> <span class="o">=></span> <span class="nx">jasmine</span><span class="p">.</span><span class="nx">Ajax</span><span class="p">.</span><span class="nx">install</span><span class="p">());</span>
<span class="nx">afterEach</span><span class="p">(()</span> <span class="o">=></span> <span class="nx">jasmine</span><span class="p">.</span><span class="nx">Ajax</span><span class="p">.</span><span class="nx">uninstall</span><span class="p">());</span>
<span class="p">});</span>
</pre></div>
<p>As per the <a href="http://jasmine.github.io/2.0/ajax.html">docs</a>, you can then pre-program responses before your subject under test makes them:</p>
<div class="highlight"><pre><span class="nx">jasmine</span><span class="p">.</span><span class="nx">Ajax</span><span class="p">.</span><span class="nx">stubRequest</span><span class="p">(</span><span class="s1">'http://example.org/api'</span><span class="p">)</span>
<span class="p">.</span><span class="nx">andReturn</span><span class="p">({</span> <span class="nx">responseText</span><span class="o">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span> <span class="nx">json</span><span class="o">:</span> <span class="s1">'ok!'</span> <span class="p">})</span> <span class="p">});</span>
</pre></div>
<p>jasmine-ajax executes all XHR callbacks on the same tick making them effectivly syncronous. This results in your test-code much easier to write and reason about.</p>
<h3>Pairing with <code>xhr</code></h3>
<p><code>XMLHttpRequest</code> doesn't have the nicest API to work with; fortunatley the <a href="https://github.com/Raynos/xhr"><code>xhr</code></a> module on NPM provides a nice, lightweight abstraction (it even smooths out the cracks in IE8). However, it's not all plain sailing as the xhr module caches the <code>XMLHttpRequest</code> object on the window when it is loaded (ie: <code>require()</code>'d).</p>
<div class="highlight"><pre><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span><span class="p">.</span><span class="nx">XMLHttpRequest</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">XMLHttpRequest</span><span class="p">;</span>
</pre></div>
<p>This results in requests made using the <code>xhr</code> module ignorning the fake <code>XMLHttpRequest</code> constructor that <code>jasmine-ajax</code> has placed on the window. Fortunatley there's an easy fix, just re-assign <code>xhr.XMLHttpRequest</code> after you've installed jasmine-ajax:</p>
<div class="highlight"><pre><span class="kr">import</span> <span class="s1">'jasmine-ajax'</span><span class="p">;</span>
<span class="kr">import</span> <span class="nx">xhr</span> <span class="nx">from</span> <span class="s1">'xhr'</span><span class="p">;</span>
<span class="nx">describe</span><span class="p">(</span><span class="s1">'myTestSuite'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">beforeEach</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">jasmine</span><span class="p">.</span><span class="nx">Ajax</span><span class="p">.</span><span class="nx">install</span><span class="p">();</span>
<span class="nx">xhr</span><span class="p">.</span><span class="nx">XMLHttpRequest</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">XMLHttpRequest</span><span class="p">;</span>
<span class="p">});</span>
<span class="nx">afterEach</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">jasmine</span><span class="p">.</span><span class="nx">Ajax</span><span class="p">.</span><span class="nx">uninstall</span><span class="p">();</span>
<span class="nx">xhr</span><span class="p">.</span><span class="nx">XMLHttpRequest</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">XMLHttpRequest</span><span class="p">;</span>
<span class="p">});</span>
<span class="p">});</span>
</pre></div>
<h3>Closing thoughts on Jasmine</h3>
<p>I've recently switched to Jasmine after using Mocha and SinonJS succesfully for a few years. My hope was that the Jasmine ecosystem, which has a lot of funtionality built-in, would be more productive and easier for new-comers to the project to pick up than Mocha. I've been very impressed by how easy it was to get up and running on Jasmine; and the v2.3 API is pleasant to code against (it will be instantly familiar to <a href="http://chaijs.com/guide/installation/">chai</a> users), however I do wish that Jasmine made it easier to write custom matchers as I find myself missing the verbosity of SinonJS's <code>sinon.match(val => { ... })</code>.</p>
</div>
<hr />
</article>
<article>
<header>
<h2><a href="http://jonnyreeves.co.uk/2015/single-page-app-versioning/#single-page-app-versioning">Single Page App Versioning</a></h2>
<p>
Posted on Sat 26 September 2015 in <a href="http://jonnyreeves.co.uk/category/misc.html">misc</a> •
</p>
</header>
<div>
<p>Versioning applications isn't quite as clear cut as <a href="http://semver.org/">versioning libraries</a>; individuals on teams will almost certainly fork the codebase to work on features (sometimes long-lived) but you will still want to provide an easy, and consistent way to refer to each build generated from your Continuous Integration system.</p>
<p>My preferred approach combines the human managed semantic version number from a project's <code>package.json</code> file with the short commit hash; ie: <code>0.1.6-deff00</code>; you can configure your project's build system to replace the commit hash for local (non-ci) builds. This also has the nice advantage of making each build traceable back to your project's Git repository should you forget to tag a build :)</p>
<p>The following example is for <a href="https://webpack.github.io/">webpack</a>, but could easily be modified for any build system:</p>
<div class="highlight"><pre><span class="kd">var</span> <span class="nx">webpack</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'webpack'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">gitrev</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'git-rev-sync'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">pkg</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'./package.json'</span><span class="p">)</span>
<span class="kd">function</span> <span class="nx">version</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">pkg</span><span class="p">.</span><span class="nx">version</span> <span class="o">+</span> <span class="s1">'-'</span> <span class="o">+</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">PRODUCTION</span> <span class="o">?</span> <span class="nx">gitrev</span><span class="p">.</span><span class="kr">short</span><span class="p">()</span> <span class="o">:</span> <span class="s1">'dev'</span><span class="p">);</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="cm">/* ... */</span>
<span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span>
<span class="k">new</span> <span class="nx">webpack</span><span class="p">.</span><span class="nx">DefinePlugin</span><span class="p">({</span>
<span class="nx">VERSION</span><span class="o">:</span> <span class="nx">version</span><span class="p">()</span>
<span class="p">})</span>
<span class="p">]</span>
<span class="p">}</span>
</pre></div>
</div>
<hr />
</article>
<article>
<header>
<h2><a href="http://jonnyreeves.co.uk/2015/getting-proper-stack-traces-with-mocha-and-typescript/#getting-proper-stack-traces-with-mocha-and-typescript">Getting Proper Stack Traces with Mocha and TypeScript</a></h2>
<p>
Posted on Thu 20 August 2015 in <a href="http://jonnyreeves.co.uk/category/typescript.html">TypeScript</a> •
</p>
</header>
<div>
<p>Mocha and TypeScript are awesome when everything's working, but when you have a failing test something's not quite right:</p>
<div class="highlight"><pre><span class="p">$</span><span class="nv">npm</span><span class="x"> test</span>
<span class="x"> Calculator</span>
<span class="x"> </span><span class="cp">#</span><span class="nf">add</span><span class="x"></span>
<span class="x"> 1) should work</span>
<span class="x"> 0 passing (21ms)</span>
<span class="x"> 1 failing</span>
<span class="x"> 1) Calculator </span><span class="cp">#</span><span class="nf">add</span><span class="x"> should work:</span>
<span class="x"> AssertionError: expected 6 to equal 7</span>
<span class="x"> + expected - actual</span>
<span class="x"> -6</span>
<span class="x"> +7</span>
<span class="x"> at Context.<anonymous> (test/Calculator.spec.js:12:38)</span>
</pre></div>
<p>The stacktrace at the end points us to the failing assertion; however the stacktrace is pointing <code>Calculator.spec.js</code> - not our TypeScript test-case. </p>
<p>The TypeScript compiler (<code>tsc</code>) will generate <a href="http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/">source maps</a> to enable debugging the TypeScript source rather than the transpiled ES5 javascript which gets written out by the compiler. We can enable this with the <code>--sourcemap</code> flag.</p>
<p>Problem is that V8 (the engine that powers Node) doesn't use these sourcemaps when generating stacktraces; <a href="https://github.com/evanw/node-source-map-support">source-map-support</a> to the rescue! We can enable it by passing <code>--require source-map-support/register</code> to <code>mocha</code>:</p>
<div class="highlight"><pre>{
"scripts": {
"test": "mocha --require source-map-support/register test/**/*.spec.js"
}
}
</pre></div>
<p>Now we get an accurate stacktrace on failure :)</p>
<div class="highlight"><pre> 1) Calculator #add should work:
AssertionError: expected 6 to equal 7
+ expected - actual
-6
+7
at Context.<anonymous> (test/Calculator.spec.ts:15:22)
</pre></div>
<p>Thanks to redditor <a href="https://www.reddit.com/user/pieeta">pieeta</a> who tipped me off :) I've posted the sample project up on <a href="https://github.com/jonnyreeves/mocha-typescript-stacktraces">Github</a>.</p>
</div>
<hr />
</article>
<article>
<header>
<h2><a href="http://jonnyreeves.co.uk/2015/using-chai-with-typescript-and-mocha/#using-chai-with-typescript-and-mocha">Using Chai with TypeScript and Mocha</a></h2>
<p>
Posted on Fri 14 August 2015 in <a href="http://jonnyreeves.co.uk/category/typescript.html">TypeScript</a> •
</p>
</header>
<div>
<p>Continuing my work on <a href="/2015/injecting-into-constructors-with-typescript-decorators/">Decorator-drive dependency injection in Typescript</a> I've overhauled the tests by replacing the manual assertions with <a href="http://chaijs.com/">chai</a>. Chai provides a fluent api for writing descriptive assertions - there are a couple of different flavours, personally I feel the <a href="http://chaijs.com/guide/styles/"><code>expect</code></a> style pairs well with TypeScript as I'm not a fan of extending <code>Object.prototype</code>.</p>
<p>Installation is straight forward. First install <code>chai</code> as a dev dependency:</p>
<div class="highlight"><pre>npm install chai --save-dev
</pre></div>
<p>Next grab the <code>d.ts</code> file for chai via <a href="http://definitelytyped.org/tsd/"><code>tsd</code></a> to obtain the type definitions.</p>
<div class="highlight"><pre>tsd install chai --save
</pre></div>
<p>You can now start refactoring your test cases; given the following simple mocha test-case:</p>
<div class="highlight"><pre>describe('Calculator', () => {
it('should add two numbers', () => {
const calc : Calculator = new Calculator();
// Manual assertion.
if (calc.add(5, 3) !== 8) {
throw new Error('expected 5 + 3 to equal 8');
}
});
});
</pre></div>
<p>We can leverage chai's expect method to perform the assertion:</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="p">{</span> <span class="n">expect</span> <span class="p">}</span> <span class="kn">from</span> <span class="s">'chai'</span><span class="p">;</span>
<span class="n">describe</span><span class="p">(</span><span class="s">'Calculator'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="n">it</span><span class="p">(</span><span class="s">'should add two numbers'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="n">const</span> <span class="n">calc</span> <span class="p">:</span> <span class="n">Calculator</span> <span class="o">=</span> <span class="n">new</span> <span class="n">Calculator</span><span class="p">();</span>
<span class="o">//</span> <span class="n">Chai</span> <span class="n">assertion</span><span class="o">.</span>
<span class="n">expect</span><span class="p">(</span><span class="n">calc</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">3</span><span class="p">))</span><span class="o">.</span><span class="n">to</span><span class="o">.</span><span class="n">equal</span><span class="p">(</span><span class="mi">8</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">});</span>
</pre></div>
<p>When running the tests, chai will provide a useful error message should the assertion fail:</p>
<div class="highlight"><pre>1 failing
1) Calculator should add two numbers:
AssertionError: expected 6 to equal 8
+ expected - actual
-8
+6
at Context.<anonymous> (test/Calculator.spec.js:26:45)
</pre></div>
<p>Check out <a href="https://github.com/jonnyreeves/ts-prop-injection/pull/3">this pull request</a> which highlights how I integrated chai into the ts-prop-inject project.</p>
</div>
<hr />
</article>
<article>
<header>
<h2><a href="http://jonnyreeves.co.uk/2015/injecting-into-constructors-with-typescript-decorators/#injecting-into-constructors-with-typescript-decorators">Injecting into Constructors with TypeScript Decorators</a></h2>
<p>
Posted on Thu 06 August 2015 in <a href="http://jonnyreeves.co.uk/category/typescript.html">TypeScript</a> •
</p>
</header>
<div>
<p>In the <a href="/2015/injecting-into-methods-with-typescript-decorators/">previous post</a> I added support for injecting into methods; this post provides the last missing InjectionPoint, constructor injection. As with <a href="http://pierrechamberlain.ca/blog/2011/04/custom_metadata/">AS3 Metadata</a>, you can't annotate the <code>#constructor()</code> method directly:</p>
<div class="highlight"><pre><span class="k">class</span> <span class="n">MyClass</span> {
// <span class="n">Although</span> <span class="n">this</span> <span class="n">would</span> <span class="n">be</span> <span class="n">the</span> <span class="n">preferred</span> <span class="n">syntax</span> <span class="n">it</span> <span class="k">will</span> <span class="n">throw</span> <span class="n">a</span> <span class="n">compiler</span> <span class="n">error</span>,<span class="sr"></span>
<span class="sr"> //</span> <span class="n">TS1206:</span> <span class="n">Decorators</span> <span class="n">are</span> <span class="nb">not</span> <span class="n">valid</span> <span class="n">here</span>.
<span class="nv">@inject</span>(<span class="s">'firstName'</span>)
<span class="n">constructor</span>(<span class="nb">name</span> : <span class="n">string</span>) { ... }
}
</pre></div>
<p>Instead, we need to add the decorator to the Class, ie:</p>
<div class="highlight"><pre>@inject('firstName')
class MyClass {
constructor(name : string) { ... }
}
</pre></div>
<p>This is slightly awkward as it moves our decorator away from the constructor declaration which contains our injection arguments - that's somewhat compounded by the fact constructor injection is the most desirable form of the three (constructor, method and property) as it promotes immutability by ensuring an object has all of its dependencies when constructed - still, at least it works :)</p>
<p>The first change is to the <code>@inject</code> decorator, previously we expected the first argument to the decoratorFactory (<code>target</code>) to be the instance of the Class being decorated; however because we are decorating the class itself, the decoratorFactory can't be invoked with the instance, instead it receives a reference to the Class' constructor function, likewise it does not receive a <code>decoratedPropertyName</code> argument.:</p>
<div class="highlight"><pre><span class="kd">function</span> <span class="nx">inject</span><span class="p">(...</span><span class="nx">injectionKeys</span> <span class="o">:</span> <span class="nb">Array</span><span class="o"><</span><span class="nx">string</span><span class="o">></span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kd">function</span> <span class="nx">decoratorFactory</span><span class="p">(</span><span class="nx">target</span> <span class="o">:</span> <span class="nb">Object</span><span class="o">|</span><span class="nb">Function</span><span class="p">,</span> <span class="nx">decoratedPropertyName</span><span class="o">?</span> <span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="o">:</span> <span class="k">void</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">targetType</span> <span class="o">:</span> <span class="nb">Function</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">target</span> <span class="o">===</span> <span class="s1">'function'</span> <span class="o">&&</span> <span class="nx">decoratedPropertyName</span> <span class="o">===</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">targetType</span> <span class="o">=</span> <span class="nx">target</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="nx">targetType</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">constructor</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* ... */</span>
<span class="p">}</span>
</pre></div>
<p>Next we need to record the <code>injectionKeys</code> that the user wants to be used to fulfill the constructor's dependencies - our <code>InjectionPoint</code> object is currently used to record dependencies, however it doesn't really fit for recording a constructor's dependencies:</p>
<ul>
<li><code>InjectionPoint#constructor()</code> takes three arguments, the target instance, decorated property name and a list of injectionKeys, but there is no target instance for a decorated constructor (we just get a reference to the Constructor function), and the decoratorFactory also does not receive a decoratedPropertyName argument.</li>
<li><code>InjectionPoint#inject()</code> is expected to perform the injection against the target instance, but we need to Construct a new instance (and return it)</li>
</ul>
<p>To deal with these special cases I ended up creating the <code>ConstructorInjectionPoint</code> class which specialises <code>InjectionPoint</code> to deal with the issues outlined above:</p>
<div class="highlight"><pre>export class ConstructorInjectionPoint extends InjectionPoint{
constructor(injectionKeys : Array<string>) {
super(null, 'constructor', injectionKeys);
}
inject(values : Array<any>) : void {
throw new Error('Unsupported operation #inject()');
}
}
</pre></div>
<p>This style of specialisation is not ideal; the worst offender is that <code>ConstructorInjectionPoint#inject()</code> throws an unsupported operation error - this design leads to a violation of the Liskov Substitution Principle (all sub-types should be interchangeable with their parent type) so it's something I plan to come back and revisit at a later date. </p>
<p>The decorator can now create the appropriate <code>InjectionPoint</code> type based on the arguments it receives</p>
<div class="highlight"><pre><span class="kd">function</span> <span class="nx">inject</span><span class="p">(...</span><span class="nx">injectionKeys</span> <span class="o">:</span> <span class="nb">Array</span><span class="o"><</span><span class="nx">string</span><span class="o">></span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kd">function</span> <span class="nx">decoratorFactory</span><span class="p">(</span><span class="nx">target</span> <span class="o">:</span> <span class="nb">Object</span><span class="o">|</span><span class="nb">Function</span><span class="p">,</span> <span class="nx">decoratedPropertyName</span><span class="o">?</span> <span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="o">:</span> <span class="k">void</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">targetType</span> <span class="o">:</span> <span class="nb">Function</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">injectionPoint</span> <span class="o">:</span> <span class="nx">InjectionPoint</span><span class="p">;</span>
<span class="c1">// Decorator applied to Class (for Constructor injection).</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">target</span> <span class="o">===</span> <span class="s1">'function'</span> <span class="o">&&</span> <span class="nx">decoratedPropertyName</span> <span class="o">===</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">targetType</span> <span class="o">=</span> <span class="nx">target</span><span class="p">;</span>
<span class="nx">injectionPoint</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ConstructorInjectionPoint</span><span class="p">(</span><span class="nx">injectionKeys</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Decorator applied to member (method or property).</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">target</span> <span class="o">===</span> <span class="s1">'object'</span> <span class="o">&&</span> <span class="k">typeof</span> <span class="nx">decoratedPropertyName</span> <span class="o">===</span> <span class="s1">'string'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">targetType</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">constructor</span><span class="p">;</span>
<span class="nx">injectionPoint</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">InjectionPoint</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">decoratedPropertyName</span><span class="p">,</span> <span class="nx">injectionKeys</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">targetType</span><span class="p">.</span><span class="nx">__inject__</span><span class="cp">[</span><span class="nx">injectionPoint.propertyName</span><span class="cp">]</span> <span class="o">=</span> <span class="nx">injectionPoint</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>Now that the <code>ConstructorInjectionPoint</code> is being recorded, we need to modify <code>Injector#instantiate()</code> to make use of it when creating the resulting instance:</p>
<div class="highlight"><pre><span class="k">class</span> <span class="n">Injector</span> {
<span class="n">instantiate</span><span class="s"><T>(Class : Constructable<T></span>) : <span class="n">T</span> {
// <span class="n">Create</span> <span class="n">an</span> <span class="n">instance</span> <span class="k">of</span> <span class="n">the</span> <span class="n">target</span> <span class="nb">Class</span> <span class="n">applying</span> <span class="n">the</span> <span class="n">Constructor</span> <span class="n">InjectionPoint</span> <span class="k">if</span> <span class="n">it</span> <span class="k">has</span> <span class="nb">one</span>.
<span class="n">const</span> <span class="n">instance</span> : <span class="n">T</span> = <span class="n">this</span>.<span class="n">createInjecteeInstance</span>(<span class="nb">Class</span>);
/* ... <span class="n">apply</span> <span class="n">property</span> <span class="o">and</span> <span class="k">method</span> <span class="n">injection</span> <span class="n">points</span> ... */
<span class="k">return</span> <span class="n">instance</span>;
}
<span class="n">private</span> <span class="n">createInjecteeInstance</span><span class="s"><T>(Class : { new(...args : Array<any></span>) : <span class="n">T</span> }) : <span class="n">T</span> {
<span class="k">let</span> <span class="n">result</span> : <span class="n">T</span>;
<span class="k">if</span> (<span class="nb">Class</span>.<span class="n">hasOwnProperty</span>(<span class="s">'__inject__'</span>)) {
<span class="n">const</span> <span class="n">injectionPoint</span> : <span class="n">InjectionPoint</span> = (<span class="s"><InjectionTarget></span> <span class="nb">Class</span>).<span class="n">__inject__</span>.<span class="n">constructor</span>];
<span class="k">if</span> (<span class="n">injectionPoint</span>) {
<span class="n">result</span> = <span class="n">invokeConstructor</span>(<span class="nb">Class</span>, <span class="n">this</span>.<span class="n">getInjectionValues</span>(<span class="n">injectionPoint</span>));
}
}
// <span class="n">If</span> <span class="n">no</span> <span class="n">Constructor</span> <span class="n">InjectionPoint</span> <span class="k">is</span> <span class="n">found</span> <span class="k">return</span> <span class="n">a</span> <span class="nb">new</span> <span class="n">instance</span> <span class="n">with</span> <span class="n">no</span> <span class="n">arguments</span>.
<span class="k">return</span> <span class="n">result</span> || <span class="nb">new</span> <span class="nb">Class</span>();
}
}
</pre></div>
<p>The <code>invokeConstructor()</code> is a necessary evil that crops up in a lot of languages where you wish to call a constructor function with the <code>new</code> keyword. Note that you can't make use of <code>Function.apply</code> here as there is no valid scope to pass (the scope is the new instance!), as a result we end up with this familiar pattern, the pyramid of doom:</p>
<div class="highlight"><pre><span class="kd">function</span> <span class="nx">invokeConstructor</span><span class="o"><</span><span class="nx">T</span><span class="o">></span><span class="p">(</span><span class="nx">Class</span> <span class="o">:</span> <span class="p">{</span> <span class="k">new</span><span class="p">(...</span><span class="nx">args</span> <span class="o">:</span> <span class="nb">Array</span><span class="o"><</span><span class="nx">any</span><span class="o">></span><span class="p">)</span> <span class="o">:</span> <span class="nx">T</span> <span class="p">},</span> <span class="nx">args</span> <span class="o">:</span> <span class="nb">Array</span><span class="o"><</span><span class="nx">any</span><span class="o">></span><span class="p">)</span> <span class="o">:</span> <span class="nx">T</span> <span class="p">{</span>
<span class="k">switch</span> <span class="p">(</span><span class="nx">args</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="mi">0</span><span class="o">:</span> <span class="k">return</span> <span class="k">new</span> <span class="nx">Class</span><span class="p">();</span>
<span class="k">case</span> <span class="mi">1</span><span class="o">:</span> <span class="k">return</span> <span class="k">new</span> <span class="nx">Class</span><span class="p">(</span><span class="nx">args</span><span class="cp">[</span><span class="mi">0</span><span class="cp">]</span><span class="p">);</span>
<span class="k">case</span> <span class="mi">2</span><span class="o">:</span> <span class="k">return</span> <span class="k">new</span> <span class="nx">Class</span><span class="p">(</span><span class="nx">args</span><span class="cp">[</span><span class="mi">0</span><span class="cp">]</span><span class="p">,</span> <span class="nx">args</span><span class="cp">[</span><span class="mi">1</span><span class="cp">]</span><span class="p">);</span>
<span class="k">case</span> <span class="mi">3</span><span class="o">:</span> <span class="k">return</span> <span class="k">new</span> <span class="nx">Class</span><span class="p">(</span><span class="nx">args</span><span class="cp">[</span><span class="mi">0</span><span class="cp">]</span><span class="p">,</span> <span class="nx">args</span><span class="cp">[</span><span class="mi">1</span><span class="cp">]</span><span class="p">,</span> <span class="nx">args</span><span class="cp">[</span><span class="mi">2</span><span class="cp">]</span><span class="p">);</span>
<span class="k">default</span><span class="o">:</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">"I got bored..."</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<p>As before I've pushed the code to <a href="https://github.com/jonnyreeves/ts-prop-injection/tree/03-ctor-injection">Github</a> and create a <a href="https://github.com/jonnyreeves/ts-prop-injection/pull/2">Pull Request</a> to highlight changes from the previous post.</p>
</div>
<hr />
</article>
<article>
<header>
<h2><a href="http://jonnyreeves.co.uk/2015/injecting-into-methods-with-typescript-decorators/#injecting-into-methods-with-typescript-decorators">Injecting into Methods with TypeScript Decorators</a></h2>
<p>
Posted on Mon 03 August 2015 in <a href="http://jonnyreeves.co.uk/category/typescript.html">TypeScript</a> •
</p>
</header>
<div>
<p>Expanding upon my <a href="http://localhost:8000/2015/basic-typescript-dependency-injection-with-decorators/">last post on Property based Dependency Injection</a>, let's have a look at what we need to do in order to inject into methods - here's what we want to achieve:</p>
<div class="highlight"><pre>class Person {
private _firstName : string;
private _lastName : string;
@inject('firstName', 'lastName')
setName(first : string, last : string) {
this._firstName = first;
this._last = last;
}
getFullName() : string {
return `<span class="cp">${</span><span class="n">firstName</span><span class="cp">}</span> <span class="cp">${</span><span class="n">lastName</span><span class="cp">}</span>`
}
}
</pre></div>
<p>Here we are decorating the <code>Person#setName()</code> method with the <code>@inject</code> decorator and requesting the values mapped against the 'firstName' and 'lastName' injection keys from the Dependency Container - our test case would look something like this:</p>
<div class="highlight"><pre>// Setup the container
injector.map('firstName', 'Jonny');
injector.map('lastName', 'Reeves');
// Instantiate a Person, the container will apply the injections.
const jonny = injector.instantiate(Person);
// Test it worked.
if (jonny.getFullName() !== 'Jonny Reeves') {
throw new Error('expected jonny.getFullName() to be Jonny Reeves but was ' + jonny.getFullName());
}
</pre></div>
<p>First of all we need to modify our <code>@inject</code> decorator, originally it only expected a single argument (the <code>injectionKey</code>), as methods can take more than one argument we need to make use of <a href="">rest parameters</a> to accept any number of strings:</p>
<div class="highlight"><pre><span class="kd">function</span> <span class="nx">inject</span><span class="p">(...</span><span class="nx">injectionKeys</span> <span class="o">:</span> <span class="nb">Array</span><span class="o"><</span><span class="nx">string</span><span class="o">></span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
</pre></div>
<p>We face a slightly tricker challenge in the fact that injecting values into methods requires us to inoke a method rather than just assinging a property on the target object, in the previous implementation the <code>__inject__</code> Object was a simple hash of property name to injectionKey, but in order to inovoke the target method with the injection values in the correct scope we will need access to the target object as well. To group this data together I've created an <code>InjectionPoint</code> object and assign this as the new value in the <code>__inject__</code> map:</p>
<div class="highlight"><pre><span class="kd">function</span> <span class="nx">inject</span><span class="p">(...</span><span class="nx">injectionKeys</span> <span class="o">:</span> <span class="nb">Array</span><span class="o"><</span><span class="nx">string</span><span class="o">></span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kd">function</span> <span class="nx">recordInjection</span><span class="p">(</span><span class="nx">target</span> <span class="o">:</span> <span class="nb">Object</span><span class="p">,</span> <span class="nx">decoratedPropertyName</span> <span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="o">:</span> <span class="k">void</span> <span class="p">{</span>
<span class="cm">/* ... */</span>
<span class="nx">targetType</span><span class="p">.</span><span class="nx">__inject__</span><span class="cp">[</span><span class="nx">decoratedPropertyName</span><span class="cp">]</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">InjectionPoint</span><span class="p">(</span>
<span class="nx">target</span><span class="p">,</span> <span class="nx">decoratedPropertyName</span><span class="p">,</span> <span class="nx">injectionKeys</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>Now we're storing the <code>InjectionPoint</code> data we need to modify <code>Injector#instantiate()</code> to make use of it when invoked. The first change is driven by the fact the target Class' <code>__inject__</code> property is a hash of propertyNames to <code>InjectionPoint</code>'s and that an <code>InjectionPoint</code> provides one or more injectionKeys (before it was a hash of propertyNames to a single injectionKey), <code>Injector#getInjectionValues()</code> makes light work of this returning a collection of values to be injected:</p>
<div class="highlight"><pre><span class="k">class</span> <span class="n">Injector</span> {
/* ... */
<span class="n">private</span> <span class="n">getInjectionValues</span>(<span class="n">injectionPoint</span> : <span class="n">InjectionPoint</span>) : <span class="nb">Array</span><span class="s"><any></span> {
<span class="k">return</span> <span class="n">injectionPoint</span>.<span class="n">injectionKeys</span>
.<span class="nb">map</span>(<span class="nb">key</span> => <span class="n">this</span>.<span class="n">valuesByInjectionKey</span>[<span class="nb">key</span>]);
}
}
</pre></div>
<p>Now we need to determine how these values should be injected (either via a property of the target object, or by invoking a method of the target object). Seeing as the <code>InjectionPoint</code> has all the knowledge it needs to perform the correct injection I employed the <a href="http://c2.com/cgi/wiki?HollywoodPrinciple">Hollywood Principle</a> and added an <code>InjectionPoint#inject()</code> method which the <code>Injector</code> invokes:</p>
<div class="highlight"><pre><span class="kr">class</span> <span class="nx">InjectionPoint</span> <span class="p">{</span>
<span class="cm">/* ... */</span>
<span class="nx">inject</span><span class="p">(</span><span class="nx">values</span> <span class="o">:</span> <span class="nb">Array</span><span class="o"><</span><span class="nx">any</span><span class="o">></span><span class="p">)</span> <span class="o">:</span> <span class="k">void</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="k">this</span><span class="p">.</span><span class="nx">_target</span><span class="cp">[</span><span class="nx">this._decoratedPropertyName</span><span class="cp">]</span> <span class="o">===</span> <span class="s1">'function'</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_target</span><span class="cp">[</span><span class="nx">this._decoratedPropertyName</span><span class="cp">]</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_target</span><span class="p">,</span> <span class="nx">values</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="c1">// Property injection can only use the first value.</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_target</span><span class="cp">[</span><span class="nx">this._decoratedPropertyName</span><span class="cp">]</span> <span class="o">=</span> <span class="nx">values</span><span class="cp">[</span><span class="mi">0</span><span class="cp">]</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>As before, the code and unit tests can be found over at <a href="https://github.com/jonnyreeves/ts-prop-injection/tree/02-method-injection">github.com/jonnyreeves/ts-prop-injection</a> - I've also raised a pull request on the repo to highlight the changes: <a href="https://github.com/jonnyreeves/ts-prop-injection/pull/1">github.com/jonnyreeves/ts-prop-injection/pull/1</a>.</p>
</div>
<hr />
</article>
<article>
<header>
<h2><a href="http://jonnyreeves.co.uk/2015/basic-typescript-dependency-injection-with-decorators/#basic-typescript-dependency-injection-with-decorators">Basic Typescript Dependency Injection with Decorators</a></h2>
<p>
Posted on Sat 01 August 2015 in <a href="http://jonnyreeves.co.uk/category/typescript.html">TypeScript</a> •
</p>
</header>
<div>
<p>Typescript 1.5 <a href="https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#decorators">introduced decorators</a> to the language which lets us experiment with <a href="https://en.wikipedia.org/wiki/Metaprogramming">meta-programming</a>. Metadata driven Dependency Injection frameworks allow you to write highly decoupled units which are easy to test and switch out between projects / frameworks. Let's see how we can use decorators to get some simple property injection working with TypeScript.</p>
<p>Let's start with an example of what we want to achieve:</p>
<div class="highlight"><pre><span class="k">class</span> <span class="n">LoginService</span> {
<span class="n">userModel</span> : <span class="n">UserModel</span>;
<span class="n">performLogin</span>() : <span class="nb">void</span> {
<span class="k">if</span> (!<span class="n">this</span>.<span class="n">userModel</span>.<span class="n">isLoggedIn</span>()) {
// ... <span class="n">implementation</span> <span class="n">omitted</span>...
}
}
</pre></div>
<p>Here our <code>LoginService</code> wants to check if our user is logged in before doing anything; as a result we need to provide a reference to the <code>UserModel</code> before invoking <code>LoginService#performLogin()</code>, we can do this manually:</p>
<div class="highlight"><pre>const myUserModel = new UserModel();
const myLoginService = new LoginService();
// Manually inject the dependency before using the Service.
myLoginService.userModel = myUserModel;
myLoginService.performLogin();
</pre></div>
<p>We can use a decorator to record the fact that <code>LoginService</code> has a dependency on <code>UserModel</code>:</p>
<div class="highlight"><pre><span class="k">class</span> <span class="n">LoginService</span> {
<span class="nv">@inject</span>(<span class="s">'UserModel'</span>)
<span class="n">userModel</span> : <span class="n">UserModel</span>
</pre></div>
<p>Now we need to implement our <code>@inject</code> decorator, the contract requires us to export a factory function (ie: a function which returns a function) - this factory function will be invoked each time a new instance of the supplied Class is constructed giving us a chance to modify the behavior of the program at run-time.</p>
<div class="highlight"><pre><span class="kd">function</span> <span class="nx">inject</span><span class="p">(</span><span class="nx">injectionKey</span> <span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Our decorator provides a factory function which will be invoked with an</span>
<span class="c1">// instance of the decorated Class and the name of the decorated property.</span>
<span class="k">return</span> <span class="kd">function</span> <span class="nx">recordInjection</span><span class="p">(</span><span class="nx">target</span> <span class="o">:</span> <span class="nb">Object</span><span class="p">,</span> <span class="nx">decoratedPropertyName</span> <span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="o">:</span> <span class="k">void</span> <span class="p">{</span>
<span class="c1">// Get a reference to the Class of the target object which has been</span>
<span class="c1">// decorated.</span>
<span class="kr">const</span> <span class="nx">targetType</span> <span class="o">:</span> <span class="p">{</span> <span class="nx">__inject__</span><span class="o">?:</span> <span class="nb">Object</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">constructor</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">targetType</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="s1">'__inject__'</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">targetType</span><span class="p">.</span><span class="nx">__inject__</span> <span class="o">=</span> <span class="p">{};</span>
<span class="p">}</span>
<span class="c1">// Associate this property with the injectionKey provided in the </span>
<span class="c1">// decorator call</span>
<span class="nx">targetType</span><span class="p">.</span><span class="nx">__inject__</span><span class="cp">[</span><span class="nx">decoratedPropertyName</span><span class="cp">]</span> <span class="o">=</span> <span class="nx">injectionKey</span><span class="p">;</span>
<span class="p">};</span>
<span class="p">}</span>
</pre></div>
<p>Now we need somewhere to record injection mappings so we have values to inject into decorated properties:</p>
<div class="highlight"><pre><span class="k">class</span> <span class="n">Injector</span> <span class="p">{</span>
<span class="n">private</span> <span class="nl">valuesByInjectionKey</span> <span class="p">:</span> <span class="p">{</span> <span class="p">[</span> <span class="nl">injectionKey</span> <span class="p">:</span> <span class="n">string</span> <span class="p">]</span> <span class="o">:</span> <span class="n">any</span> <span class="p">}</span> <span class="o">=</span> <span class="p">{};</span>
<span class="cm">/**</span>
<span class="cm"> * Associate an injectionKey with a value so that the supplied value can be </span>
<span class="cm"> * injected into properties of the target Class decorated with the `@inject` </span>
<span class="cm"> * decorator.</span>
<span class="cm"> *</span>
<span class="cm"> * @param {string} injectionKey</span>
<span class="cm"> * @param {*} value</span>
<span class="cm"> */</span>
<span class="n">mapValue</span><span class="p">(</span><span class="nl">injectionKey</span> <span class="p">:</span> <span class="n">string</span><span class="p">,</span> <span class="nl">value</span> <span class="p">:</span> <span class="n">any</span><span class="p">)</span> <span class="o">:</span> <span class="kt">void</span> <span class="p">{</span>
<span class="n">this</span><span class="p">.</span><span class="n">valuesByInjectionKey</span><span class="p">[</span><span class="n">injectionKey</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>Continuing our original example we would map the injectionKey <code>UserModel</code> to the instance of <code>UserModel</code> that we want injected, eg:</p>
<div class="highlight"><pre>const injector = new Injector();
injector.mapValue('UserModel', new UserModel());
</pre></div>
<p>Finally we need to introduce a factory function which will instantiate a class but also fulfill injections based on mappings in the Injector - to enable this we add an <code>#instantiate()</code> method to <code>Injector</code>.</p>
<div class="highlight"><pre><span class="k">class</span> <span class="n">Injector</span> <span class="p">{</span>
<span class="cm">/**</span>
<span class="cm"> * Create a new instance of the supplied Class fulfilling any property </span>
<span class="cm"> * injections which are present in the injectionRules map.</span>
<span class="cm"> */</span>
<span class="n">instantiate</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">(</span><span class="kt">Class</span> <span class="o">:</span> <span class="p">{</span> <span class="n">new</span><span class="p">(...</span><span class="nl">args</span><span class="p">:</span> <span class="n">any</span><span class="p">[])</span> <span class="o">:</span> <span class="n">T</span> <span class="p">})</span> <span class="o">:</span> <span class="n">T</span> <span class="p">{</span>
<span class="c1">// Start by creating a new instance of the target Class.</span>
<span class="k">const</span> <span class="nl">instance</span> <span class="p">:</span> <span class="n">any</span> <span class="o">=</span> <span class="n">new</span> <span class="kt">Class</span><span class="p">();</span>
<span class="c1">// Loop through all properties decorated with `@inject()` in this Class and</span>
<span class="c1">// try to satisfy them if there is a mapped value.</span>
<span class="k">for</span> <span class="p">(</span><span class="n">let</span> <span class="n">injectionPoint</span> <span class="n">of</span> <span class="n">this</span><span class="p">.</span><span class="n">getInjectionPoints</span><span class="p">(</span><span class="kt">Class</span><span class="p">))</span> <span class="p">{</span>
<span class="k">const</span> <span class="nl">injectionValue</span> <span class="p">:</span> <span class="n">any</span> <span class="o">=</span> <span class="n">this</span><span class="p">.</span><span class="n">valuesByInjectionKey</span><span class="p">[</span><span class="n">injectionPoint</span><span class="p">.</span><span class="n">injectionKey</span><span class="p">];</span>
<span class="c1">// Perform the injection if we have a value assigned to this injectionKey.</span>
<span class="k">if</span> <span class="p">(</span><span class="n">injectionValue</span><span class="p">)</span> <span class="p">{</span>
<span class="n">instance</span><span class="p">[</span><span class="n">injectionPoint</span><span class="p">.</span><span class="n">propertyName</span><span class="p">]</span> <span class="o">=</span> <span class="n">injectionValue</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">instance</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">private</span> <span class="n">getInjectionPoints</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">(</span><span class="kt">Class</span> <span class="o">:</span> <span class="p">{</span> <span class="n">__inject__</span><span class="o">?:</span> <span class="p">{</span> <span class="p">[</span> <span class="nl">prop</span> <span class="p">:</span> <span class="n">string</span> <span class="p">]</span> <span class="o">:</span> <span class="n">string</span> <span class="p">}</span> <span class="p">})</span> <span class="o">:</span> <span class="n">Array</span><span class="o"><</span><span class="n">InjectionPoint</span><span class="o">></span> <span class="p">{</span>
<span class="n">var</span> <span class="nl">result</span> <span class="p">:</span> <span class="n">Array</span><span class="o"><</span><span class="n">InjectionPoint</span><span class="o">></span> <span class="o">=</span> <span class="p">[];</span>
<span class="c1">// Retrieve the `__inject__` hash created by the @inject decorator from the</span>
<span class="c1">// target Class.</span>
<span class="k">if</span> <span class="p">(</span><span class="kt">Class</span><span class="p">.</span><span class="n">hasOwnProperty</span><span class="p">(</span><span class="err">'</span><span class="n">__inject__</span><span class="err">'</span><span class="p">))</span> <span class="p">{</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">Object</span><span class="p">.</span><span class="n">keys</span><span class="p">(</span><span class="kt">Class</span><span class="p">.</span><span class="n">__inject__</span><span class="p">)</span>
<span class="p">.</span><span class="n">map</span><span class="p">((</span><span class="nl">propertyName</span> <span class="p">:</span> <span class="n">string</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="nl">propertyName</span><span class="p">:</span> <span class="n">propertyName</span><span class="p">,</span>
<span class="nl">injectionKey</span><span class="p">:</span> <span class="kt">Class</span><span class="p">.</span><span class="n">__inject__</span><span class="p">[</span><span class="n">propertyName</span><span class="p">]</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">interface</span> <span class="n">InjectionPoint</span> <span class="p">{</span>
<span class="nl">propertyName</span> <span class="p">:</span> <span class="n">string</span><span class="p">;</span>
<span class="nl">injectionKey</span> <span class="p">:</span> <span class="n">string</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>The <code>Injector#intantiate()</code> method looks for the <code>#__inject__</code> property added to a Class' constructor function by the <code>@inject</code> decorator and then uses the values that hash contains to fulfil the decorated dependencies of the target Class - ie:</p>
<div class="highlight"><pre>const myUserModel = new UserModel();
injector.mapValue('UserModel', myUserModel);
// `#userModel` will be injected automatically by by injector
var myLoginService = injector.instantiate(LoginService)
myLoginService.performLogin();
</pre></div>
<p>A complete example with Mocha Tests provided over at <a href="https://github.com/jonnyreeves/ts-prop-injection/tree/01-property-injection">github.com/jonnyreeves/ts-prop-injection</a>. </p>
<p>Part 2: <a href="/2015/injecting-into-methods-with-typescript-decorators/">Adding support of method injection</a>.</p>
</div>
<hr />
</article>
<article>
<header>
<h2><a href="http://jonnyreeves.co.uk/2015/hello-typescript-and-mocha/#hello-typescript-and-mocha">Hello Typescript and Mocha!</a></h2>
<p>
Posted on Mon 27 July 2015 in <a href="http://jonnyreeves.co.uk/category/typescript.html">TypeScript</a> •
</p>
</header>
<div>
<p>Let's see what we can do with TypeScript then. First things first we need to create a local project to hack on:</p>
<div class="highlight"><pre>$ npm init
name: (hello-ts-mocha)
version: (1.0.0)
description: Getting started with TypeScript and mocha
entry point: (index.js)
test command: mocha
git repository:
keywords:
license: (ISC)
</pre></div>
<p>Now let's grab the TypeScript compiler so we can build the project:</p>
<div class="highlight"><pre>npm install typescript --save
</pre></div>
<p>Now let's grab Mocha for our tests</p>
<div class="highlight"><pre>npm install mocha --save-dev
</pre></div>
<h2>Create a Subject to Test</h2>
<p>Time to create our subject that we're going to test, let's do some TDD (: Create a new file, <code>index.ts</code> in the project root</p>
<div class="highlight"><pre>export default class Calculator {
add(x : number, y : number) : number {
return 0;
}
}
</pre></div>
<p>And now to compile this down to ES5 so we can execute it under node:</p>
<div class="highlight"><pre>./node_modules/typescript/bin/tsc index.ts --module commonjs
</pre></div>
<p>We can simplify the compilation step by adding a prepublish <a href="https://docs.npmjs.com/misc/scripts">npm script</a> to the <code>package.json</code> file:</p>
<div class="highlight"><pre>{
...
"scripts": {
"prepublish": "tsc index.ts --module commonjs ",
"test": "mocha"
}
...
}
</pre></div>
<p>We can now compile with <code>npm install</code>.</p>
<h2>Adding a Test</h2>
<p>Let's create our first test; following convention lets create our test-case in <code>test/CalculatorTest</code>.</p>
<p>Before writing the test-case we need to grab the Mocha Type Definitions (requried for the TypeScript compiler (<code>tsc</code>) to work). Easiest way to get these it to use the DefinitelyTyped TypeScript Definition manager (<code>tsd</code>).</p>
<div class="highlight"><pre>npm install tsd -g
</pre></div>
<p>Once installed we can use it to grab mocha's type definitions:</p>
<div class="highlight"><pre>tsd install mocha --save
</pre></div>
<p>This will add a <code>typings/</code> folder to our project. Here's what our source tree looks like so far:</p>
<div class="highlight"><pre>hello-ts-mocha
├── index.ts
├── node_modules
│ └── typescript
├── package.json
├── test
│ └── CalculatorTest.js
├── tsd.json
└── typings
├── mocha
└── tsd.d.ts
</pre></div>
<p>Now we can reference the mocha type definition using a <code>/// <reference /></code> comment:</p>
<div class="highlight"><pre><span class="o">///</span> <span class="o"><</span><span class="n">reference</span> <span class="n">path</span><span class="o">=</span><span class="s">"../typings/mocha/mocha.d.ts"</span> <span class="o">/></span>
<span class="kn">import</span> <span class="nn">Calculator</span> <span class="nn">from</span> <span class="s">'../index'</span><span class="p">;</span>
<span class="n">describe</span><span class="p">(</span><span class="s">'Calculator'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="n">var</span> <span class="n">subject</span> <span class="p">:</span> <span class="n">Calculator</span><span class="p">;</span>
<span class="n">beforeEach</span><span class="p">(</span><span class="n">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="n">subject</span> <span class="o">=</span> <span class="n">new</span> <span class="n">Calculator</span><span class="p">();</span>
<span class="p">});</span>
<span class="n">describe</span><span class="p">(</span><span class="s">'#add'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="n">it</span><span class="p">(</span><span class="s">'should add two numbers together'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="n">var</span> <span class="n">result</span> <span class="p">:</span> <span class="n">number</span> <span class="o">=</span> <span class="n">subject</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">result</span> <span class="o">!==</span> <span class="mi">5</span><span class="p">)</span> <span class="p">{</span>
<span class="n">throw</span> <span class="n">new</span> <span class="n">Error</span><span class="p">(</span><span class="s">'Expected 2 + 3 = 5 but was '</span> <span class="o">+</span> <span class="n">result</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">});</span>
</pre></div>
<p>Before we can run the tests we need to compile them, best way to do this is to add a <code>preTest</code> NPM script to invoke the typscript compiler before running mocha. Open up the <code>package.json</code> file again:</p>
<div class="highlight"><pre>{
...
"scripts": {
"pretest": "tsc test/*Test.ts --module commonjs",
"test": "mocha"
}
}
</pre></div>
<p>We can now run our test-case with <code>npm test</code>:</p>
<div class="highlight"><pre>$ npm test
> [email protected] pretest /private/tmp/foo
> tsc test/*Test.ts --module commonjs
> [email protected] test /private/tmp/foo
> mocha
Calculator
#add
1) should add two numbers together
0 passing (19ms)
1 failing
1) Calculator #add should add two numbers together:
Error: Expected 2 + 3 = 5 but was 0
at Context.<anonymous> (test/CalculatorTest.js:12:23)
</pre></div>
<p>Have fun fixing the test-case :) Full source over over at <a href="https://github.com/jonnyreeves/hello-ts-mocha">github.com/jonnyreeves/hello-ts-mocha</a></p>
</div>
<hr />
</article>
<article>
<header>
<h2><a href="http://jonnyreeves.co.uk/2014/simple-javascript-classes-in-commonjs/#simple-javascript-classes-in-commonjs">Simple JavaScript Classes in CommonJS</a></h2>
<p>
Posted on Sat 26 July 2014 in <a href="http://jonnyreeves.co.uk/category/javascript.html">JavaScript</a> •
</p>
</header>
<div>
<p>Having recently become an advocate of CommonJS modules and Browserify our team was looking for a simple way to define Plain old JavaScript Classes in our modules. We've settled on the following style, making use of <a href="https://github.com/justmoon/node-extend">extend</a> and <a href="https://github.com/isaacs/inherits">inherits</a>; two tiny, focused CommonJS modules which will add a whopping 2kb to our project when unminified.</p>
<div class="highlight"><pre><span class="s2">"use strict"</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">inherits</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">"inherits"</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">extend</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">"extends"</span><span class="p">);</span>
<span class="c1">// Declare the constructor and member properties.</span>
<span class="kd">function</span> <span class="nx">MyClass</span><span class="p">(</span><span class="nx">foo</span><span class="p">,</span> <span class="nx">bar</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Make a call to the Parent Class' constructor. The `_super` </span>
<span class="c1">// member is provided by the `inherits` call below and is</span>
<span class="c1">// preferred to calling the `MyParentClass` constructor function</span>
<span class="c1">// directly should we want to alter the inheritence.</span>
<span class="nx">MyClass</span><span class="p">.</span><span class="nx">super_</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">foo</span><span class="p">);</span>
<span class="c1">// Keep things straight forward and performant by simply hinting</span>
<span class="c1">// at privacy with a leading underscore.</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_bar</span> <span class="o">=</span> <span class="nx">bar</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Establish an inheritance relationship with another Class.</span>
<span class="nx">inherits</span><span class="p">(</span><span class="nx">MyClass</span><span class="p">,</span> <span class="nx">MyParentClass</span><span class="p">);</span>
<span class="c1">// Define static members directly on the Constructor function.</span>
<span class="nx">MyClass</span><span class="p">.</span><span class="nx">explode</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">"KABOOM!"</span><span class="p">);</span>
<span class="p">};</span>
<span class="c1">// Declare instance methods.</span>
<span class="nx">extend</span><span class="p">(</span><span class="nx">MyClass</span><span class="p">.</span><span class="nx">prototype</span><span class="p">,</span> <span class="p">{</span>
<span class="c1">// Place all methods on the prototype so they're shared amongst</span>
<span class="c1">// all instances. Using `extends` in this fashion ensures that</span>
<span class="c1">// the `constructor` property remains correctly pointed at the</span>
<span class="c1">// `MyClass` function so `instanceof` checks work as expected.</span>
<span class="nx">greet</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">someone</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Make a call to the Parent Class' `greet` method.</span>
<span class="kd">var</span> <span class="nx">greeting</span> <span class="o">=</span> <span class="nx">MyClass</span><span class="p">.</span><span class="nx">super_</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">greet</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">someone</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">greeting</span> <span class="o">+</span> <span class="s2">", dude!"</span><span class="p">;</span>
<span class="p">},</span>
<span class="c1">// Again, just hint at privacy with a leading underscore; doing</span>
<span class="c1">// so ensures you have easy access to `this` in each method.</span>
<span class="nx">_throbulate</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="c1">// Implementation omitted for brevity.</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="nx">module</span><span class="p">.</span><span class="kr">export</span> <span class="o">=</span> <span class="nx">MyClass</span><span class="p">;</span>
</pre></div>
</div>
</article>
<div class="pagination">
<a class="btn" href="http://jonnyreeves.co.uk/index2.html">
<i class="fa fa-angle-left"></i> Older Posts
</a>
</div>
<footer>
<p>© Jonny Reeves </p>
<p>Built using <a href="http://getpelican.com" target="_blank">Pelican</a> - <a href="https://github.com/alexandrevicenzi/flex" target="_blank">Flex</a> theme by <a href="http://alexandrevicenzi.com" target="_blank">Alexandre Vicenzi</a></p> </footer>
</main>
<script type="text/javascript">
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-30309890-1', 'auto');
ga('send', 'pageview');
</script>
<script type="application/ld+json">
{
"@context" : "http://schema.org",
"@type" : "Blog",
"name": " Jonny Reeves ",
"url" : "http://jonnyreeves.co.uk",
"image": "/images/avatar.jpg",
"description": ""
}
</script></body>
</html>