-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
1619 lines (1136 loc) · 103 KB
/
index.html
File metadata and controls
1619 lines (1136 loc) · 103 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
996
997
998
999
1000
<!DOCTYPE html>
<!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
<!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
<!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<title>whazzing around</title>
<meta name="author" content="Zachery Moneypenny">
<meta name="description" content="Since this summer I’ve been quite busy both at work and on local projects and
figured it was time to reflect and look towards the upcoming year …">
<!-- http://t.co/dKP3o1e -->
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="canonical" href="http://whazzing.com">
<link href="/favicon.png" rel="icon">
<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
<link href="/atom.xml" rel="alternate" title="whazzing around" type="application/atom+xml">
<script src="/javascripts/modernizr-2.0.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>!window.jQuery && document.write(unescape('%3Cscript src="./javascripts/libs/jquery.min.js"%3E%3C/script%3E'))</script>
<script src="/javascripts/octopress.js" type="text/javascript"></script>
<!--Fonts from Google"s Web font directory at http://google.com/webfonts -->
<link href="http://fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<link href="http://fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-8888483-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body >
<header role="banner"><hgroup>
<h1><a href="/">whazzing around</a></h1>
<h2>a thing on the internet</h2>
</hgroup>
</header>
<nav role="navigation"><ul class="subscription" data-subscription="rss">
<li><a href="/atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></li>
</ul>
<form action="http://google.com/search" method="get">
<fieldset role="search">
<input type="hidden" name="q" value="site:whazzing.com" />
<input class="search" type="text" name="q" results="0" placeholder="Search"/>
</fieldset>
</form>
<ul class="main-navigation">
<li><a href="/">Blog</a></li>
<li><a href="/blog/archives">Archives</a></li>
<li><a href="/presentations">Presentations</a></li>
<li><a href="/videos">Videos</a></li>
</ul>
</nav>
<div id="main">
<div id="content">
<div class="blog-index">
<article>
<header>
<h1 class="entry-title"><a href="/blog/2013/12/01/catching-up/">Catching Up</a></h1>
<p class="meta">
<time datetime="2013-12-01T08:45:00-06:00" pubdate data-updated="true">Dec 1<span>st</span>, 2013</time>
| <a href="/blog/2013/12/01/catching-up/#disqus_thread"
data-disqus-identifier="http://whazzing.com/blog/2013/12/01/catching-up/">Comments</a>
</p>
</header>
<div class="entry-content"><p>Since this summer I’ve been quite busy both at work and on local projects and
figured it was time to reflect and look towards the upcoming year.</p>
<h2>2013: The Year in Review</h2>
<p>Let’s talk about work stuff and home stuff.</p>
<h3>@Work</h3>
<p>At work, I started 2013 as primarily a <strong>C/C++ developer</strong> working on connecting
our flagship accounting application to connected services.
It was extremely interesting, if sometimes frustrating, to enhance a 15-year-old
code base to talk to a new generation of more nimble web services. Throughout
the summer we stamped out bugs and responded to early feedback from testers, as
well as worked on merges out to all the major versions of the code base. At the
same time we started prepping documentation and transition materials to ease the
difficulty in transferring knowledge of a 20+ year-old
code base to a new team.</p>
<p>Starting in August I began splitting my time between my desktop development
tasks and my new group. I’m now working on
<a href="http://paychecks.intuit.com">ViewMyPaycheck</a>, which is built on
<a href="http://backbonejs.org">Backbone.js</a>/HTML5/<a href="http://sass-lang.com">SCSS</a> for
the front-end and Java Web Services on the backend. It’s really my first time
developing in Java, and so I’m learning new things every day.</p>
<p>Luckily, due to my extensive tours of different Javascript frameworks in my
Rails projects at home I was able to come strong out of the gate in working
on the Backbone code. My experience with Sass allowed me to not only pick
up our styles quickly, but re-architect them to ease the evolution and
concurrent development by multiple engineers.</p>
<p>There was some tech debt wrapped up in the new code base but my excellent team
has thus far done a great job of prioritizing refactoring and
build/infrastructure improvements against the race to be feature-complete for
the 2013 tax season.</p>
<h3>@Home</h3>
<p>The holiday season of 2012 was quite a disaster in my family’s annual gift
exchange. Each adult had to submit a list of things they wanted to my mom, and
then she drew the names for <strong>two</strong> different exchanges: one for our immediate
family and one for our extended family. When Xmas Morning arrived we found a
ridiculous outcome: <em>since most everyone had gone for the ‘easy’ thing on their
target’s gift list, most people received two of one thing on their list</em>.</p>
<p>It spoke to an age-old problem of holiday gift-giving where, when grandparents,
aunts, uncles, etc. all clamor for a list of things a kid wants, they have no
way of ensuring that someone else didn’t already buy something on the list.
So in the ensuing winter, when I just so happened to have a lot of time on my
hands due to the birth of my son, I set out to create a web app to solve the
problem.</p>
<p>I finished a pre-alpha version of <a href="http://giftr.us">giftr.us</a> in the spring and
my family used it for birthdays throughout the spring and summer. It’s in a
pretty good spot right now, but I wasn’t able to finish the formal <strong>Gift
Exchange</strong> functionality for the 2013 Holidays.</p>
<p>This year I also began a journey to help out the
<a href="http://madrailers.org">MadRailers Meetup</a> which culminated in me taking over
the organization in the fall. I’ve been a member of the meetup since roundabouts
shortly after I moved back from California, and it’s enabled me to meet a ton
of great folks in the Madison tech community. I started out volunteering for
talks in the spring, and in the summer I started suggesting other topics or
speakers. By the time of the
<a href="http://madisonruby.com">Madison Ruby conference</a> we’d laid out a great schedule
of speakers throughout the end of the year, and I’m really pleased with where
the group is headed and our new space: the newly-renovated
<a href="http://mynewlibrary.org/">Madison Public Library</a>!</p>
<h2>2014: The Year to Come</h2>
<h3>@Work</h3>
<p>I’m incredibly excited to continue the modernization our Backbone app. A short
list of the changes and/or improvements I’m planning:</p>
<ul>
<li>Move to using DevOps-provided <a href="http://www.vagrantup.com/">Vagrant</a> images on
developer machines to more closely align development and deployment
environments.</li>
<li>Move our static site code (HTML/CSS/JS/Images) to a CDN to improve load times.</li>
<li>Move from <a href="http://ant.apache.org/">Ant</a> to <a href="http://gruntjs.com">Grunt</a> to
build the site (transpile SCSS, run JS tests, etc.)</li>
<li>Move from base Backbone.js to <a href="http://marionettejs.com/">Marionette.js</a> to
get better support for composing layouts, regions, and views.</li>
<li>Router changes to better control the fetching of data.</li>
</ul>
<p>These are all technical in nature; we’re obviously going to parallelize these
more architectural and platform-ish changes alongside the improvements to the
user workflow and enhancements based on user feedback. My ultimate goal is to
get the app into a continuously deployable build process. We’re a ways off right
now but it’s definitely doable.</p>
<h3>@Home</h3>
<p>I’m going to continue to develop Giftr’s gift exchange functionality, and I may
explore either turning it into a single-page app via Ember.js or similar, or
I may dip my toe into mobile native development. I’m incredibly interested
in learning more about <a href="http://xamarin.com/">Xamarin</a> for doing cross-platform
mobile development.</p>
<p>I’m also going to continue to develop more programming for MadRailers. In 2014
we’d love to explore joint-sponsored meetups with other local tech groups,
as well as move towards more diversity in our speaker lineups and membership.
We’ll be sponsoring more Newbie Nights as well as itermittent Hack Days.</p>
<h2>And Sooooo…</h2>
<p>It’s been a pretty good year! I was somehow able to maintain <em>some</em> level of
progress on my own development projects even with the birth of the first kid,
and I’m getting out of my comfort zone in my professional development which
is refreshing. Here’s to continuing to learn and improve in 2014!</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2013/04/11/automated-clickonce-build-and-deploy-using-powershell-and-msbuild/">Automated ClickOnce Build and Deploy Using Powershell and MSBuild</a></h1>
<p class="meta">
<time datetime="2013-04-11T12:14:00-05:00" pubdate data-updated="true">Apr 11<span>th</span>, 2013</time>
| <a href="/blog/2013/04/11/automated-clickonce-build-and-deploy-using-powershell-and-msbuild/#disqus_thread"
data-disqus-identifier="http://whazzing.com/blog/2013/04/11/automated-clickonce-build-and-deploy-using-powershell-and-msbuild/">Comments</a>
</p>
</header>
<div class="entry-content"><p>As I was nearing completion on the <a href="">WPF app I described in an earlier post</a> I became focused on how to easily build and deploy it to our developer and QA team. I had decided to use <a href="http://en.wikipedia.org/wiki/ClickOnce">ClickOnce</a> to facilitate easy updates, but I wanted to make it dead-simple to build and deploy the installer/updater to the network share so that anyone could easily contribute to the tool development.</p>
<p>At the time I’d been doing quite a bit of Powershell work and coincidentally I stumbled on the <a href="https://github.com/blog/1271-how-we-ship-github-for-windows">Github post</a> on how they build the <a href="http://windows.github.com/">Github for Windows application</a>. In that post I saw a tantalizing screen capture of their build/deploy script output and knew at once that I must have it.</p>
<p style="text-align:center;"><img src="/images/github-for-windows.png" height="183" width="673"/></p>
<p>From that image I reverse engineered the steps my script needed to take, and then I had to figure out how to implement each step. It’s worth noting that <strong>ClickOnce setting manipulation and deployment is not available via scripting or MSBuild commands</strong>. The code below includes my solution to these issues.</p>
<h2>Build & Deployment Script Output</h2>
<p>Below is the output of my own build and deployment script. A couple of important notes:</p>
<ul>
<li>My application consisted of one .exe file and one .dll file corresponding to two projects in a single Visual Studio solution. In the example code below I’ve replaced my .exe project name with <strong>Executable</strong> and my .dll project name with <strong>Library</strong>. The ClickOnce settings are maintained in the Executable project file.</li>
<li><strong>I should note that a decision I made is that the installer version should always be the same as the executable version.</strong> For a small tool like this it makes things simpler than to version the installer independently of the application it installs.</li>
</ul>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>PS C:\dev\tools\Executable> .\Deploy.ps1
</span><span class='line'> Checking prerequisites...
</span><span class='line'> Checking out the AssemblyInfo.cs files for version increment...
</span><span class='line'> Cleaning the build directory...
</span><span class='line'> Building Executable application...
</span><span class='line'> Building ClickOnce installer...
</span><span class='line'> Deploying updates to network server...
</span><span class='line'> Comitting version increments to Perforce...
</span><span class='line'>PS C:\dev\tools\Executable></span></code></pre></td></tr></table></div></figure>
<p>The script is written to be very silent unless an error occurs, so below is a description in more detail. The script does the following:</p>
<ul>
<li>Checks to ensure that the current user has prerequisites installed (in this case perforce).</li>
<li>Checks out the appropriate files needed to increment the version number of the DLL, executable, and ClickOnce installer.</li>
<li>Cleans the build directory.</li>
<li>Builds the DLL and executable.</li>
<li>Retrieves the (file) version of the newly-build executable.</li>
<li>Forces (hacks) the executable version into the executable’s .csproj definition of the ClickOnce settings.</li>
<li>Builds the installer with the new version and the just-built binaries.</li>
<li>Copies all of the installer files to the appropriate network fileserver.</li>
<li>Commit the changes to the AssemblyInfo and csproj files (i.e., the version changes).</li>
</ul>
<h2>Build & Deployment Script</h2>
<p><strong>Please note</strong> that I’ve changed a few things about the script below:</p>
<ul>
<li>Perforce repository paths</li>
<li>Network deployment paths</li>
<li>Binary names</li>
</ul>
<p>I’ve included the entire (sanitized) script below, and after it describe in greater detail the interesting parts.</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Write-Host "Tool Build/Deployment Script"
</span><span class='line'>
</span><span class='line'>$outputPrefix = " "
</span><span class='line'>$msbuild = "C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.EXE"
</span><span class='line'>
</span><span class='line'>Write-Host $outputPrefix"Checking prerequisites..."
</span><span class='line'>
</span><span class='line'>$p4Output = p4
</span><span class='line'>if($p4Output -match "'p4' is not recognized as an internal or external command")
</span><span class='line'>{
</span><span class='line'> Write-Error "Cannot find p4.exe in your PATH."
</span><span class='line'> Exit
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>Write-Host $outputPrefix"Checking out the AssemblyInfo.cs files for version increment..."
</span><span class='line'>p4 edit //my/project/tool/path/Library/Properties/AssemblyInfo.cs | Out-Null
</span><span class='line'>p4 edit //my/project/tool/path/Executable/Properties/AssemblyInfo.cs | Out-Null
</span><span class='line'>p4 edit //my/project/tool/path/Executable/Executable.csproj | Out-Null
</span><span class='line'>
</span><span class='line'>Write-Host $outputPrefix"Cleaning the build directory..."
</span><span class='line'>Invoke-Expression "$msbuild Executable\Executable.csproj /p:Configuration=Release /p:Platform=AnyCPU /t:clean /v:quiet /nologo"
</span><span class='line'>
</span><span class='line'>Write-Host $outputPrefix"Building Executable application..."
</span><span class='line'>Invoke-Expression "$msbuild Executable\Executable.csproj /p:Configuration=Release /p:Platform=AnyCPU /t:build /v:quiet /nologo"
</span><span class='line'>
</span><span class='line'>$newExeVersion = Get-ChildItem .\Executable\bin\Release\Executable.exe | Select-Object -ExpandProperty VersionInfo | % { $_.FileVersion }
</span><span class='line'>$newLibVersion = Get-ChildItem .\Executable\bin\Release\Library.dll | Select-Object -ExpandProperty VersionInfo | % { $_.FileVersion }
</span><span class='line'>
</span><span class='line'>Write-Host $outputPrefix"Building ClickOnce installer..."
</span><span class='line'>#
</span><span class='line'># Because the ClickOnce target doesn't automatically update or sync the application version
</span><span class='line'># with the assembly version of the EXE, we need to grab the version off of the built assembly
</span><span class='line'># and update the Executable.csproj file with the new application version.
</span><span class='line'>#
</span><span class='line'>$ProjectXml = [xml](Get-Content Executable\Executable.csproj)
</span><span class='line'>$ns = new-object Xml.XmlNamespaceManager $ProjectXml.NameTable
</span><span class='line'>$ns.AddNamespace('msb', 'http://schemas.microsoft.com/developer/msbuild/2003')
</span><span class='line'>$AppVersion = $ProjectXml.SelectSingleNode("//msb:Project/msb:PropertyGroup/msb:ApplicationVersion", $ns)
</span><span class='line'>$AppVersion.InnerText = $newExeVersion
</span><span class='line'>$TargetPath = Resolve-Path "Executable\Executable.csproj"
</span><span class='line'>$ProjectXml.Save($TargetPath)
</span><span class='line'>
</span><span class='line'>Invoke-Expression "$msbuild Executable\Executable.csproj /p:Configuration=Release /p:Platform=AnyCPU /t:publish /v:quiet /nologo"
</span><span class='line'>
</span><span class='line'>Write-Host $outputPrefix"Deploying updates to network server..."
</span><span class='line'>$LocalInstallerPath = (Resolve-Path "Executable\bin\Release\app.publish").ToString() + "\*"
</span><span class='line'>$RemoteInstallerPath = "\\network\path\Executable\DesktopClient\"
</span><span class='line'>Copy-Item $LocalInstallerPath $RemoteInstallerPath -Recurse -Force
</span><span class='line'>
</span><span class='line'>Write-Host $outputPrefix"Committing version increments to Perforce..."
</span><span class='line'>p4 submit -d "Updating Executable ClickOnce Installer to version $newExeVersion" //my/project/tool/path/Executable/Executable.csproj | Out-Null
</span><span class='line'>p4 submit -d "Updating Library to version $newLibVersion" //my/project/tool/path/Library/Properties/AssemblyInfo.cs | Out-Null
</span><span class='line'>p4 submit -d "Updating Executable to version $newExeVersion" //my/project/tool/path/Executable/Properties/AssemblyInfo.cs | Out-Null</span></code></pre></td></tr></table></div></figure>
<h3>Automated Version Increment</h3>
<p>You may have noticed that I don’t take any specific action to manage the version numbers of Executable.exe and Library.dll even though I explicitly check out the AssemblyInfo.cs files.</p>
<p>The <a href="http://msbuildextensionpack.codeplex.com/">MSBuild Extension Pack</a> is an open-source collection of MSBuild targets that make things like version management much easier. After adding the extensions to a relative path to my projects I just needed to add the following near the bottom of <code>Executable.csproj</code>.</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt"><PropertyGroup></span>
</span><span class='line'> <span class="nt"><ExtensionTasksPath></span>..\contrib\ExtensionPack\4.0.6.0\<span class="nt"></ExtensionTasksPath></span>
</span><span class='line'><span class="nt"></PropertyGroup></span>
</span><span class='line'><span class="nt"><Import</span> <span class="na">Project=</span><span class="s">"$(ExtensionTasksPath)MSBuild.ExtensionPack.VersionNumber.targets"</span>
</span><span class='line'> <span class="na">Condition=</span><span class="s">" '$(BuildingInsideVisualStudio)'!='true' "</span> <span class="nt">/></span>
</span><span class='line'><span class="nt"><PropertyGroup</span> <span class="na">Condition=</span><span class="s">" '$(BuildingInsideVisualStudio)'!='true' "</span><span class="nt">></span>
</span><span class='line'> <span class="nt"><AssemblyMajorVersion></span>1<span class="nt"></AssemblyMajorVersion></span>
</span><span class='line'> <span class="nt"><AssemblyMinorVersion></span>3<span class="nt"></AssemblyMinorVersion></span>
</span><span class='line'> <span class="nt"><AssemblyFileMajorVersion></span>1<span class="nt"></AssemblyFileMajorVersion></span>
</span><span class='line'> <span class="nt"><AssemblyFileMinorVersion></span>3<span class="nt"></AssemblyFileMinorVersion></span>
</span><span class='line'> <span class="nt"><AssemblyInfoSpec></span>Properties\AssemblyInfo.cs<span class="nt"></AssemblyInfoSpec></span>
</span><span class='line'><span class="nt"></PropertyGroup></span>
</span></code></pre></td></tr></table></div></figure>
<p>A couple things to note here:</p>
<ul>
<li>The <code>Condition</code> attributes on lines 5 & 6 ensure that the version increments only occur when I run the <code>Deploy.ps1</code> script, as opposed to every time I build through the Visual Studio IDE.</li>
<li>I am holding the Major and Minor versions fixed via lines 7-10, so that only the Build and Revision numbers are auto-incremented.</li>
</ul>
<p>The above code is used <strong>both</strong> in <code>Executable.csproj</code> and <code>Library.csproj</code>, so that both the executable and the library have their version numbers managed. In doing this I can also change the major/minor versions of the executable and library independently.</p>
<h3>Propagate Exe Version to ClickOnce Installer</h3>
<p>As I mentioned earlier, I wanted to keep the installer version the same as the executable version. The problem was that there’s no way to manage the ClickOnce settings via MSBuild or other API. Lines 35-41 of the script are the, ahem, workaround that I devised.</p>
<p>Since we want to set the ClickOnce installer version to the same as the executable, we must first fetch the executable version:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$newExeVersion = Get-ChildItem .\Executable\bin\Release\Executable.exe | Select-Object -ExpandProperty VersionInfo | % { $_.FileVersion }</span></code></pre></td></tr></table></div></figure>
<p>This line uses the <a href="http://technet.microsoft.com/en-us/library/ee176927.aspx">powerful object piping capabilities in Powershell</a> to fetch the FileVersion property from the assembly itself.</p>
<p>Once we have the executable version, we must then somehow insert it into <code>Executable.csproj</code> where the ClickOnce settings are defined. For reference, the associated XML from the csproj file is:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt"><PropertyGroup></span>
</span><span class='line'> <span class="nt"><ApplicationVersion></span>1.3.0407.01<span class="nt"></ApplicationVersion></span>
</span><span class='line'><span class="nt"></PropertyGroup></span>
</span></code></pre></td></tr></table></div></figure>
<p>Lines 35-41 read in the csproj file as XML and extracts the <code>ApplicationVersion</code> node. It then replaces the contents of that node with the assembly version we read from the executable and saves the entire XML structure back to the csproj file.</p>
<h2>Summary</h2>
<p>Through automating the build and deployment process I’ve learned a lot about Powershell and MSBuild and I’ll definitely be improving this in the future. The great thing about this particular combination of tools is that Powershell provides the glue that holds together the powerful build automation (and logging) that MSBuild offers.</p>
<p>While it’s unfortunate that ClickOnce has so many manual aspects to it (and I think I know why) the ease of XML manipulation and file processing from Powershell make it easy to work around ClickOnce’s lack of automation.</p>
<p>In the future I may look at moving the install/upgrade process to the <a href="http://www.wixtoolset.org/">WiX Toolset</a> as it’s much more configurable and automatable. ClickOnce was really a stop-gap solution because it’s for an internal tool and simple enough for my bootstrapping needs.</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2013/02/22/responsive-wpf-applications-with-reactiveui/">Responsive WPF Applications With ReactiveUI</a></h1>
<p class="meta">
<time datetime="2013-02-22T21:25:00-06:00" pubdate data-updated="true">Feb 22<span>nd</span>, 2013</time>
| <a href="/blog/2013/02/22/responsive-wpf-applications-with-reactiveui/#disqus_thread"
data-disqus-identifier="http://whazzing.com/blog/2013/02/22/responsive-wpf-applications-with-reactiveui/">Comments</a>
</p>
</header>
<div class="entry-content"><p>Learn about my newfound love for the <a href="http://reactiveui.net/">ReactiveUI MVVM framework</a>.</p>
<p>My day job is to wrangle 12 million lines of 20+ year-old C and C++ using a custom Win32-based UI library that was built in the mid-90s and never fundamentally improved. It does what it was designed to do really well (bind transactions to views, zoom between list items, the transactions they compose, and the reports on those transactions) but sometimes I idly wonder what’s been going on in bleeding edge Windows app development in the interim.</p>
<p>Last week I downloaded and played around with <a href="http://windows.github.com/">GitHub’s excellent Windows client</a>. I vaguely remembered <a href="https://github.com/blog/1151-designing-github-for-windows">a blog post</a> on the gear underlying the desktop app awhile back, and I was interested to see where it was at these days. You know you’re a geek when you read the entire list of licenses in the About view to get a sense of the underlying technology.</p>
<p>After looking around at the various .NET libraries involved, and reading <a href="https://github.com/blog/1127-github-for-windows">some follow-up</a> <a href="https://github.com/blog/1420-github-for-windows-recent-improvements">blog posts</a> I decided to <strong>build a front-end to a developer tool</strong> I whipped up for our developers and QA engineers at work.</p>
<p>Essentially, in debug mode QuickBooks Payroll can talk to a variety of backend environments. Configuring the various endpoints involves editing several config files that are in different places depending on whether you have an installed build or a developer build. To make it easier to configure things I wrote a command-line tool that can tell you what environments you’re currently configured to talk to, as well as list available environments and change the current environment. I built the command-line tool on top of a library that implemented all the core logic because I knew that I’d eventually want to build an easier-to-use GUI on top of it.</p>
<p>So as a little weekend project I took inspiration from GitHub for Windows Metro/Modern visual design and a desire to look further into ReactiveUI’s take on multi-threaded UI.</p>
<p>Multi-threaded UI development typically has one sticking point; it’s easy enough to define a lambda or function and set it up to run on a separate thread but one has to be very careful when moving the result of that lambda back onto the UI thread to display it. <strong>Reactive’s</strong> secret sauce is to simplify this dangerous activity and deliver a true ‘fire-and-forget’ multi-thread solution.</p>
<p>Thus far I’ve found that it’s very easy to chain async commands AND provide a nice responsive user experience. In cases where you have certain application behaviors that are gated upon other actions completing the ReactiveUI framework makes it extremely simple.</p>
<p>An example:</p>
<ul>
<li>App bootstraps.</li>
<li>Check for environment definition updates.</li>
<li>If updates are available; download and install updates.</li>
<li>When updates are complete, or if no updates are available then validate current saved environment settings.</li>
<li>If settings are invalid or missing show the special UI telling user to edit the app settings.</li>
<li>If settings are valid then discover the current environment.</li>
<li>Once the current environment has been determined; set the properties on the view model that are bound to the UI that describe the current environment.</li>
</ul>
<p>The state machine representing the above workflow was implemented with three View Models bound to one XAML MainWindow. The cleanest one is below, and I’ve commented how the commands to <strong>check for updates</strong> and <strong>download and install updates</strong> are implemented (I’m still working on tightening up the other two).</p>
<p>I’ve included the entire file below because I think it’s valuable to view the initialization of the commands and observers and their targets in context. This view model encapsulates all of the updater functionality and how the different states of updating are exposed to the UI.</p>
<figure class='code'><figcaption><span>EnvironmentsUpdaterViewModel.cs </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
<span class='line-number'>86</span>
<span class='line-number'>87</span>
<span class='line-number'>88</span>
<span class='line-number'>89</span>
<span class='line-number'>90</span>
<span class='line-number'>91</span>
<span class='line-number'>92</span>
<span class='line-number'>93</span>
<span class='line-number'>94</span>
<span class='line-number'>95</span>
<span class='line-number'>96</span>
<span class='line-number'>97</span>
<span class='line-number'>98</span>
<span class='line-number'>99</span>
<span class='line-number'>100</span>
<span class='line-number'>101</span>
<span class='line-number'>102</span>
<span class='line-number'>103</span>
<span class='line-number'>104</span>
<span class='line-number'>105</span>
<span class='line-number'>106</span>
<span class='line-number'>107</span>
<span class='line-number'>108</span>
<span class='line-number'>109</span>
<span class='line-number'>110</span>
<span class='line-number'>111</span>
<span class='line-number'>112</span>
<span class='line-number'>113</span>
<span class='line-number'>114</span>
<span class='line-number'>115</span>
<span class='line-number'>116</span>
<span class='line-number'>117</span>
<span class='line-number'>118</span>
<span class='line-number'>119</span>
<span class='line-number'>120</span>
<span class='line-number'>121</span>
<span class='line-number'>122</span>
</pre></td><td class='code'><pre><code class='csharp'><span class='line'><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
</span><span class='line'><span class="k">using</span> <span class="nn">System.Collections.Generic</span><span class="p">;</span>
</span><span class='line'><span class="k">using</span> <span class="nn">System.ComponentModel</span><span class="p">;</span>
</span><span class='line'><span class="k">using</span> <span class="nn">System.Linq</span><span class="p">;</span>
</span><span class='line'><span class="k">using</span> <span class="nn">System.Reactive.Linq</span><span class="p">;</span>
</span><span class='line'><span class="k">using</span> <span class="nn">System.Text</span><span class="p">;</span>
</span><span class='line'><span class="k">using</span> <span class="nn">System.Threading.Tasks</span><span class="p">;</span>
</span><span class='line'><span class="k">using</span> <span class="nn">Intuit.Payroll.Tools.SetPayrollEnvironment</span><span class="p">;</span>
</span><span class='line'><span class="k">using</span> <span class="nn">ReactiveUI</span><span class="p">;</span>
</span><span class='line'><span class="k">using</span> <span class="nn">ReactiveUI.Xaml</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="k">namespace</span> <span class="nn">Intuit.Payroll.Tools.PayrollEnvironments</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="k">public</span> <span class="k">enum</span> <span class="n">UpdateState</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="n">Uninitialized</span><span class="p">,</span> <span class="c1">// No action has been taken yet</span>
</span><span class='line'> <span class="n">CheckingForUpdates</span><span class="p">,</span> <span class="c1">// Checking the update source for possible updates</span>
</span><span class='line'> <span class="n">UpdateAvailable</span><span class="p">,</span> <span class="c1">// An update is available for download</span>
</span><span class='line'> <span class="n">ApplyingUpdates</span><span class="p">,</span> <span class="c1">// Currently downloading and installing updates</span>
</span><span class='line'> <span class="n">Completed</span> <span class="c1">// Used when updates have completed or when no updates are available</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'>
</span><span class='line'> <span class="k">public</span> <span class="k">class</span> <span class="nc">EnvironmentsUpdaterViewModel</span> <span class="p">:</span> <span class="n">ReactiveObject</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="k">private</span> <span class="n">PayrollEnvironmentConfiguration</span> <span class="n">_EnvConfig</span><span class="p">;</span>
</span><span class='line'> <span class="k">private</span> <span class="n">QuickBooksInformation</span> <span class="n">_LocalSettings</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'> <span class="k">public</span> <span class="nf">EnvironmentsUpdaterViewModel</span><span class="p">(</span><span class="n">QuickBooksInformation</span> <span class="n">localInfo</span><span class="p">)</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="n">_LocalSettings</span> <span class="p">=</span> <span class="n">localInfo</span><span class="p">;</span>
</span><span class='line'> <span class="n">Status</span> <span class="p">=</span> <span class="n">_LocalSettings</span><span class="p">.</span><span class="n">IsValid</span> <span class="p">?</span> <span class="n">Properties</span><span class="p">.</span><span class="n">Resources</span><span class="p">.</span><span class="n">InitialUpdateMsg</span>
</span><span class='line'> <span class="p">:</span> <span class="n">Properties</span><span class="p">.</span><span class="n">Resources</span><span class="p">.</span><span class="n">InvalidSettingsUpdaterMsg</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'> <span class="n">UpdateState</span> <span class="p">=</span> <span class="n">UpdateState</span><span class="p">.</span><span class="n">Uninitialized</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'> <span class="n">InitializeConfiguration</span><span class="p">();</span>
</span><span class='line'>
</span><span class='line'> <span class="c1">// Creating the EnvironmentsFileUpdater implicitly goes out to the update location</span>
</span><span class='line'> <span class="c1">// (likely on a local or VPN network share), so we want to make sure it's async.</span>
</span><span class='line'> <span class="n">CheckForUpdates</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ReactiveAsyncCommand</span><span class="p">(</span><span class="k">null</span><span class="p">,</span> <span class="m">1</span><span class="p">);</span>
</span><span class='line'> <span class="n">var</span> <span class="n">updaterFuture</span> <span class="p">=</span> <span class="n">CheckForUpdates</span><span class="p">.</span><span class="n">RegisterAsyncFunction</span><span class="p">(</span><span class="n">_</span> <span class="p">=></span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="n">UpdateState</span> <span class="p">=</span> <span class="n">UpdateState</span><span class="p">.</span><span class="n">CheckingForUpdates</span><span class="p">;</span>
</span><span class='line'> <span class="k">return</span> <span class="k">new</span> <span class="nf">EnvironmentsFileUpdater</span><span class="p">(</span><span class="n">_EnvConfig</span><span class="p">);</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'> <span class="n">_Updater</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ObservableAsPropertyHelper</span><span class="p"><</span><span class="n">EnvironmentsFileUpdater</span><span class="p">>(</span><span class="n">updaterFuture</span><span class="p">,</span> <span class="n">_</span> <span class="p">=></span> <span class="n">raisePropertyChanged</span><span class="p">(</span><span class="s">"Updater"</span><span class="p">));</span>
</span><span class='line'>
</span><span class='line'> <span class="c1">// When the updater has been created, initialized and set then check if any updates</span>
</span><span class='line'> <span class="c1">// are available on the update server.</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="n">ObservableForProperty</span><span class="p">(</span><span class="n">x</span> <span class="p">=></span> <span class="n">x</span><span class="p">.</span><span class="n">Updater</span><span class="p">).</span><span class="n">Subscribe</span><span class="p">(</span><span class="n">_</span> <span class="p">=></span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="n">Updater</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="n">UpdateState</span> <span class="p">=</span> <span class="n">Updater</span><span class="p">.</span><span class="n">UpdateAvailable</span> <span class="p">?</span> <span class="n">UpdateState</span><span class="p">.</span><span class="n">UpdateAvailable</span> <span class="p">:</span> <span class="n">UpdateState</span><span class="p">.</span><span class="n">Completed</span><span class="p">;</span>
</span><span class='line'> <span class="n">Status</span> <span class="p">=</span> <span class="n">Updater</span><span class="p">.</span><span class="n">UpdateAvailable</span> <span class="p">?</span> <span class="n">Properties</span><span class="p">.</span><span class="n">Resources</span><span class="p">.</span><span class="n">EnvironmentUpdatesAvailableMsg</span>
</span><span class='line'> <span class="p">:</span> <span class="kt">string</span><span class="p">.</span><span class="n">Format</span><span class="p">(</span><span class="n">Properties</span><span class="p">.</span><span class="n">Resources</span><span class="p">.</span><span class="n">CurrentEnvironmentMessage</span><span class="p">,</span> <span class="n">Updater</span><span class="p">.</span><span class="n">LatestVersion</span><span class="p">);</span>
</span><span class='line'> <span class="c1">// If updates are available; download and install them</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="n">UpdateState</span> <span class="p">==</span> <span class="n">UpdateState</span><span class="p">.</span><span class="n">UpdateAvailable</span><span class="p">)</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="n">UpdateState</span> <span class="p">=</span> <span class="n">UpdateState</span><span class="p">.</span><span class="n">ApplyingUpdates</span><span class="p">;</span>
</span><span class='line'> <span class="n">DownloadUpdates</span><span class="p">.</span><span class="n">Execute</span><span class="p">(</span><span class="k">null</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'>
</span><span class='line'> <span class="c1">// After we download and apply updates, update the status and mark the workflow as completed.</span>
</span><span class='line'> <span class="n">DownloadUpdates</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ReactiveAsyncCommand</span><span class="p">(</span><span class="k">null</span><span class="p">,</span> <span class="m">1</span><span class="p">);</span>
</span><span class='line'> <span class="n">DownloadUpdates</span><span class="p">.</span><span class="n">RegisterAsyncAction</span><span class="p">(</span><span class="n">_</span> <span class="p">=></span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="n">Updater</span><span class="p">.</span><span class="n">UpdateToVersion</span><span class="p">(</span><span class="n">Updater</span><span class="p">.</span><span class="n">LatestVersion</span><span class="p">))</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="n">Status</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Format</span><span class="p">(</span><span class="n">Properties</span><span class="p">.</span><span class="n">Resources</span><span class="p">.</span><span class="n">CurrentEnvironmentMessage</span><span class="p">,</span> <span class="n">Updater</span><span class="p">.</span><span class="n">LatestVersion</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="k">else</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="n">Status</span> <span class="p">=</span> <span class="n">Properties</span><span class="p">.</span><span class="n">Resources</span><span class="p">.</span><span class="n">EnvironmentUpdateErrorMsg</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="n">UpdateState</span> <span class="p">=</span> <span class="n">UpdateState</span><span class="p">.</span><span class="n">Completed</span><span class="p">;</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'>
</span><span class='line'> <span class="k">private</span> <span class="k">void</span> <span class="nf">InitializeConfiguration</span><span class="p">()</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="k">try</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="n">_EnvConfig</span> <span class="p">=</span> <span class="n">EnvironmentManager</span><span class="p">.</span><span class="n">GetEnvironmentConfiguration</span><span class="p">(</span><span class="n">_LocalSettings</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span><span class="p">)</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="n">_EnvConfig</span> <span class="p">=</span> <span class="k">new</span> <span class="n">PayrollEnvironmentConfiguration</span> <span class="p">{</span> <span class="n">Version</span> <span class="p">=</span> <span class="m">0</span><span class="p">,</span>
</span><span class='line'> <span class="n">UpdatesLocation</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span> <span class="p">};</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'>
</span><span class='line'> <span class="k">private</span> <span class="n">ObservableAsPropertyHelper</span><span class="p"><</span><span class="n">EnvironmentsFileUpdater</span><span class="p">></span> <span class="n">_Updater</span><span class="p">;</span>
</span><span class='line'> <span class="k">private</span> <span class="n">EnvironmentsFileUpdater</span> <span class="n">Updater</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_Updater</span><span class="p">.</span><span class="n">Value</span><span class="p">;</span> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="cp">#pragma warning disable 0649</span>
</span><span class='line'> <span class="k">private</span> <span class="n">UpdateState</span> <span class="n">_UpdateState</span><span class="p">;</span>
</span><span class='line'><span class="cp">#pragma warning restore 0649</span>
</span><span class='line'> <span class="k">public</span> <span class="n">UpdateState</span> <span class="n">UpdateState</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_UpdateState</span><span class="p">;</span> <span class="p">}</span>
</span><span class='line'> <span class="k">set</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="n">RaiseAndSetIfChanged</span><span class="p">(</span><span class="k">value</span><span class="p">);</span> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="cp">#pragma warning disable 0649</span>
</span><span class='line'> <span class="k">private</span> <span class="kt">string</span> <span class="n">_Status</span><span class="p">;</span>
</span><span class='line'><span class="cp">#pragma warning restore 0649</span>
</span><span class='line'> <span class="k">public</span> <span class="kt">string</span> <span class="n">Status</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_Status</span><span class="p">;</span> <span class="p">}</span>
</span><span class='line'> <span class="k">set</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="n">RaiseAndSetIfChanged</span><span class="p">(</span><span class="k">value</span><span class="p">);</span> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'>
</span><span class='line'> <span class="k">public</span> <span class="n">ReactiveAsyncCommand</span> <span class="n">CheckForUpdates</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">protected</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span><span class='line'> <span class="k">public</span> <span class="n">ReactiveAsyncCommand</span> <span class="n">DownloadUpdates</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">protected</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>The data that the updater object fetches from is located on an intranet share, and so it was important to check for and apply updates asynchronously (especially if, like me, you’re working over the VPN).</p>
<p>I’m unsure at this point if it’s ok that I’m updating the <code>UpdateState</code> property from within many of the lambdas. I feel that there’s something risky going on there but everything seems to work fine for me as it is.</p>
<p>I’ll break down the two main uses of Reactive’s async commands below. Note that I’m using the <code>UpdateState</code> property primarily outside of this class; the window binds certain elements’ visibility to the state of the updater object, but only when the state has certain values.</p>
<figure class='code'><figcaption><span>Initialize the Check for Updates Command </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='csharp'><span class='line'><span class="n">CheckForUpdates</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ReactiveAsyncCommand</span><span class="p">(</span><span class="k">null</span><span class="p">,</span> <span class="m">1</span><span class="p">);</span>
</span><span class='line'><span class="n">var</span> <span class="n">updaterFuture</span> <span class="p">=</span> <span class="n">CheckForUpdates</span><span class="p">.</span><span class="n">RegisterAsyncFunction</span><span class="p">(</span><span class="n">_</span> <span class="p">=></span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="c1">// Set the current state</span>
</span><span class='line'> <span class="n">UpdateState</span> <span class="p">=</span> <span class="n">UpdateState</span><span class="p">.</span><span class="n">CheckingForUpdates</span><span class="p">;</span>
</span><span class='line'> <span class="c1">// The network access occurs in the updater constructor; this line is</span>
</span><span class='line'> <span class="c1">// why we want this to be done asynchronously</span>
</span><span class='line'> <span class="k">return</span> <span class="k">new</span> <span class="nf">EnvironmentsFileUpdater</span><span class="p">(</span><span class="n">_EnvConfig</span><span class="p">);</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'><span class="c1">// Connect the future object to the helper object that backs the Updater property.</span>
</span><span class='line'><span class="n">_Updater</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ObservableAsPropertyHelper</span><span class="p"><</span><span class="n">EnvironmentsFileUpdater</span><span class="p">>(</span><span class="n">updaterFuture</span><span class="p">,</span> <span class="n">_</span> <span class="p">=></span> <span class="n">raisePropertyChanged</span><span class="p">(</span><span class="s">"Updater"</span><span class="p">));</span>
</span></code></pre></td></tr></table></div></figure>
<p>When the async function returns the new <code>EnvironmentsFileUpdater</code> instance and sets the backing field of the <code>Updater</code> property, I then lean on an Observable. Below you can see that I set it up such that when the <code>Updater</code> field changes we check whether an update is available and then either kick off the <code>DownloadUpdates</code> command or set the overall state to <code>UpdateState.Completed</code> to signal to the external listeners that the update process is complete.</p>
<p>The <code>Status</code> property is the string value that’s bound to the UI that updates the user as to what’s happening in the update process.</p>
<figure class='code'><figcaption><span>Initialize the Updater Observer </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class='csharp'><span class='line'><span class="c1">// When the updater has been created, initialized and set then check if any updates</span>
</span><span class='line'><span class="c1">// are available on the update server.</span>
</span><span class='line'><span class="k">this</span><span class="p">.</span><span class="n">ObservableForProperty</span><span class="p">(</span><span class="n">x</span> <span class="p">=></span> <span class="n">x</span><span class="p">.</span><span class="n">Updater</span><span class="p">).</span><span class="n">Subscribe</span><span class="p">(</span><span class="n">_</span> <span class="p">=></span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="n">Updater</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="n">UpdateState</span> <span class="p">=</span> <span class="n">Updater</span><span class="p">.</span><span class="n">UpdateAvailable</span> <span class="p">?</span> <span class="n">UpdateState</span><span class="p">.</span><span class="n">UpdateAvailable</span> <span class="p">:</span> <span class="n">UpdateState</span><span class="p">.</span><span class="n">Completed</span><span class="p">;</span>
</span><span class='line'> <span class="n">Status</span> <span class="p">=</span> <span class="n">Updater</span><span class="p">.</span><span class="n">UpdateAvailable</span> <span class="p">?</span> <span class="n">Properties</span><span class="p">.</span><span class="n">Resources</span><span class="p">.</span><span class="n">EnvironmentUpdatesAvailableMsg</span>
</span><span class='line'> <span class="p">:</span> <span class="kt">string</span><span class="p">.</span><span class="n">Format</span><span class="p">(</span><span class="n">Properties</span><span class="p">.</span><span class="n">Resources</span><span class="p">.</span><span class="n">CurrentEnvironmentMessage</span><span class="p">,</span> <span class="n">Updater</span><span class="p">.</span><span class="n">LatestVersion</span><span class="p">);</span>
</span><span class='line'> <span class="c1">// If updates are available; download and install them</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="n">UpdateState</span> <span class="p">==</span> <span class="n">UpdateState</span><span class="p">.</span><span class="n">UpdateAvailable</span><span class="p">)</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="n">UpdateState</span> <span class="p">=</span> <span class="n">UpdateState</span><span class="p">.</span><span class="n">ApplyingUpdates</span><span class="p">;</span>
</span><span class='line'> <span class="n">DownloadUpdates</span><span class="p">.</span><span class="n">Execute</span><span class="p">(</span><span class="k">null</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">});</span>
</span></code></pre></td></tr></table></div></figure>
<p>The <code>DownloadUpdates</code> command actually handles the work of downloading the latest environment definitions and installing them to the appropriate local storage mechanism. Again, it must reach out over the intranet and so it’s best to make this action asynchronous. Most of the code you see below is concerned with updating the UI-bound status value; line 5 is the key method.</p>
<figure class='code'><figcaption><span>Initialize the Download Updates Command </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='csharp'><span class='line'><span class="n">DownloadUpdates</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ReactiveAsyncCommand</span><span class="p">(</span><span class="k">null</span><span class="p">,</span> <span class="m">1</span><span class="p">);</span>
</span><span class='line'><span class="n">DownloadUpdates</span><span class="p">.</span><span class="n">RegisterAsyncAction</span><span class="p">(</span><span class="n">_</span> <span class="p">=></span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="c1">// Update to the latest version of the environment definitions</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="n">Updater</span><span class="p">.</span><span class="n">UpdateToVersion</span><span class="p">(</span><span class="n">Updater</span><span class="p">.</span><span class="n">LatestVersion</span><span class="p">))</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="n">Status</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Format</span><span class="p">(</span><span class="n">Properties</span><span class="p">.</span><span class="n">Resources</span><span class="p">.</span><span class="n">CurrentEnvironmentMessage</span><span class="p">,</span> <span class="n">Updater</span><span class="p">.</span><span class="n">LatestVersion</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="k">else</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="n">Status</span> <span class="p">=</span> <span class="n">Properties</span><span class="p">.</span><span class="n">Resources</span><span class="p">.</span><span class="n">EnvironmentUpdateErrorMsg</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="c1">// Finalize the state of the update process</span>
</span><span class='line'> <span class="n">UpdateState</span> <span class="p">=</span> <span class="n">UpdateState</span><span class="p">.</span><span class="n">Completed</span><span class="p">;</span>
</span><span class='line'> <span class="p">});</span>
</span></code></pre></td></tr></table></div></figure>
<p>I hope this has been a decent non-trivial example on how to use the ReactiveUI framework to build WPF view models; if it looks interesting have a look at the <a href="http://reactiveui.net/welcome/pdf">great docs</a> that have been synthesized from <a href="http://blog.paulbetts.org/">Paul Betts blog post examples</a> of different usages of the framework.</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2012/12/31/twitter-and-facebook-logins-via-omniauth-in-rails/">Fitbit Logins via Omniauth in Rails</a></h1>
<p class="meta">
<time datetime="2012-12-31T10:54:00-06:00" pubdate data-updated="true">Dec 31<span>st</span>, 2012</time>
| <a href="/blog/2012/12/31/twitter-and-facebook-logins-via-omniauth-in-rails/#disqus_thread"
data-disqus-identifier="http://whazzing.com/blog/2012/12/31/twitter-and-facebook-logins-via-omniauth-in-rails/">Comments</a>
</p>
</header>
<div class="entry-content"><p>It all started with a <a href="http://whazzing.com/blog/2012/03/20/becoming-the-finisher/#comment-743146618">comment</a> and a <a href="https://github.com/whazzmaster/fitgem-client/pull/4">pull request</a>.</p>
<p>I hadn’t put time into maintaining my <a href="http://github.com/whazzmaster/fitgem">fitbit gem</a> <a href="http://www.fitbitclient.com">reference app</a> in quite awhile, and when I got a pull request to update it to the latest rails version I at first hesitated. After talking with some folks at <a href="http://www.bendyworks.com">my coworking space</a>, however, I decided to use some holiday downtime to pull in the changes and see if I could build on them to improve the quality of the site.</p>
<p>At first I was horrified to learn that I’d stopped in mid-refactoring and I realized it was no wonder that Marcel (the submitter) reported he’d had trouble getting tests to run. The first step was to delete whole directories of files and tests that no longer conformed to the current design of the site and ensure that the files that remained all contributed to that design.</p>
<p>I grabbed Marcel’s pull request and got to work merging it in, at which point I looked at the login code and got embarrassed again. The truth is, the <a href="http://github.com/whazzmaster/fitgem">fitgem</a> library wasn’t really built for managing oauth logins; instead it is optimized for using a token/secret for a given user to fetch data from the API. Yes, you could use fitgem to facilitate an oauth login process, but it certainly wasn’t built with rails in mind and you had to roll your own controllers, views, and token and secret management.</p>
<p>The thing is, we already have a library that does that sort of thing: <a href="https://github.com/intridea/omniauth">OmniAuth</a> is a fantastic library for consuming <a href="https://github.com/intridea/omniauth/wiki/List-of-Strategies">pluggable login strategies</a>. I didn’t have to search long before I found a <a href="https://github.com/tkgospodinov/omniauth-fitbit">fitbit strategy for omniauth</a>, and I set to work integrating it into the reference app.</p>
<p>Luckily I had been working a lot with OmniAuth lately, as I’d implemented logins via Facebook and Twitter in another project just the week before. As always I leaned heavily on Ryan Bates’ excellent <a href="http://railscasts.com">Railscasts</a> in getting up to speed on how <a href="http://railscasts.com/episodes/235-devise-and-omniauth-revised">OmniAuth worked with Devise</a>, and specifically with Twitter and <a href="http://railscasts.com/episodes/360-facebook-authentication">Facebook</a>. <small-plug>Railscasts Pro ($9/month) has been totally worth it for me. The Pro episodes go into a lot of depth and I’ve learned a ton from them.</small-plug></p>
<p>Using the omniauth-fitbit gem I was able to delete a lot of now-redundant code for signing into/up for the application. There were, however, a few hiccups along the way that I wanted to note:</p>
<ul>