-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
1588 lines (1299 loc) · 125 KB
/
atom.xml
File metadata and controls
1588 lines (1299 loc) · 125 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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[whazzing around]]></title>
<link href="http://whazzing.com/atom.xml" rel="self"/>
<link href="http://whazzing.com/"/>
<updated>2013-12-01T17:11:12-06:00</updated>
<id>http://whazzing.com/</id>
<author>
<name><![CDATA[Zachery Moneypenny]]></name>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[Catching Up]]></title>
<link href="http://whazzing.com/blog/2013/12/01/catching-up/"/>
<updated>2013-12-01T08:45:00-06:00</updated>
<id>http://whazzing.com/blog/2013/12/01/catching-up</id>
<content type="html"><![CDATA[<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>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Automated ClickOnce Build and Deploy Using Powershell and MSBuild]]></title>
<link href="http://whazzing.com/blog/2013/04/11/automated-clickonce-build-and-deploy-using-powershell-and-msbuild/"/>
<updated>2013-04-11T12:14:00-05:00</updated>
<id>http://whazzing.com/blog/2013/04/11/automated-clickonce-build-and-deploy-using-powershell-and-msbuild</id>
<content type="html"><
</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>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Responsive WPF Applications With ReactiveUI]]></title>
<link href="http://whazzing.com/blog/2013/02/22/responsive-wpf-applications-with-reactiveui/"/>
<updated>2013-02-22T21:25:00-06:00</updated>
<id>http://whazzing.com/blog/2013/02/22/responsive-wpf-applications-with-reactiveui</id>
<content type="html"><![CDATA[<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>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Fitbit Logins via Omniauth in Rails]]></title>
<link href="http://whazzing.com/blog/2012/12/31/twitter-and-facebook-logins-via-omniauth-in-rails/"/>
<updated>2012-12-31T10:54:00-06:00</updated>
<id>http://whazzing.com/blog/2012/12/31/twitter-and-facebook-logins-via-omniauth-in-rails</id>
<content type="html"><![CDATA[<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>
<li>Unlike Twitter and Facebook, logging into Fitbit via omniauth-fitbit doesn’t allow devise to remember you from session to session, so you end having to re-login every time. I haven’t yet figured out why but it may be something about how Fitbit conducts OAuth logins or it may be a configuration issue with omniauth-fitbit.</li>
<li>Logging in via Fitbit is fine and dandy, but if you want to use the token/secret for the logged-in user to fetch data from the API through the fitgem interface you’ll need to store them. I just added fields to my user object:</li>
</ul>
<figure class='code'><figcaption><span>Add Oauth fields to User model</span><a href='https://github.com/whazzmaster/fitgem-client/blob/master/db/migrate/20121225193557_add_oauth_fields_to_users.rb'>_add_oauth_fields_to_users.rb </a></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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">class</span> <span class="nc">AddOauthFieldsToUsers</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span>
</span><span class='line'> <span class="k">def</span> <span class="nf">change</span>
</span><span class='line'> <span class="n">add_column</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">:oauth_token</span><span class="p">,</span> <span class="ss">:string</span>
</span><span class='line'> <span class="n">add_column</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">:oauth_secret</span><span class="p">,</span> <span class="ss">:string</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<ul>
<li>I made some, ahem, unorthodox decisions that will have to refactored later. Chief among them was to add the FitbitClient to the User model:</li>
</ul>
<figure class='code'><figcaption><span>Access to FitbitClient through User model</span><a href='https://github.com/whazzmaster/fitgem-client/blob/master/app/models/user.rb'>user.rb </a></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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
</span><span class='line'>
</span><span class='line'> <span class="c1"># Elided for conciseness</span>
</span><span class='line'>
</span><span class='line'> <span class="k">def</span> <span class="nf">linked?</span>
</span><span class='line'> <span class="n">oauth_token</span><span class="o">.</span><span class="n">present?</span> <span class="o">&&</span> <span class="n">oauth_secret</span><span class="o">.</span><span class="n">present?</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'> <span class="k">def</span> <span class="nf">fitbit_data</span>
</span><span class='line'> <span class="k">raise</span> <span class="s2">"Account is not linked with a Fitbit account"</span> <span class="k">unless</span> <span class="n">linked?</span>
</span><span class='line'> <span class="vi">@client</span> <span class="o">||=</span> <span class="no">Fitgem</span><span class="o">::</span><span class="no">Client</span><span class="o">.</span><span class="n">new</span><span class="p">(</span>
</span><span class='line'> <span class="ss">:consumer_key</span> <span class="o">=></span> <span class="no">ENV</span><span class="o">[</span><span class="s2">"FITBIT_CONSUMER_KEY"</span><span class="o">]</span><span class="p">,</span>
</span><span class='line'> <span class="ss">:consumer_secret</span> <span class="o">=></span> <span class="no">ENV</span><span class="o">[</span><span class="s2">"FITBIT_CONSUMER_SECRET"</span><span class="o">]</span><span class="p">,</span>
</span><span class='line'> <span class="ss">:token</span> <span class="o">=></span> <span class="n">oauth_token</span><span class="p">,</span>
</span><span class='line'> <span class="ss">:secret</span> <span class="o">=></span> <span class="n">oauth_secret</span><span class="p">,</span>
</span><span class='line'> <span class="ss">:user_id</span> <span class="o">=></span> <span class="n">uid</span>
</span><span class='line'> <span class="p">)</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'> <span class="k">def</span> <span class="nf">has_fitbit_data?</span>
</span><span class='line'> <span class="o">!</span><span class="vi">@client</span><span class="o">.</span><span class="n">nil?</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'> <span class="c1"># Elided for conciseness</span>
</span><span class='line'>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<p>Eventually I should use a better pattern for this, but for now it’s simplistic and anywhere you have a logged-in user in your application you will be able to call <code>fitbit_data</code> to get access to a configured FitbitClient instance.</p>
<p>And with that we’re logging in via Fitbit and using the token/secret returned via omniuath-fitbit to fuel data retrieval from the Fitbit API through fitgem.</p>
<h3>List of Resources</h3>
<ul>
<li><a href="https://github.com/intridea/omniauth">OmniAuth</a>: Base authentication library</li>
<li><a href="https://github.com/tkgospodinov/omniauth-fitbit">omniauth-fitbit</a>: Fitbit-specific pluggable OmniAuth strategy</li>
<li><a href="http://github.com/whazzmaster/fitgem">fitgem</a>: Fitbit API library</li>
<li>Fitgem Reference Application (<a href="http://fitbitclient.com">app</a>|<a href="https://github.com/whazzmaster/fitgem-client">code</a>): An example application using omniauth-fitbit and fitgem</li>
<li><a href="http://dev.fitbit.com">Fitbit.com Developer Site</a>: Manage your developer keys for use with omniauth-fitbit and fitgem</li>
<li><a href="http://railscasts.com/episodes/235-devise-and-omniauth-revised">OmniAuth and Devise Railscast</a>: Integrating OmniAuth, Devise, and Twitter</li>
<li><a href="http://railscasts.com/episodes/360-facebook-authentication">Facebook Authentication with OmniAuth Railscast</a>: Setting up OmniAuth to work with Facebook</li>
</ul>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[I Seem to Be Getting Along Fine Without Facebook]]></title>
<link href="http://whazzing.com/blog/2012/11/26/i-seem-to-be-getting-along-fine-without-facebook/"/>
<updated>2012-11-26T20:24:00-06:00</updated>
<id>http://whazzing.com/blog/2012/11/26/i-seem-to-be-getting-along-fine-without-facebook</id>
<content type="html"><![CDATA[<p>I’m trying <strong>really</strong> hard to not read Hacker News these days, as the contrarian argument-for-argument’s-sake nature of the comments tends to enrage me, but one of the more mystifying memes that seems to persist down in the Comments Dungeon is that the social contract of the United States of America requires you to be on Facebook.</p>
<p>In each thread where the topic appears there’s someone that says “I wish I didn’t have to have a Facebook account but unfortunately it’s required.” Someone responds and notes that no, <strong>it’s not required</strong>, and all they have to do is delete their account. That (sensible) response is then responded to a million times with the now-ubiquitous oath: to be a fully-fledged member of a social group in 2012 requires one to be on Facebook. And to that I restate the obvious: no, it really isn’t.</p>
<p>How many devices do you use on a daily basis? Phones, laptops, tablets, desktops, kiosks, and more. There exists a whole world outside of Facebook and all you have to do is grasp it. I prefer phone calls or coffee with friends over email but it’ll do in a pinch. I share my photos through Flickr and Twitter (depending on whether they came from my DSLR or iPhone). I blog at a skrillion different venues. People know where to find me online and IRL, and I’m confident that within my social circle I won’t be left off some invite just because I’m not on Facebook. My friends are my friends— we enjoy each others’ company! What I found when I deleted my Facebook account was that none of my friends really used it for anything meaningful. My wall was eternally filled with high school people I hadn’t seen/talked to/cared about in a decade, and extended family that I didn’t talk to much anyways.</p>
<p>In the last 5 years I’ve taken two significant (though very First-World-Problemy) steps: I got rid of my car, and I deleted my Facebook account. Interestingly enough <strong>at the time I did each of these things</strong> I was much more uncomfortable and worried about having no car than that I would miss some crucial social interaction by leaving Facebook.</p>
<p>When I left Facebook there was much more a sense of relief than anything else; <strong>I suggest you try it</strong>.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Madison Ruby 2012]]></title>
<link href="http://whazzing.com/blog/2012/08/27/madison-ruby-2012/"/>
<updated>2012-08-27T17:04:00-05:00</updated>
<id>http://whazzing.com/blog/2012/08/27/madison-ruby-2012</id>
<content type="html"><![CDATA[<p><em>Please note</em>: Madison Ruby photos are now online at <a href="http://www.flickr.com/photos/whazzmaster/sets/72157631263044942/">Flickr</a>.</p>
<p><a href="http://www.flickr.com/photos/whazzmaster/7869056820/"><img class="right" src="http://farm9.staticflickr.com/8282/7869056820_aacfe79e4b_n.jpg" width="320" height="213" title="Greg Tarnoff at Madison Ruby 2012" ></a></p>
<p>I’m working off the conference hangover. Email catchup, priotizing
tasks, etc. took up my time this morning, but I couldn’t let Madison’s
premier software conference fade without writing down some thoughts.</p>
<p>Firstly: much love to Jim and Jen Remsik as well as all the volunteers,
speakers, and organizers that helped make v2 of Madison Ruby even more
fun and entertaining than the inaugural conf last year. The diversity
of topics was again front and center: from team
dynamics, to talks about social justice, to funky drummers, to sources
of inspiration and the finer points of immigration law, the organizers
found a way to communicate technical content while advancing the
community in other areas as well.</p>
<p>First up was the <a href="http://madisonruby2012.sched.org/event/45828a0ad9ccd45642acbeeefabd9aa1">Design Eye for the Dev Guy or Gal</a> workshop on Thursday. I misunderstood the focus, which is mostly because I signed up for it before a comprehensive description had been published. <a href="https://twitter.com/pengwynn">Wynn Netherland</a> did a great job laying out the finer points of HTML5, Sass, Compass, and more. I had expected the workshop to range more towards the philosophy of web design, rather than a technical how-to around using the tools. Even so, however, I learned a lot about Compass. My previous aversion stemmed from when compass and blueprint were sold as a boxed set, but I’m definitely going to check it out on the next major project.</p>
<p>Highlight: In a discussion about the ubiquity of Twitter Bootstrap Wynn
asked how many folks were using Octopress for their dev blog, and then
added, “You know, you don’t HAVE to use the default style… you can add
some color in there.” <em>sheepish grin</em></p>
<p>I may have had a little too much fun at the Github-sponsored drinkup on
Thursday evening; waking up and riding my bike downtown at 7am was a
monumental task.</p>
<p>Friday’s talks were very interesting, especially the <a href="http://madisonruby2012.sched.org/event/b50ba5ffa1585b870253b928b153d74f"><strong>Anti-Opression 101</strong></a>
talk by Lindsey Bieda and Steve Klabnik. Though I had seen it a
previous <a href="http://www.meetup.com/Mad-Railers/">Mad-Railers meetup</a> I also enjoyed <a href="https://twitter.com/rathboma">Matthew Rathbone</a>’s talk
about how <a href="http://foursquare.com">Foursquare</a> uses <a href="http://hadoop.apache.org/">Hadoop</a> for its various data processing needs. Unfortunately, I had to take off early on Friday and missed some of the afternoon talks, including what I heard was a <a href="https://twitter.com/#!/search/?q=clyde+%23madisonruby&src=typd">fantastic 45 minutes</a> with <a href="http://en.wikipedia.org/wiki/Clyde_Stubblefield">Clyde Stubblefield</a>.</p>
<p>Saturday I was feeling a lot better, and hit the Farmer’s Market early
for coffee and pastries. I’m embarassed to say that I hadn’t looked
at the schedule closely enough and so it was an extremely pleasant surprise
that the first talk was by Paolo Perrotta, author of the best Ruby book
in existence: <a href="http://pragprog.com/book/ppmetr/metaprogramming-ruby">Metaprogramming Ruby</a>.
It was a great talk about ghosts, fake ghosts, and all manner of
potential problems one will encounter when using the metaprogramming
aspect of the Ruby language.</p>
<p>Later in the day were several successive talks that excited the hell out
of me, but none moreso than <a href="https://twitter.com/rubybuddha">Leon Gersing</a>’s talk
on the Weird in programming. Difficult to describe so I’ll just say
that if he’s speaking at a conference make sure you get your ass to that
talk and be prepared to enjoy it.</p>
<p>The <strong>Teaching Rails</strong> panel discussion late in the day on Saturday was
interesting. I almost didn’t attend because I thought it was going to
be about how best to teach Rails to new folks. Instead it was more
about the <em>business</em> of teaching Rails and how each of the four
panelists approached it. Ultimately the panel dovetailed into a talk
about certification, payscales, and other highly interesting topics to
any software developer. I felt that <a href="https://twitter.com/j3">Jeff Casimir</a>’s innocent
suggestion of some kind of certification was not considered for a single
second before being shouted down by everyone else on the panel and in
attendance. In particular, I think his idea has merit but the operational reality is
bad, which is why everyone just said “NO CERTIFICATION” when I think
what is needed is a
discussion about what certification is trying to accomplish and how it
can be done without turning into a paradise for grifters and dummies.</p>