<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-6790398825087211279</atom:id><lastBuildDate>Fri, 06 Sep 2024 20:37:47 +0000</lastBuildDate><category>FUTS</category><category>SAS</category><category>Unit testing</category><category>.NET</category><category>Cross-training</category><category>Design Patterns</category><category>FTP</category><category>Meditation</category><category>Programming</category><category>Ruby</category><category>SQL Server</category><category>SubSonic</category><category>TDD</category><category>Technology</category><category>Yoga</category><title>Technology ramblings</title><description>ABC, 123, Ruby, C#, SAS, SQL, TDD, VB.NET, XYZ</description><link>http://technoyoga.blogspot.com/</link><managingEditor>noreply@blogger.com (DavidR)</managingEditor><generator>Blogger</generator><openSearch:totalResults>17</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-3935345197315859836</guid><pubDate>Thu, 13 Dec 2007 16:57:00 +0000</pubDate><atom:updated>2007-12-13T09:06:00.107-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQL Server</category><title>SQL Server 2005 Non-Adoption</title><description>The &lt;a href=&quot;http://sqlblog.com/blogs/andrew_kelly/archive/2006/11/24/366.aspx&quot;&gt;tide may have finally turned&lt;/a&gt;, but I have some opinions why SQL Server 2005 was not initially embraced by a lot of IT shops.&lt;br /&gt;&lt;br /&gt;First of all, have you seen how well it integrates with Access 2003 for ADP development? It&#39;s phenomenal! (In the sense that it is non-functional.) Let&#39;s hope Access 2007 / SQL Server 2008 integration works.&lt;br /&gt;&lt;br /&gt;Second of all, I have a sneaking suspicion that there really &lt;em&gt;weren&#39;t &lt;/em&gt;a lot of devs out there dying to write C# or VB.NET stored procedures, and that this highly touted new feature was largely a gimmick imagined up by the big brains in Redmond and not really something that the practical developer would really use. T-SQL rocks after all! :)&lt;br /&gt;&lt;br /&gt;You have opinions one way or the other?&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/12/sql-server-2005-non-adoption.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-6220757611740624020</guid><pubDate>Sat, 01 Dec 2007 18:24:00 +0000</pubDate><atom:updated>2007-12-02T13:27:13.774-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">FUTS</category><category domain="http://www.blogger.com/atom/ns#">SAS</category><category domain="http://www.blogger.com/atom/ns#">TDD</category><title>Futzing with FUTS - Part III</title><description>&lt;p&gt;Let&#39;s dig a little deeper into using &lt;a href=&quot;http://www.thotwave.com/products/futs.jsp&quot;&gt;FUTS (Framework for Unit Testing SAS® programs)&lt;/a&gt;. &lt;/p&gt;We have 10,000 hospitalization records in a CSV file that looks like this. We want to do some basic analyses on this file: average age at admission, average length of stay and number of patient-days per doctor.&lt;br /&gt;&lt;p&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_12_01_hosp_data.GIF&quot; height=&quot;374&quot; width=&quot;528&quot; /&gt;&lt;/p&gt;In order to unit test the SAS code that will perform the analysis, we learned in &lt;a href=&quot;http://technoyoga.blogspot.com/2007/11/futzing-with-futs-part-i.html&quot;&gt;part 1&lt;/a&gt; &amp;amp; &lt;a href=&quot;http://technoyoga.blogspot.com/2007/11/futzing-with-futs-part-ii.html&quot;&gt;part 2&lt;/a&gt; in this series that testable code is written as macros. I&#39;ve written three relatively simple SAS macros to do the three calculations. (I&#39;m using &lt;a href=&quot;http://www.pauldickman.com/teaching/sas/age.php&quot;&gt;Billy Kreuter&#39;s age calculation code&lt;/a&gt;.) This file that contains the three macros is named c:\hosp\macros.sas.&lt;br /&gt;&lt;pre&gt;&lt;span style=&quot;color: rgb(0, 0, 255);&quot;&gt;%macro Age(agevar,bdatevar,indexdt);&lt;br /&gt; &amp;amp;agevar = floor((intck(&#39;month&#39;,&amp;amp;bdatevar,&amp;amp;indexdt) - (day(&amp;amp;indexdt) &lt; losvar =&quot; &amp;amp;dischargedtvar&quot;&gt;&lt;/pre&gt;&lt;br /&gt;The Age and LOS macros calculate new variables and are to be called inside a data step, whereas the DocPatDays macro creates a new summary dataset, one record per DoctorID.&lt;br /&gt;&lt;p&gt;Before writing the production SAS program, let&#39;s test the macros with unit tests that utilize the FUTS macros. The test data for testing Age and LOS is called test_hosps.CSV and looks like this. It&#39;s basically a sample of the main hospitalizations CSV file plus EXPECTED_AGE and EXPECTED_LOS, calculated by hand.&lt;/p&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_12_01_test_hosp.GIF&quot; height=&quot;303&quot; width=&quot;664&quot; /&gt;&lt;br /&gt;&lt;p&gt;The unit test code for Age looks like this. (We&#39;re importing the test_hosps.CSV data, calculated Age using the production macro and then comparing the EXPECTED_AGE with the age calculated by the macro.)&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;span style=&quot;color: rgb(0, 0, 255);&quot;&gt;options mprint;&lt;br /&gt;%include &#39;c:\hosp\macros.sas&#39;;&lt;br /&gt;proc import datafile=&#39;test_hosps.csv&#39; out=TestData dbms=csv replace;&lt;br /&gt;getnames=yes;&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;data actual;&lt;br /&gt;set TestData (drop=EXPECTED_AGE);&lt;br /&gt;%Age(AGE,BDATE,ADMITDATE);&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;data expected;&lt;br /&gt;set TestData (rename=(EXPECTED_AGE=AGE));&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;%assert_compare_equal(base=expected,compare=actual);&lt;/span&gt;&lt;/pre&gt;On the first run of the unit test program (utAge.sas) we get an error on the assert.&lt;br /&gt;&lt;pre&gt;&lt;span style=&quot;color: rgb(0, 0, 255);&quot;&gt;68         %assert_compare_equal(base=expected,compare=actual);&lt;br /&gt;MPRINT(ASSERT_COMPARE_EQUAL):   proc compare base=expected compare=actual;&lt;br /&gt;MPRINT(ASSERT_COMPARE_EQUAL):  ;&lt;br /&gt;MPRINT(ASSERT_COMPARE_EQUAL):   run;&lt;br /&gt;&lt;br /&gt;NOTE: There were 10 observations read from the data set WORK.EXPECTED.&lt;br /&gt;NOTE: There were 10 observations read from the data set WORK.ACTUAL.&lt;br /&gt;NOTE: The PROCEDURE COMPARE printed page 1.&lt;br /&gt;NOTE: PROCEDURE COMPARE used (Total process time):&lt;br /&gt;real time           0.45 seconds&lt;br /&gt;cpu time            0.03 seconds&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;MPRINT(GENERATE_EVENT):   options linesize=max;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: rgb(255, 0, 0);&quot;&gt;ERROR: Data set actual not equal to expected&lt;/span&gt;&lt;/pre&gt;Looking at the .lst file reveals what is going on....proc import is attaching the BEST12 format and informat to the expected age variable (not present on the calculated age variable). They key thing is that all observations with all compared variables are equal, but there&#39;s still that annoying error in the log.&lt;br /&gt;&lt;pre&gt;&lt;span style=&quot;color: rgb(0, 0, 255);&quot;&gt;&lt;br /&gt;                               The COMPARE Procedure&lt;br /&gt;                    Comparison of WORK.EXPECTED with WORK.ACTUAL&lt;br /&gt;                                   (Method=EXACT)&lt;br /&gt;&lt;br /&gt;                                 Data Set Summary&lt;br /&gt;&lt;br /&gt;          Dataset                 Created          Modified  NVar    NObs&lt;br /&gt;&lt;br /&gt;          WORK.EXPECTED  26NOV07:19:37:43  26NOV07:19:37:43     7      10&lt;br /&gt;          WORK.ACTUAL    26NOV07:19:37:43  26NOV07:19:37:43     7      10&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;                                 Variables Summary&lt;br /&gt;&lt;br /&gt;                 Number of Variables in Common: 7.&lt;br /&gt;                 Number of Variables with Differing Attributes: 1.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;               Listing of Common Variables with Differing Attributes&lt;br /&gt;&lt;br /&gt;              Variable  Dataset        Type  Length  Format   Informat&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: rgb(255, 0, 0);&quot;&gt;                    AGE       WORK.EXPECTED  Num        8  BEST12.  BEST32.&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 0, 255);&quot;&gt;&lt;br /&gt;                        WORK.ACTUAL    Num        8&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;                                Observation Summary&lt;br /&gt;&lt;br /&gt;                           Observation      Base  Compare&lt;br /&gt;&lt;br /&gt;                           First Obs           1        1&lt;br /&gt;                           Last  Obs          10       10&lt;br /&gt;&lt;br /&gt;          Number of Observations in Common: 10.&lt;br /&gt;          Total Number of Observations Read from WORK.EXPECTED: 10.&lt;br /&gt;          Total Number of Observations Read from WORK.ACTUAL: 10.&lt;br /&gt;&lt;br /&gt;          Number of Observations with Some Compared Variables Unequal: 0.&lt;br /&gt;          Number of Observations with All Compared Variables Equal: 10.&lt;br /&gt;&lt;br /&gt;          NOTE: No unequal values were found. All values compared are exactly equal.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;   To take care of that bothersome error caused by a simple difference is irrelevant variable attributes, the utAge.sas unit test SAS program gets updated to this.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style=&quot;color: rgb(0, 0, 255);&quot;&gt;options mprint;&lt;br /&gt;%include &#39;c:\hosp\macros.sas&#39;;&lt;br /&gt;proc import datafile=&#39;test_hosps.csv&#39; out=TestData dbms=csv replace;&lt;br /&gt;getnames=yes;&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;data actual;&lt;br /&gt;set TestData (drop=EXPECTED_AGE);&lt;br /&gt;%Age(AGE,BDATE,ADMITDATE);&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;data expected;&lt;br /&gt;set TestData (rename=(EXPECTED_AGE=AGE));&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: rgb(255, 0, 0);&quot;&gt;format age;  informat age;&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 0, 255);&quot;&gt; *Takes away formating/informating;&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;%assert_compare_equal(base=expected,compare=actual);&lt;/span&gt;&lt;/pre&gt;The error goes away! Our unit test for the Age macro passes! :)&lt;br /&gt;&lt;br /&gt;The unit test code for LOS looks very similar to the Age unit test code.&lt;br /&gt;&lt;pre&gt;&lt;span style=&quot;color: rgb(0, 0, 255);&quot;&gt;options mprint;&lt;br /&gt;%include &#39;C:\hosp\macros.sas&#39;;&lt;br /&gt;proc import datafile=&#39;test_hosps.csv&#39; out=TestData dbms=csv replace;&lt;br /&gt;getnames=yes;&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;data actual;&lt;br /&gt;set TestData (drop=EXPECTED_LOS);&lt;br /&gt;%LOS(LOS,ADMITDATE,DISCHARGEDATE);&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;data expected;&lt;br /&gt;set TestData (rename=(EXPECTED_LOS=LOS));&lt;br /&gt;format LOS;  informat LOS; *Takes away formating/informating;&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;%assert_compare_equal(base=expected,compare=actual);&lt;/span&gt;&lt;/pre&gt;We need another test data file for the PatDocDays unit testing. It is hand-calculated doctor-level summary data and will supply the expected values when processing the test_hosps.CSV with the PatDocDays macro.&lt;br /&gt;&lt;p&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_12_01_test_pt_days.GIF&quot; height=&quot;267&quot; width=&quot;245&quot; /&gt;&lt;/p&gt;The utDocPatDays.sas unit test code looks like this.&lt;br /&gt;&lt;pre&gt;&lt;span style=&quot;color: rgb(0, 0, 255);&quot;&gt;options mprint;&lt;br /&gt;%include &#39;C:\hosp\macros.sas&#39;;&lt;br /&gt;proc import datafile=&#39;test_hosps.csv&#39; out=InputData dbms=csv replace;&lt;br /&gt;getnames=yes;&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;data InputData;&lt;br /&gt;set InputData;&lt;br /&gt;%LOS(LOS,AdmitDate,DischargeDate);&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;proc import datafile=&#39;test_patient_days.csv&#39; out=Expected dbms=csv replace;&lt;br /&gt;getnames=yes;&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;data Expected;&lt;br /&gt;set Expected (rename=(EXPECTED_PT_DAYS=PT_DAYS));&lt;br /&gt;format PT_DAYS;  informat PT_DAYS; *Takes away formating/informating;&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;%DocPatDays(actual,InputData,PT_DAYS,DOCTORID,LOS);&lt;br /&gt;&lt;br /&gt;data actual;&lt;br /&gt;set actual (keep=DOCTORID PT_DAYS);&lt;br /&gt;format PT_DAYS;  informat PT_DAYS;&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;%assert_compare_equal(base=expected,compare=actual);&lt;/span&gt;&lt;/pre&gt;And finally, the production code looks like this.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style=&quot;color: rgb(0, 0, 255);&quot;&gt;options mprint;&lt;br /&gt;%include &#39;C:\hosp\macros.sas&#39;;&lt;br /&gt;proc import datafile=&#39;hospitalizations.csv&#39; out=HospitalData dbms=csv replace;&lt;br /&gt;getnames=yes;&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;*Calculate age at hospital admission and length of stay;&lt;br /&gt;data HospitalData2;&lt;br /&gt;set HospitalData;&lt;br /&gt;%Age(AgeAtAdmit,BDate,AdmitDate);&lt;br /&gt;%LOS(LOS,AdmitDate,DischargeDate);&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;proc means data=HospitalData2;&lt;br /&gt;var AgeAtAdmit LOS;&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;*Calculate patient-days per doctor;&lt;br /&gt;%DocPatDays(PatDays,HospitalData2,PatDays,DoctorID,LOS);&lt;br /&gt;&lt;br /&gt;proc means data=PatDays maxdec=2;&lt;br /&gt;var PatDays;&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;proc print data=PatDays;&lt;br /&gt;var DoctorID PatDays;&lt;br /&gt;run;&lt;/span&gt;&lt;/pre&gt;The output looks like this.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style=&quot;color: rgb(0, 0, 255);&quot;&gt;&lt;br /&gt;                                The MEANS Procedure&lt;br /&gt;&lt;br /&gt;Variable          N            Mean         Std Dev         Minimum         Maximum&lt;br /&gt;&lt;br /&gt;AgeAtAdmit    10000      46.2669000      23.1170955       6.0000000      86.0000000&lt;br /&gt;LOS           10000       8.0142000       3.7280810       2.0000000      14.0000000&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;                                The MEANS Procedure&lt;br /&gt;&lt;br /&gt;                            Analysis Variable : PatDays&lt;br /&gt;&lt;br /&gt;          N            Mean         Std Dev         Minimum         Maximum&lt;br /&gt;&lt;br /&gt;        100          801.42           95.13          609.00         1049.00&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;                                                    Pat&lt;br /&gt;                            Obs        DOCTORID    Days&lt;br /&gt;&lt;br /&gt;                              1             100     744&lt;br /&gt;                              2             101     732&lt;br /&gt;                              3             102     840&lt;br /&gt;                              4             103    1035&lt;br /&gt;                              5             104     879&lt;br /&gt;                              6             105     907&lt;br /&gt;                              7             106     752&lt;br /&gt;                              8             107     771&lt;br /&gt;                              9             108     880&lt;br /&gt;                             10             109     901&lt;br /&gt;                             11             110     775&lt;br /&gt;                             12             111     910&lt;br /&gt;                             13             112     799&lt;br /&gt;                             14             113     849&lt;br /&gt;                             15             114     774&lt;br /&gt;                             16             115     753&lt;br /&gt;                             17             116     845&lt;br /&gt;                             18             117     726&lt;br /&gt;                             19             118     690&lt;br /&gt;                             20             119     856&lt;br /&gt;                             21             120     675&lt;br /&gt;                             22             121     747&lt;br /&gt;                             23             122     727&lt;br /&gt;                             24             123     692&lt;br /&gt;                             25             124     992&lt;br /&gt;                             26             125     755&lt;br /&gt;                             27             126     782&lt;br /&gt;                             28             127     954&lt;br /&gt;                             29             128     773&lt;br /&gt;                             30             129     926&lt;br /&gt;                             31             130     790&lt;br /&gt;                             32             131     727&lt;br /&gt;                             33             132     702&lt;br /&gt;                             34             133     922&lt;br /&gt;                             35             134     843&lt;br /&gt;                             36             135     835&lt;br /&gt;                             37             136     755&lt;br /&gt;                             38             137     625&lt;br /&gt;                             39             138     687&lt;br /&gt;                             40             139     728&lt;br /&gt;                             41             140     805&lt;br /&gt;                             42             141     687&lt;br /&gt;                             43             142     669&lt;br /&gt;                             44             143     651&lt;br /&gt;                             45             144    1010&lt;br /&gt;                             46             145     909&lt;br /&gt;                             47             146     950&lt;br /&gt;                             48             147     805&lt;br /&gt;                             49             148     920&lt;br /&gt;                             50             149     679&lt;br /&gt;                             51             150     777&lt;br /&gt;                             52             151     756&lt;br /&gt;                             53             152     837&lt;br /&gt;                             54             153     798&lt;br /&gt;                             55             154     832&lt;br /&gt;                             56             155     893&lt;br /&gt;                             57             156     684&lt;br /&gt;                             58             157     754&lt;br /&gt;                             59             158     808&lt;br /&gt;                             60             159     788&lt;br /&gt;                             61             160     793&lt;br /&gt;                             62             161     816&lt;br /&gt;                             63             162     752&lt;br /&gt;                             64             163    1049&lt;br /&gt;                             65             164     712&lt;br /&gt;                             66             165     778&lt;br /&gt;                             67             166     880&lt;br /&gt;                             68             167     945&lt;br /&gt;                             69             168     866&lt;br /&gt;                             70             169     713&lt;br /&gt;                             71             170     842&lt;br /&gt;                             72             171     807&lt;br /&gt;                             73             172     854&lt;br /&gt;                             74             173     609&lt;br /&gt;                             75             174     698&lt;br /&gt;                             76             175     712&lt;br /&gt;                             77             176     704&lt;br /&gt;                             78             177     944&lt;br /&gt;                             79             178     696&lt;br /&gt;                             80             179     754&lt;br /&gt;                             81             180     811&lt;br /&gt;                             82             181     803&lt;br /&gt;                             83             182     771&lt;br /&gt;                             84             183     798&lt;br /&gt;                             85             184     766&lt;br /&gt;                             86             185     872&lt;br /&gt;                             87             186     623&lt;br /&gt;                             88             187     755&lt;br /&gt;                             89             188     759&lt;br /&gt;                             90             189     795&lt;br /&gt;                             91             190     788&lt;br /&gt;                             92             191     706&lt;br /&gt;                             93             192     959&lt;br /&gt;                             94             193     785&lt;br /&gt;                             95             194     823&lt;br /&gt;                             96             195    1027&lt;br /&gt;                             97             196     903&lt;br /&gt;                             98             197     751&lt;br /&gt;                             99             198     765&lt;br /&gt;                            100             199     891&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;FYI: I generated the hospitalizations.CSV dataset with this C# program.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_12_01_csharp_program.GIF&quot; alt=&quot;C# data generation program&quot;&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/12/futzing-with-futs-part-iii.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-3390390074493671390</guid><pubDate>Thu, 22 Nov 2007 21:52:00 +0000</pubDate><atom:updated>2007-11-22T13:59:11.531-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Cross-training</category><title>The Value of Cross-Training</title><description>&lt;p&gt;It was back when &lt;a href=&quot;http://en.wikipedia.org/wiki/Java_1.1#JDK_1.1_.2819_February_1997.29&quot;&gt;Java 1.1&lt;/a&gt; was a hot ticket item that I decided to tinker with this cool, new language.  I learned the basics and made a number of fun applets.  One silly one I recall involved a cat chasing a mouse around on a rectangular surface.  The hardware mouse moved the software mouse and the cat head followed the mouse wherever it went automatically.  At the time I was in a primarily Microsoft shop, so I never got to use my Java knowledge at work, but when &lt;a href=&quot;http://en.wikipedia.org/wiki/C_Sharp&quot;&gt;C#&lt;/a&gt; came out, it was very easy to pick up for me due to my Java experience.&lt;/p&gt;A while back I also learned a fair bit about &lt;a href=&quot;http://en.wikipedia.org/wiki/Adobe_Flash&quot;&gt;writing Flash applications using Macromedia Flash MX&lt;/a&gt;.  I wonder how much this is going to help when I dig into &lt;a href=&quot;http://www.microsoft.com/silverlight/&quot;&gt;Silverlight&lt;/a&gt; one of these days.&lt;br /&gt;&lt;br /&gt;More recently, I spent a weekend studying &lt;a href=&quot;http://www.rubyonrails.org/&quot;&gt;Ruby on Rails&lt;/a&gt; and then BAM! - a few months later &lt;a href=&quot;http://weblogs.asp.net/scottgu/archive/2007/10/14/asp-net-mvc-framework.aspx&quot;&gt;Microsoft&#39;s ASP.NET MVC Framework&lt;/a&gt; becomes a hot topic.  Due to my experience with RoR, understanding the architecture and coding of MS MVC has been very easy.  I may never code a production RoR app, but by studying it, I&#39;ve given myself a valuable education.&lt;br /&gt;&lt;br /&gt;Cynics may grumble about &quot;M$ copying others&quot; (I&#39;m won&#39;t touch that one!), but there have definitely been other instances of helpful cross-training not involving Microsoft products.  An obvious example is all that IBM Mainframe &lt;a href=&quot;http://en.wikipedia.org/wiki/Job_Control_Language&quot;&gt;JCL&lt;/a&gt; I got to write a decade or so ago...that prepared me quite well for…for…for, well, um, Soap Headers!  (And punch cards prepared me for &lt;a href=&quot;http://www.randomhouse.com/catalog/display.pperl?isbn=9781400051212&amp;amp;view=excerpt&quot;&gt;voting in Palm Beach county.&lt;/a&gt;)  LOL.  Ok, maybe that&#39;s a bit of a stretch, but I am certain that my work with &lt;a href=&quot;http://en.wikipedia.org/wiki/Perl&quot;&gt;Perl&lt;/a&gt; helped with understanding some &lt;a href=&quot;http://poignantguide.net/ruby/&quot;&gt;Ruby&lt;/a&gt; syntax and of course when SAS started supporting regular expressions, the experience with Perl regex paid off.  And my explorations into the &lt;a href=&quot;http://www.fresher.com/&quot;&gt;Matisse object-oriented database&lt;/a&gt; definitely helped grokking &lt;a href=&quot;http://www.hibernate.org/343.html&quot;&gt;NNHibernate&lt;/a&gt; easier.  Procedural programming in QBASIC certainly prepped me for classic VB.  And let&#39;s not forget BASIC with its fun and frequent GOTO statements and the not infrequent use of same in VB/VBA error handling.  On Error Goto Err_Handler.  Good times.&lt;br /&gt;&lt;br /&gt;My point is that when the urge strikes you to explore that language you&#39;ve been curious about, &lt;b&gt;go for it&lt;/b&gt;.  It might just turn out to be the next cool thing or cross-train you for it.&lt;br /&gt;&lt;br /&gt;Changing gears a bit, this reminds of the cross-training encouraged by the &lt;a href=&quot;http://www.myilp.com/&quot;&gt;Integral Life Practice starter kit&lt;/a&gt;.  By exercising your body, mind, spirit, and shadow (unconscious) in different ways, each realm of human experience is enhanced.  For example, the benefits of meditation (spirit) extend well beyond the spiritual realm, into the physical (a more relaxed state), mental (more clarity), and shadow (unconscious material is more accessible to productive processing).&lt;br /&gt;&lt;p&gt;Happy Thanksgiving! :)&lt;/p&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/11/value-of-cross-training.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-3153762394029798006</guid><pubDate>Thu, 22 Nov 2007 03:56:00 +0000</pubDate><atom:updated>2007-11-21T20:16:06.204-08:00</atom:updated><title>Visual Studio.NET 2008 Arrives</title><description>&lt;p&gt;No more beta blues! &lt;a href=&quot;http://msdn2.microsoft.com/en-us/vstudio/default.aspx&quot;&gt;Visual Studio.NET 2008 has been released to manufacturing!&lt;/a&gt;&lt;/p&gt;90-day trial editions are available right now &lt;a href=&quot;http://msdn2.microsoft.com/en-us/vstudio/products/aa700831.aspx&quot;&gt;at this location on MSDN&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;&lt;a href=&quot;http://www.microsoft.com/express/download/default.aspx&quot;&gt;Here is the express products web install page&lt;/a&gt; for &lt;b&gt;Visual Basic &lt;/b&gt;2008 Express Edition, &lt;b&gt;Visual C# &lt;/b&gt;2008 Express Edition, &lt;b&gt;Visual C++ &lt;/b&gt;2008 Express Edition, &amp;amp; &lt;b&gt;Visual Web Developer &lt;/b&gt;2008 Express Edition. Free to download immediately.  (You&#39;ll want a fast connection.)&lt;/p&gt;&lt;a href=&quot;http://blogs.msdn.com/dougste/archive/2007/11/02/new-features-of-visual-studio-2008-and-microsoft-net-framework-3-5.aspx&quot;&gt;Here&#39;s a nice blog post&lt;/a&gt; about new features courtesy of Doug Stewart.&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/11/visual-studionet-2008-arrives.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-6195995567978145057</guid><pubDate>Sun, 18 Nov 2007 18:55:00 +0000</pubDate><atom:updated>2007-11-18T10:58:37.818-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">FUTS</category><category domain="http://www.blogger.com/atom/ns#">SAS</category><category domain="http://www.blogger.com/atom/ns#">Unit testing</category><title>Futzing with FUTS - Part II</title><description>&lt;p&gt;In this second look at &lt;a href=&quot;http://www.thotwave.com/products/futs.jsp&quot;&gt;FUTS (Framework for Unit Testing SAS® programs)&lt;/a&gt;, I will walk through an example of how to convert a regular piece of SAS code into a testable piece of SAS code with a unit test for testing it.  The example consists of proc sql code to calculate age group based on age at an index date.&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;proc sql;&lt;br /&gt;  create table Calc as&lt;br /&gt;      select *, (case&lt;br /&gt;                 when int((IndexDt-BDate)/365.25) between 0 and 17 then &#39;A: 0-17&#39;&lt;br /&gt;                 when int((IndexDt-BDate)/365.25) between 18 and 34 then &#39;B: 18-34&#39;&lt;br /&gt;                 when int((IndexDt-BDate)/365.25) between 35 and 49 then &#39;C: 35-49&#39;&lt;br /&gt;                 when int((IndexDt-BDate)/365.25) between 50 and 64 then &#39;D: 50-64&#39;&lt;br /&gt;                 when int((IndexDt-BDate)/365.25) &gt; 64 then &#39;E: 65+&#39;&lt;br /&gt;                 else &#39;?&#39; end) as AgeGroup&lt;br /&gt;      from sample;&lt;br /&gt;quit;&lt;/pre&gt;As I mentioned in &lt;a href=&quot;http://technoyoga.blogspot.com/2007/11/futzing-with-futs-part-i.html&quot;&gt;Part I&lt;/a&gt;, the trick to making this code testable is to place it in a macro.  Editing this code to fit in a macro results in something like this.  The macro creates the calculated age group variable in an output dataset (outfile) given an input dataset (infile) containing a birthdate variable (bdatevar) and index date variable (indexdtvar).&lt;br /&gt;&lt;pre&gt;%macro CalcAgeGroup(outfile,infile,bdatevar,indexdtvar);&lt;br /&gt;proc sql;&lt;br /&gt;  create table &amp;amp;outfile as&lt;br /&gt;      select *, (case&lt;br /&gt;                 when int((&amp;amp;indexdtvar-&amp;amp;bdatevar)/365.25) between 0 and 17 then &#39;A: 0-17&#39;&lt;br /&gt;                 when int((&amp;amp;indexdtvar-&amp;amp;bdatevar)/365.25) between 18 and 34 then &#39;B: 18-34&#39;&lt;br /&gt;                 when int((&amp;amp;indexdtvar-&amp;amp;bdatevar)/365.25) between 35 and 49 then &#39;C: 35-49&#39;&lt;br /&gt;                 when int((&amp;amp;indexdtvar-&amp;amp;bdatevar)/365.25) between 50 and 64 then &#39;D: 50-64&#39;&lt;br /&gt;                 when int((&amp;amp;indexdtvar-&amp;amp;bdatevar)/365.25) &gt; 64 then &#39;E: 65+&#39;&lt;br /&gt;                 else &#39;?&#39; end) as AgeGroup&lt;br /&gt;      from &amp;infile;&lt;br /&gt;quit;&lt;br /&gt;%mend CalcAgeGroup;&lt;/pre&gt;&lt;br /&gt;The production code that calls this macro to achieve the same goal as the first bit of code looks like this.  A simple one liner.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;%CalcAgeGroup(Calc,sample,BDate,IndexDt);&lt;/pre&gt;&lt;br /&gt;To unit test the macro, we need at least a minimal set of test data like this CSV.  In this sample data file, you can see there&#39;s an ID var, BDATE, INDEXDT, and EXPECTED_AGEGROUP based on a calculation done by hand.&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_11_18_test_data_csv.GIF&quot; alt=&quot;Test Data CSV&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Given the CSV, the unit test code looks like this.  (The macro is assume to be saved in a file called CalcAgeGroup.sas.)&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;*Import the CSV file into a SAS dataset called TestData;&lt;br /&gt;proc import datafile=&#39;test_data.csv&#39; out=TestData dbms=csv replace; getnames=yes; run;&lt;br /&gt;&lt;br /&gt;data input; *Prep input dataset;&lt;br /&gt;  set TestData (drop=Expected_AgeGroup);&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;data expected; *Prep expected output dataset;&lt;br /&gt;  length AgeGroup $8.; *Make AgeGroup the right size;&lt;br /&gt;  set TestData (rename=(Expected_AgeGroup=AgeGroup));&lt;br /&gt;  format AgeGroup;  informat AgeGroup; *Drop format/informat;&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;%include &#39;CalcAgeGroup.sas&#39;;&lt;br /&gt;%CalcAgeGroup(Calc,input,bdate,indexdt);  *Run test data through macro;&lt;br /&gt;&lt;br /&gt;%assert_compare_equal(base=expected,compare=Calc); *Compare macro output &amp;amp; expected output;&lt;/pre&gt;&lt;br /&gt;Here we see the use of the FUTS macro &lt;b&gt;%assert_compare_equal&lt;/b&gt; which compares SAS two datasets (attributes and data values) and throws an error into the log if the base and compare files are at all different.  The rest of the code is fairly self-explanatory and involves preparing an input dataset for the macro and an expected output file to compare with the actual output file.&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/11/futzing-with-futs-part-ii.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-6076920052124033313</guid><pubDate>Sat, 17 Nov 2007 22:29:00 +0000</pubDate><atom:updated>2007-11-17T14:30:27.058-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Design Patterns</category><title>Design Pattern Resources</title><description>&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://www.dofactory.com/Patterns/Patterns.aspx&quot;&gt;data &amp;amp; object factory&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://msdn.microsoft.com/msdnmag/issues/01/07/patterns/&quot;&gt;Design Patterns: Solidify Your C# Application Architecture with Design Patterns&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://msdn.microsoft.com/msdnmag/issues/05/07/DesignPatterns/default.aspx&quot;&gt;Discover the Design Patterns You&#39;re Already Using in the .NET Framework&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;CodeProject&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://www.codeproject.com/cs/design/csdespat_1.asp&quot;&gt;Illustrated GOF Design Patterns in C# Part I: Creational&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://www.codeproject.com/cs/design/csdespat_2.asp&quot;&gt;Illustrated GOF Design Patterns in C# Part II: Structural I&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://www.codeproject.com/cs/design/csdespat_3.asp&quot;&gt;Illustrated GOF Design Patterns in C# Part III: Structural II&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://www.codeproject.com/cs/design/csdespat_4.asp&quot;&gt;Illustrated GOF Design Patterns in C# Part IV: Behavioral I&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://www.codeproject.com/cs/design/csdespat_5.asp&quot;&gt;Illustrated GOF Design Patterns in C# Part V: Behavioral II&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://www.codeproject.com/cs/design/csdespat_6.asp&quot;&gt;Illustrated GOF Design Patterns in C# Part VI: Behavioral III&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://rubygof.googlecode.com/svn/trunk/&quot;&gt;Implementation of the GoF patterns in ruby&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/11/design-pattern-resources.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-4066749981787090806</guid><pubDate>Sat, 17 Nov 2007 05:52:00 +0000</pubDate><atom:updated>2007-11-16T22:11:21.148-08:00</atom:updated><title>The Other Mother of Invention: Impatience</title><description>&lt;p&gt;On 11/8, I blogged about &lt;a href=&quot;http://technoyoga.blogspot.com/2007/11/unit-testing-email-method.html&quot;&gt;Unit Testing an Email Method&lt;/a&gt;.  Things have happened since then...&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_11_16_badsleep.GIF&quot; alt=&quot;Bad Sleep&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;You may have heard the saying about necessity or laziness being the mother of invention, but I say impatience is a good mother, too. I say this based on the fact that I quickly got sick of waiting for my email unit tests to run when I was running the whole batch of unit tests in NUnit. Pretty soon I had them [Ignore]&#39;d out. I was distressed about the yellow instead of the green, but my impatience was stronger. One property of a good unit test is that it runs rapidly.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Eventually (doh!) it dawned on me that I could both have &lt;i&gt;and&lt;/i&gt; eat my cake. How? I modified my email unit test to query my Outlook Inbox every two seconds to see if the target email had arrived and exit early if so (with success). Otherwise, it would sleep for two more seconds and check the inbox again -- up to a maximum of eight repetitions (i.e., 16 seconds).&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This refactoring made me quite the happy camper. Imagine my joy as I ran &lt;i&gt;all&lt;/i&gt; of my unit tests &lt;i&gt;quickly&lt;/i&gt; without the manditory 10-second waits for the tests involving email. I, like many NUnit users I am sure, just love to see that green bar -- and as soon as possible!&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I took all of the Outlook-related goo and put it in a separate class called OutlookInbox with a single public (static) method called EmailReceived. I just pass in the subject line of the email I am expecting to receive and the method works its magic and I get back a boolean value telling me if the email arrived in time or not. The responsibility for doing the 2-second chunks of waiting is delegated to this method and the work of actually interfacing with Outlook is delegated to a helper method called checkInbox.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;using System;&lt;br /&gt;using System.Runtime.InteropServices;&lt;br /&gt;using Outlook = Microsoft.Office.Interop.Outlook;&lt;br /&gt;&lt;br /&gt;namespace TestHarness&lt;br /&gt;{&lt;br /&gt;    public class OutlookInbox&lt;br /&gt;    {&lt;br /&gt;        private OutlookInbox() { }&lt;br /&gt;                                           &lt;br /&gt;        public static bool EmailReceived(string subjectline)&lt;br /&gt;        {&lt;br /&gt;            const int MAXTRIES = 8;&lt;br /&gt;            int tries = 0;&lt;br /&gt;            bool emailReceived = false;&lt;br /&gt;            do&lt;br /&gt;            {&lt;br /&gt;                tries++;&lt;br /&gt;                System.Threading.Thread.Sleep(2000);&lt;br /&gt;                emailReceived = checkInbox(subjectline);&lt;br /&gt;            } while (emailReceived == false &amp;&amp; tries &lt; MAXTRIES);&lt;br /&gt;            return emailReceived;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private static bool checkInbox(string subjectline)&lt;br /&gt;        {&lt;br /&gt;            //This webpage (http://www.developerfusion.co.uk/show/4667/) was helpful in &lt;br /&gt;            //developing this code.&lt;br /&gt;            Outlook.ApplicationClass outlookApp = null;&lt;br /&gt;            Outlook.NameSpace outlookNS = null;&lt;br /&gt;            bool emailArrived = false;&lt;br /&gt;            try&lt;br /&gt;            {&lt;br /&gt;                outlookApp = new Outlook.ApplicationClass();&lt;br /&gt;                outlookNS = outlookApp.GetNamespace(&quot;MAPI&quot;);&lt;br /&gt;                outlookNS.Session.Logon(&quot;outlook&quot;, &quot;&quot;, false, true);&lt;br /&gt;                Outlook.MAPIFolder inbox = outlookNS.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);&lt;br /&gt;                foreach (Outlook.MailItem email in inbox.Items)&lt;br /&gt;                {&lt;br /&gt;                    if (email.Subject == subjectline)&lt;br /&gt;                    {&lt;br /&gt;                        emailArrived = true;&lt;br /&gt;                        email.Delete();&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            finally&lt;br /&gt;            {&lt;br /&gt;                if (outlookNS != null)&lt;br /&gt;                    outlookNS.Logoff();&lt;br /&gt;            }&lt;br /&gt;            return emailArrived;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Now my unit test code looks like this.  This is testing a web service that sends email.  The business about ServiceConnections.WebServiceBaseUrl() manages whether we are pointing at  the dev or prod service.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;using System;&lt;br /&gt;using NUnit.Framework;&lt;br /&gt;using IndependentContractorApp.BusinessLayer;&lt;br /&gt;&lt;br /&gt;namespace TestHarness&lt;br /&gt;{&lt;br /&gt;   [TestFixture]&lt;br /&gt;   public class EmailServiceTestSuite&lt;br /&gt;   {&lt;br /&gt;       [Test]&lt;br /&gt;       public void SendRegularText()&lt;br /&gt;       {&lt;br /&gt;           string asmx = &quot;EmailService.asmx&quot;;&lt;br /&gt;           string url = ServiceConnections.WebServiceBaseUrl() + asmx;&lt;br /&gt;           EmailWebService.EmailService svc = new EmailWebService.EmailService();&lt;br /&gt;           svc.Url = url;&lt;br /&gt;           svc.Credentials = System.Net.CredentialCache.DefaultCredentials;&lt;br /&gt;           DateTime now = DateTime.Now;&lt;br /&gt;           svc.SendRegularText(&quot;me@mycompany.org&quot;, &quot;SendRegularText Test Email - &quot; + now, &quot;Body Text&quot;);&lt;br /&gt;           Assert.IsTrue(OutlookInbox.EmailReceived(&quot;SendRegularText Test Email - &quot; + now),&lt;br /&gt;               &quot;Email failed to arrive&quot;);&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_11_16_greenbar.GIF&quot; alt=&quot;NUnit green bar of success&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Ahh, a thing of beauty. :)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/11/other-mother-of-invention-impatience.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-3588723496374368875</guid><pubDate>Wed, 14 Nov 2007 00:49:00 +0000</pubDate><atom:updated>2007-11-13T16:50:06.613-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unit testing</category><title>Improve Your Unit Testing</title><description>&lt;p&gt;&lt;br /&gt;Improve your unit testing with:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&#39;http://www.ayende.com/projects/rhino-mocks.aspx&#39;&gt;Rhino Mocks&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&#39;http://www.codeproject.com/gen/design/autp5.asp&#39;&gt;Unit Test Patterns&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&#39;http://www.nunit.org/&#39;&gt;NUnit&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&#39;http://www.testdriven.net/&#39;&gt;TestDriven.NET&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&#39;http://codebetter.com/blogs/scott.bellware/archive/2006/02/28/139446.aspx&#39;&gt;NUnit Code Snippets for Visual Studio 2005&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/11/improve-your-unit-testing.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-8471683600021015660</guid><pubDate>Sun, 11 Nov 2007 19:17:00 +0000</pubDate><atom:updated>2007-11-11T11:20:27.276-08:00</atom:updated><title>Futzing with FUTS - Part I</title><description>&lt;p&gt;&lt;a href=&#39;http://en.wikipedia.org/wiki/Unit_testing&#39;&gt;Unit testing&lt;/a&gt; is a well accepted practice in the software development community.  There are many tools and articles devoted it.  &lt;a href=&#39;http://www.google.com/search?source=ig&amp;hl=en&amp;rlz=&amp;q=unit+testing&amp;btnG=Google+Search&#39;&gt;Google &#39;unit testing&#39;&lt;/a&gt; if you have any doubts.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;What about those of us working in the &lt;a href=&#39;http://www.sas.com&#39;&gt;SAS&lt;/a&gt; realm?  Given that &lt;a href=&#39;http://en.wikipedia.org/wiki/SAS_Institute&#39;&gt;SAS&lt;/a&gt; is basically a data-oriented scripting language, is it feasible to even think of unit test SAS code?  I would say, &quot;of course it is, it&#39;s code afterall!&quot;  If there is code, we can test it.  There&#39;s &lt;a href=&#39;http://cunit.sourceforge.net/&#39;&gt;CUnit&lt;/a&gt; for heaven&#39;s sake!  I&#39;ve even had the pleasure of using it (and then opted to roll my own C unit tests).  :)&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;A typical SAS program moves data around or analyzes it and consists of data steps (cursor style data manipulation) and/or procedures (&quot;procs&quot;).  Also thrown in are miscellaneous statements to make it all work how it should (e.g., libname statements).  Here is SAS program that creates a text file with 100 random numbers between 1 and 10.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;data _NULL_;&lt;br /&gt;    file &quot;c:\my_folder\random.txt&quot;;&lt;br /&gt;    do i = 1 to 100;&lt;br /&gt;        r = 1 + int(10*ranuni(-1));&lt;br /&gt;        put r;&lt;br /&gt;    end;&lt;br /&gt;run;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Naturally, SAS is capable of &lt;i&gt;WAY&lt;/i&gt; more powerful things, but we must start simple.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;What if I want to test my random number generator to make sure that it always and only generates numbers between 1 and 10?  How would I do such a thing in SAS?  Let&#39;s change gears for a minute and consider what we would do in C#.  In C# we would have solution containing three projects:  a class library called RandomNumberGenerator, a console application called RandomClient, and a class library called TestHarness.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;u&gt;RandomNumberGenerator&lt;/u&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;using System;&lt;br /&gt;&lt;br /&gt;namespace UtilityLib&lt;br /&gt;{&lt;br /&gt;    public class RandomNumberGenerator&lt;br /&gt;    {&lt;br /&gt;        private static Random generator = new Random();&lt;br /&gt;&lt;br /&gt;        public static int Ranuni()&lt;br /&gt;        {&lt;br /&gt;            return generator.Next(1, 11);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;u&gt;RandomClient&lt;/u&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;using System;&lt;br /&gt;using System.IO;&lt;br /&gt;using UtilityLib;&lt;br /&gt;&lt;br /&gt;namespace RandomClient&lt;br /&gt;{&lt;br /&gt;    class Program&lt;br /&gt;    {&lt;br /&gt;        static void Main(string[] args)&lt;br /&gt;        {&lt;br /&gt;            using (StreamWriter writer = new StreamWriter(@&quot;c:\my_folder\random.txt&quot;))&lt;br /&gt;            {&lt;br /&gt;                for(int i=0; i&lt;100; ++i)&lt;br /&gt;                    writer.WriteLine(RandomNumberGenerator.Ranuni());&lt;br /&gt;                writer.Flush();&lt;br /&gt;                writer.Close();&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;u&gt;TestHarness&lt;/u&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;using System;&lt;br /&gt;using UtilityLib;&lt;br /&gt;using NUnit.Framework;&lt;br /&gt;&lt;br /&gt;namespace TestHarness&lt;br /&gt;{&lt;br /&gt;    [TestFixture]&lt;br /&gt;    public class RandomNumberGeneratorTestSuite&lt;br /&gt;    {&lt;br /&gt;        [Test]&lt;br /&gt;        public void Ranuni_TestBounds()&lt;br /&gt;        {&lt;br /&gt;            for (int i = 0; i &lt; 100; ++i)&lt;br /&gt;            {&lt;br /&gt;                int r = RandomNumberGenerator.Ranuni();&lt;br /&gt;                Assert.IsTrue(r &gt;= 1 &amp;&amp; r &lt;= 10);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        [Test]&lt;br /&gt;        public void Ranuni_TestFullRange()&lt;br /&gt;        {&lt;br /&gt;            int[] counts = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };&lt;br /&gt;            for (int i = 0; i &lt; 100; ++i)&lt;br /&gt;                counts[RandomNumberGenerator.Ranuni() - 1] += 1;&lt;br /&gt;            for (int i = 0; i &lt; 10; ++i)&lt;br /&gt;                Assert.IsTrue(counts[i] &gt;= 1);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&#39;http://www.geocities.com/technoyoga/blog_images/2007_11_11_nunit.GIF&#39; alt=&#39;NUnit Success&#39;&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Now let&#39;s get back to SAS.  What can we similarly do to test random number generation in SAS?  The trick to unit testing in SAS is to place the code you want to test (your black box, if you will), into a macro.  So the random number generation code becomes a SAS macro like this.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;u&gt;RandomNumberGenerator&lt;/u&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;%macro RandomNumberGenerator;&lt;br /&gt;    1 + int(10*ranuni(-1))&lt;br /&gt;%mend;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Now my client code looks like this.  It just calls the RandomNumberGenerator macro 100 times to create the output file.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;u&gt;RandomClient&lt;/u&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;data _NULL_;&lt;br /&gt;    file &quot;c:\my_folder\random.txt&quot;;&lt;br /&gt;    do i = 1 to 100;&lt;br /&gt;        r = %RandomNumberGenerator;&lt;br /&gt;        put r;&lt;br /&gt;    end;&lt;br /&gt;run;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Now what about a test harness for RandomNumberGenerator?  It is finally time for &lt;a href=&#39;http://www.thotwave.com/products/futs.jsp&#39;&gt;FUTS (Framework for Unit Testing SAS® programs)&lt;/a&gt; to make its appearance.  FUTS, a &lt;i&gt;free&lt;/i&gt; product from &lt;a href=&#39;http://www.thotwave.com/&#39;&gt;Thotwave&lt;/a&gt;, is a wonderful set of easy to use assert SAS macros that test for various conditions - similar to the set of NUnit asserts.  Unlike NUnit, FUTS doesn&#39;t have a slick GUI, and instead FUTS throws errors into the SAS log when an assert fails and writes nothing to the log in case of success.  To run your tests, run the test harness code and check the SAS log for errors.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;To test the RandomNumberGenerator macro, I first create a temporary dataset called test1 that contains 100 random numbers.  To do a lower and upper bounds check (i.e., all random numbers are between 1 and 10), I select the max(r) and min(r) into macro variables and use the FUTS macro %assert_sym_compare to test (a) minr is greater than or equal to (GE) 1 and (b) maxr is less than or equal to (LE) 10.  This is equivalent to Assert.IsTrue(r &gt;= 1 &amp;&amp; r &lt;= 10); in the C# test Ranuni_TestBounds() above.  The second test, making sure that each number from 1 to 10 is generated at least once, is accomplished by first performing a proc freq (count how many times each value appears), then getting the count for each number (1, 2, ..., 10) into a macro variable and testing, using %assert_sym_compare again, that each count is GE 1.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;u&gt;TestHarness&lt;/u&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;data test1;&lt;br /&gt;    do i = 1 to 100;&lt;br /&gt;        r = %RandomNumberGenerator;&lt;br /&gt;        output;&lt;br /&gt;    end;  drop i;&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;proc sql noprint;&lt;br /&gt;    select min(r), max(r) into :minr, :maxr from test1;&lt;br /&gt;quit;&lt;br /&gt;%assert_sym_compare(&amp;minr, 1, type=COMPARISON, operator=GE);&lt;br /&gt;%assert_sym_compare(&amp;maxr, 10, type=COMPARISON, operator=LE);&lt;br /&gt;&lt;br /&gt;proc freq data=test1;&lt;br /&gt;    tables r / noprint out=freqout;&lt;br /&gt;run;&lt;br /&gt;proc sql noprint;&lt;br /&gt;    select count into :count1 from freqout where r=1;&lt;br /&gt;    select count into :count2 from freqout where r=2;&lt;br /&gt;    select count into :count3 from freqout where r=3;&lt;br /&gt;    select count into :count4 from freqout where r=4;&lt;br /&gt;    select count into :count5 from freqout where r=5;&lt;br /&gt;    select count into :count6 from freqout where r=6;&lt;br /&gt;    select count into :count7 from freqout where r=7;&lt;br /&gt;    select count into :count8 from freqout where r=8;&lt;br /&gt;    select count into :count9 from freqout where r=9;&lt;br /&gt;    select count into :count10 from freqout where r=10;&lt;br /&gt;quit;&lt;br /&gt;%assert_sym_compare(&amp;count1, 1, type=COMPARISON, operator=GE);&lt;br /&gt;%assert_sym_compare(&amp;count2, 1, type=COMPARISON, operator=GE);&lt;br /&gt;%assert_sym_compare(&amp;count3, 1, type=COMPARISON, operator=GE);&lt;br /&gt;%assert_sym_compare(&amp;count4, 1, type=COMPARISON, operator=GE);&lt;br /&gt;%assert_sym_compare(&amp;count5, 1, type=COMPARISON, operator=GE);&lt;br /&gt;%assert_sym_compare(&amp;count6, 1, type=COMPARISON, operator=GE);&lt;br /&gt;%assert_sym_compare(&amp;count7, 1, type=COMPARISON, operator=GE);&lt;br /&gt;%assert_sym_compare(&amp;count8, 1, type=COMPARISON, operator=GE);&lt;br /&gt;%assert_sym_compare(&amp;count9, 1, type=COMPARISON, operator=GE);&lt;br /&gt;%assert_sym_compare(&amp;count10, 1, type=COMPARISON, operator=GE);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This last bit of code is repetitive and would ideally be &quot;macro-ized&quot;.&lt;/p&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/11/futzing-with-futs-part-i.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-2158098977807558325</guid><pubDate>Sat, 10 Nov 2007 07:45:00 +0000</pubDate><atom:updated>2007-11-09T23:53:38.292-08:00</atom:updated><title>Fun with XMethods.com</title><description>&lt;p&gt;It&#39;s fun to occasionally take a stroll through &lt;a href=&quot;http://xmethods.com/&quot;&gt;xmethods.com&lt;/a&gt; see what kinds of web services folks are creating.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Here are some of the ones I found interesting in my look today.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;&lt;u&gt;Convert Text to Braille&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href=&quot;http://www.webservicex.net/braille.asmx&quot;&gt;Here&lt;/a&gt; is a neat and easy to use service for converting text to Braille.  The BrailleText conversion method takes two parameters, the text you want to convert (string) and the font size for the output (float).  It returns a byte array that is easily converted into a JPG and displayed on a WinForm.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;private void button1_Click(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt; net.webservicex.www.Braille svc = new TextToBraille1.net.webservicex.www.Braille();&lt;br /&gt; byte[] response = svc.BrailleText(this.textBox1.Text, 20);&lt;br /&gt; using (FileStream fs = new FileStream(&quot;braille.jpg&quot;, FileMode.Create, FileAccess.Write))&lt;br /&gt; {&lt;br /&gt;     foreach(byte b in response)&lt;br /&gt;     {&lt;br /&gt;         fs.WriteByte(b);&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt; pictureBox1.Load(&quot;braille.jpg&quot;);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_11_09_braille_form.JPG&quot; alt=&quot;Text to Braille winform&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;How to convert the image to actual Braille that the blind can read...hmmm...looks like there are &lt;a href=&quot;http://www.afb.org/ProdBrowseCatResults.asp?CatID=45&quot;&gt;many possibilities there&lt;/a&gt;.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;u&gt;&lt;b&gt;Dates of U.S. Holidays&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href=&quot;http://www.holidaywebservice.com/Holidays/US/Dates/USHolidayDates.asmx?WSDL&quot;&gt;This web service&lt;/a&gt; offers methods for discovering the dates of U.S. holidays for the year of your choice.  For example, the GetThanksgivingDay method requires that you pass it a year value (integer) and it returns a DateTime value.  I wired this service up to a simple ASP.NET page containing a calendar control and show this year&#39;s and next year&#39;s holidays, disabling selection of those dates as well.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Imports System.Collections.Generic&lt;br /&gt;&lt;br /&gt;Public Class Holiday&lt;br /&gt; Public Sub New(ByVal d As DateTime, ByVal nm As String)&lt;br /&gt;     Me.Date = d&lt;br /&gt;     Me.Name = nm&lt;br /&gt; End Sub&lt;br /&gt; Public [Date] As DateTime&lt;br /&gt; Public Name As String&lt;br /&gt;End Class&lt;br /&gt;&lt;br /&gt;Partial Class _Default&lt;br /&gt; Inherits System.Web.UI.Page&lt;br /&gt;&lt;br /&gt; Private holidays As New List(Of Holiday)&lt;br /&gt;&lt;br /&gt; Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load&lt;br /&gt;     Dim svc As New com.holidaywebservice.www.USHolidayDates()&lt;br /&gt;     For yr As Integer = DateTime.Now.Year To DateTime.Now.Year + 1&lt;br /&gt;         holidays.Add(New Holiday(svc.GetChristmasDay(yr), &quot;Christmas&quot;))&lt;br /&gt;         holidays.Add(New Holiday(svc.GetIndependenceDay(yr), &quot;Independence Day&quot;))&lt;br /&gt;         holidays.Add(New Holiday(svc.GetLaborDay(yr), &quot;Labor Day&quot;))&lt;br /&gt;         holidays.Add(New Holiday(svc.GetMartinLutherKingDay(yr), &quot;MLK Day&quot;))&lt;br /&gt;         holidays.Add(New Holiday(svc.GetMemorialDay(yr), &quot;Memorial Day&quot;))&lt;br /&gt;         holidays.Add(New Holiday(svc.GetNewYear(yr), &quot;New Year&#39;s Day&quot;))&lt;br /&gt;         holidays.Add(New Holiday(svc.GetPresidentsDay(yr), &quot;President&#39;s Day&quot;))&lt;br /&gt;         holidays.Add(New Holiday(svc.GetThanksgivingDay(yr), &quot;Thanksgiving&quot;))&lt;br /&gt;     Next&lt;br /&gt; End Sub&lt;br /&gt;&lt;br /&gt; Protected Sub Calendar1_DayRender(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DayRenderEventArgs) Handles Calendar1.DayRender&lt;br /&gt;     For Each h As Holiday In holidays&lt;br /&gt;         If e.Day.Date.Year = h.Date.Year AndAlso e.Day.Date.DayOfYear = h.Date.DayOfYear Then&lt;br /&gt;             e.Cell.Text = h.Name&lt;br /&gt;             e.Cell.Enabled = False&lt;br /&gt;         End If&lt;br /&gt;     Next&lt;br /&gt; End Sub&lt;br /&gt;End Class&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_11_09_calendar.GIF&quot; alt=&quot;U.S. Holidays on a Calendar&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;While the intent of this web service is quite nice, sadly it provides incorrect results as you can see in the image above (Christmas on the 24th? New Year&#39;s Day on Dec 31st?).  This just illustrates that there are dangers in using black box web services that live &quot;out there in the wild.&quot;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;u&gt;&lt;b&gt;Daily Dilbert Cartoon&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;And last, but not least, &lt;a href=&quot;http://www.esynaps.com/WebServices/DailyDiblert.asmx?WSDL&quot;&gt;this delightful web service&lt;/a&gt; serves up the URL of the&lt;br /&gt;&lt;br /&gt;Dilbert Cartoon of the day (or the bytes of the JPG).  I used the GetDailyDilbertImagePath method (no arguments) to return the URL (string) of the cartoon JPG location and plug that into a picturebox on a WinForm.  Very easy.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Public Class Form1&lt;br /&gt; Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load&lt;br /&gt;     Dim svc As New com.esynaps.www.DailyDilbert()&lt;br /&gt;     Dim url As String = svc.DailyDilbertImagePath()&lt;br /&gt;     Me.PictureBox1.Load(url)&lt;br /&gt;     Me.Text = url&lt;br /&gt; End Sub&lt;br /&gt;End Class&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_11_09_dilbert.JPG&quot; alt=&quot;WinForm displaying Dilbert cartoon of the day&quot; /&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/11/fun-with-xmethodscom.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-7229023753286111668</guid><pubDate>Fri, 09 Nov 2007 04:05:00 +0000</pubDate><atom:updated>2007-11-08T20:15:28.113-08:00</atom:updated><title>Unit Testing an Email Method</title><description>&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_11_08_inbox.jpg&quot; alt=&quot;Inbox&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I recently ran into the problem of needing to unit test a web method in an app that sends email.  Searching the web, I finally found &lt;a href=&quot;http://www.developerfusion.co.uk/show/4667/&quot;&gt;this&lt;/a&gt; which helped me develop my test.  Many thanks to &lt;a href=&quot;http://www.nullify.net/&quot;&gt;Simon Soanes&lt;/a&gt; for his post.  Here’s the C# 2.0 test harness code.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;using System;&lt;br /&gt;using NUnit.Framework;&lt;br /&gt;using MyApp.WebLayer;&lt;br /&gt;using System.Runtime.InteropServices;&lt;br /&gt;using Outlook = Microsoft.Office.Interop.Outlook; //Refs Microsoft Outlook 11.0 Object Library&lt;br /&gt;&lt;br /&gt;namespace TestHarness&lt;br /&gt;{&lt;br /&gt; [TestFixture]&lt;br /&gt; public class EmailServiceTestSuite&lt;br /&gt; {&lt;br /&gt;     [Test]&lt;br /&gt;     public void SendRegularText()&lt;br /&gt;     {&lt;br /&gt;         EmailWebService.EmailService svc = new EmailWebService.EmailService();&lt;br /&gt;         DateTime now = DateTime.Now;&lt;br /&gt;         svc.SendRegularText(&quot;me@myorg.org&quot;, &quot;SendRegularText Test Email - &quot; + now, &quot;&quot;);&lt;br /&gt;      &lt;br /&gt;         System.Threading.Thread.Sleep(10000); //Wait 10 seconds for email to arrive...sometimes not enough&lt;br /&gt;&lt;br /&gt;         Outlook.ApplicationClass outlookApp = null;&lt;br /&gt;         Outlook.NameSpace outlookNS = null;&lt;br /&gt;         bool emailArrived = false;&lt;br /&gt;         try&lt;br /&gt;         {&lt;br /&gt;             outlookApp = new Outlook.ApplicationClass();&lt;br /&gt;             outlookNS = outlookApp.GetNamespace(&quot;MAPI&quot;);&lt;br /&gt;             outlookNS.Session.Logon(&quot;outlook&quot;, &quot;&quot;, false, true);&lt;br /&gt;             Outlook.MAPIFolder inbox = outlookNS.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);&lt;br /&gt;             foreach (Outlook.MailItem email in inbox.Items)&lt;br /&gt;             {&lt;br /&gt;                 if (email.Subject == &quot;SendRegularText Test Email - &quot; + now)&lt;br /&gt;                 {&lt;br /&gt;                     emailArrived = true;&lt;br /&gt;                     email.Delete();&lt;br /&gt;                 }&lt;br /&gt;             }&lt;br /&gt;         }&lt;br /&gt;         finally&lt;br /&gt;         {&lt;br /&gt;             if (outlookNS != null)&lt;br /&gt;                 outlookNS.Logoff();&lt;br /&gt;         }&lt;br /&gt;         Assert.IsTrue(emailArrived, &quot;Email failed to arrive&quot;);&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;For testing purposes, I just send a test email to myself with a guaranteed unique subject line, wait for 10 seconds which is usually enough time for the email to show up in my Outlook Inbox, then assert that the email is present.  The SendRegularText web method accepts three parameters: email recipient, subject line, and body text (left blank in the test).&lt;/p&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/11/unit-testing-email-method.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-665378404057538656</guid><pubDate>Thu, 08 Nov 2007 02:47:00 +0000</pubDate><atom:updated>2007-11-07T18:47:57.419-08:00</atom:updated><title>VB.NET / C# Language Conversion Links</title><description>&lt;p&gt;I find it&#39;s really helpful to have at least a basic understanding of VB.NET &lt;i&gt;and&lt;/i&gt; C#.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;u&gt;C#&lt;/u&gt;&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;using System;&lt;br /&gt;&lt;br /&gt;public class MyClass&lt;br /&gt;{&lt;br /&gt;    static void Main()&lt;br /&gt;    {&lt;br /&gt;       Console.WriteLine(&quot;Hi, from C#!&quot;);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;u&gt;VB.NET&lt;/u&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Imports System&lt;br /&gt; &lt;br /&gt;Public Class MyClass&lt;br /&gt;    Shared  Sub Main()&lt;br /&gt;       Console.WriteLine(&quot;Howdy, from VB.NET!&quot;)&lt;br /&gt;    End Sub&lt;br /&gt;End Class&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Here are links to some miscellaneous online resources.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&#39;http://aspalliance.com/625&#39;&gt;Cheat sheet&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&#39;http://www.harding.edu/fmccown/vbnet_csharp_comparison.html&#39;&gt;Comparison&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Free conversion tools:&lt;br&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#39;http://labs.developerfusion.co.uk/convert/vb-to-csharp.aspx&#39;&gt;developerfusion&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;        &lt;li&gt;&lt;a href=&#39;http://www.kamalpatel.net/ConvertCSharp2VB.aspx&#39;&gt;kamalpatel.net&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;        &lt;li&gt;&lt;a href=&#39;http://www.dotnetspider.com/convert/CSharp-To-Vb.aspx&#39;&gt;dotnetspider&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;        &lt;li&gt;&lt;a href=&#39;http://codeconverter.sharpdevelop.net/SnippetConverter.aspx&#39;&gt;codeconverter&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&#39;http://blogs.msdn.com/asanto/archive/2005/10/27/485641.aspx&#39;&gt;XOR&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&#39;http://weblogs.asp.net/ssivakumar/archive/2003/09/17/27930.aspx&#39;&gt;Porting Common VB functions to C#&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/11/vbnet-c-language-conversion-links.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-7756660556951484582</guid><pubDate>Wed, 07 Nov 2007 04:14:00 +0000</pubDate><atom:updated>2007-11-06T20:19:49.641-08:00</atom:updated><title>Test Driving TallPDF.NET 3.0</title><description>&lt;p&gt;I am a big fan of PDF documents.  The only thing about them that is a drag is creating them.  I usually create them from a source document of some kind using &lt;a href=&quot;http://www.cutepdf.com/Products/CutePDF/writer.asp&quot;&gt;CutePDF Writer&lt;/a&gt;.  As the website says, &quot;FREE for personal and commercial use!  No watermarks!  No Popup Web Ads!&quot;  What could be better!?  It installs as a printer driver, so you can create a PDF out of just about any kind of file.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;For example, I can take this carefully drawn self-portrait in mspaint.exe and make a PDF using CutePDF Writer in no time flat.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_11_06_self_portrait_in_mspaint.GIF&quot; alt=&quot;self portrait in mspaint&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Hit the File | Print... menu...&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_11_06_cutepdfwriter.GIF&quot; alt=&quot;CutePDF Writer&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Select CutePDF Writer, press Print, et voila!  It&#39;s a PDF...&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_11_06_self_portrait_in_adobe1.GIF&quot; alt=&quot;PDF of self portrait&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;What could be better than that?  Well, I&#39;ll tell you...&lt;i&gt;programmatically&lt;/i&gt; creating PDFs!&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This is where &lt;a href=&quot;http://www.tallcomponents.com/default.aspx?id=tallpdf3&quot;&gt;TallPDF.NET 3.0&lt;/a&gt; comes in.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;After downloading this .NET component (I really just need the DLL, although the documentation that comes with it is nice, too), I just reference it in any old .NET application.  I will use a C# console application.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Here&#39;s all I need in the way of code and references.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_11_06_self_portrait_program.GIF&quot; alt=&quot;Visual Studio C# console program to programmatically create a PDF&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I actually don&#39;t need System.Data or System.Xml for this example, but System.Drawing, System.Web, and of course TallComponents.PDF.Layout are all required.  As you can see from the using statements, TallPDF is organized into multiple namespaces.  Here I only reference the three required by this example.  The object model is pretty self-explanatory: we have a document object, with one or more section objects, each containing paragraph objects which may be text or shapes or images.  At the end of the code, we&#39;re just pumping PDF bits through a file stream using the Document.Write method.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The PDF produced looks like this.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_11_06_self_portrait_in_adobe.GIF&quot; alt=&quot;self portrait PDF&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Here&#39;s a slightly more complex example.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;using System;&lt;br /&gt;using System.Drawing;&lt;br /&gt;using System.IO;&lt;br /&gt;using TallComponents.PDF.Layout;&lt;br /&gt;using F = TallComponents.PDF.Layout.Fonts;&lt;br /&gt;using TallComponents.PDF.Layout.Paragraphs;&lt;br /&gt;using TallComponents.PDF.Layout.Navigation;&lt;br /&gt;using TallComponents.PDF.Layout.Shapes;&lt;br /&gt;using B = TallComponents.PDF.Layout.Brushes;&lt;br /&gt;using P = TallComponents.PDF.Layout.Pens;&lt;br /&gt;using FLD = TallComponents.PDF.Layout.Shapes.Fields;&lt;br /&gt;&lt;br /&gt;namespace TestTallPdf2&lt;br /&gt;{&lt;br /&gt;   class Program&lt;br /&gt;   {&lt;br /&gt;       static void Main(string[] args)&lt;br /&gt;       {&lt;br /&gt;           string lorem1 = &quot;Lorem ipsum dolor sit amet, consectetuer adipiscing elit...&quot;;&lt;br /&gt;           string lorem2 = &quot;Cras suscipit. Aliquam hendrerit. Vivamus aliquam. Vestibulum...&quot;;&lt;br /&gt;           Document doc = new Document();&lt;br /&gt;           Section sec = new Section();&lt;br /&gt;           doc.Sections.Add(sec);&lt;br /&gt;          &lt;br /&gt;           Header hdr = new Header();&lt;br /&gt;           TextParagraph phdr = new TextParagraph();&lt;br /&gt;           hdr.Paragraphs.Add(phdr);&lt;br /&gt;           phdr.Fragments.Add(new Fragment(&quot;My Lorem Ipsum Document&quot;, F.Font.HelveticaBoldOblique, 12));&lt;br /&gt;           hdr.TopMargin = new Unit(0.5, UnitType.Inch);&lt;br /&gt;           sec.Header = hdr;&lt;br /&gt;          &lt;br /&gt;           Heading hd1 = new Heading(0);&lt;br /&gt;           hd1.SpacingBefore = 10;&lt;br /&gt;           hd1.SpacingAfter = 5;&lt;br /&gt;           hd1.Fragments.Add(new Fragment(&quot;Introduction&quot;, F.Font.Helvetica, 11));&lt;br /&gt;           sec.Paragraphs.Add(hd1);&lt;br /&gt;          &lt;br /&gt;           TextParagraph psec = new TextParagraph();&lt;br /&gt;           psec.Fragments.Add(new Fragment(lorem1, F.Font.TimesRoman, 10));&lt;br /&gt;           psec.SpacingAfter = 10;&lt;br /&gt;           psec.LineSpacing = 3;&lt;br /&gt;           sec.Paragraphs.Add(psec);&lt;br /&gt;&lt;br /&gt;           Heading hd2 = new Heading(0);&lt;br /&gt;           hd2.SpacingBefore = 10;&lt;br /&gt;           hd2.SpacingAfter = 5;&lt;br /&gt;           hd2.Fragments.Add(new Fragment(&quot;About the Author&quot;, F.Font.Helvetica, 11));&lt;br /&gt;           sec.Paragraphs.Add(hd2);&lt;br /&gt;&lt;br /&gt;           TextParagraph psec2 = new TextParagraph();&lt;br /&gt;           psec2.Justified = true;&lt;br /&gt;           psec2.Fragments.Add(new Fragment(lorem2, F.Font.TimesRoman, 10));&lt;br /&gt;           psec2.LineSpacing = 3;&lt;br /&gt;           sec.Paragraphs.Add(psec2);&lt;br /&gt;&lt;br /&gt;           Heading hd3 = new Heading(0);&lt;br /&gt;           hd3.SpacingBefore = 10;&lt;br /&gt;           hd3.SpacingAfter = 5;&lt;br /&gt;           hd3.Fragments.Add(new Fragment(&quot;1/4 of a Pie&quot;, F.Font.Helvetica, 11));&lt;br /&gt;           sec.Paragraphs.Add(hd3);&lt;br /&gt;&lt;br /&gt;           Drawing drawing = new Drawing(60, 60);&lt;br /&gt;           PieShape pie = new PieShape();&lt;br /&gt;           pie.Start = 0;&lt;br /&gt;           pie.Sweep = 90;&lt;br /&gt;           pie.Pen = new P.Pen(System.Drawing.Color.Red, 2);&lt;br /&gt;           pie.Brush = new B.SolidBrush(System.Drawing.Color.Blue);&lt;br /&gt;           drawing.Shapes.Add(pie);&lt;br /&gt;           sec.Paragraphs.Add(drawing);&lt;br /&gt;&lt;br /&gt;           Heading hd4 = new Heading(0);&lt;br /&gt;           hd4.SpacingBefore = 10;&lt;br /&gt;           hd4.SpacingAfter = 5;&lt;br /&gt;           hd4.Fragments.Add(new Fragment(&quot;Lorem Ipsum Bracelet&quot;, F.Font.Helvetica, 11));&lt;br /&gt;           sec.Paragraphs.Add(hd4);&lt;br /&gt;&lt;br /&gt;           Drawing drawing2 = new Drawing(180, 180);&lt;br /&gt;           ImageShape img = new ImageShape(@&quot;C:\my_folder\bracelet.jpg&quot;);&lt;br /&gt;           drawing2.Shapes.Add(img);&lt;br /&gt;           sec.Paragraphs.Add(drawing2);&lt;br /&gt;&lt;br /&gt;           Heading hd5 = new Heading(0);&lt;br /&gt;           hd5.SpacingBefore = 10;&lt;br /&gt;           hd5.SpacingAfter = 5;&lt;br /&gt;           hd5.Fragments.Add(new Fragment(&quot;Enter your name&quot;, F.Font.Helvetica, 11));&lt;br /&gt;           sec.Paragraphs.Add(hd5);&lt;br /&gt;&lt;br /&gt;           Drawing drawing3 = new Drawing(200, 30);&lt;br /&gt;           FLD.TextFieldShape field = new FLD.TextFieldShape(200, 30);&lt;br /&gt;           field.FullName = &quot;txtName&quot;;&lt;br /&gt;           drawing3.Shapes.Add(field);&lt;br /&gt;           sec.Paragraphs.Add(drawing3);           &lt;br /&gt;&lt;br /&gt;           ViewerPreferences vp = new ViewerPreferences();&lt;br /&gt;           vp.ZoomFactor = 0.75; //75%&lt;br /&gt;           doc.ViewerPreferences = vp;&lt;br /&gt;           using (FileStream fs = new FileStream(@&quot;C:\my_folder\lorem.pdf&quot;,&lt;br /&gt;               FileMode.Create, FileAccess.Write))&lt;br /&gt;           {&lt;br /&gt;               doc.Write(fs);&lt;br /&gt;           }&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This produces a PDF looking like this.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_11_06_lorem.GIF&quot; alt=&quot;Lorem Ipsum PDF&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Pretty nice, eh!&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;It works in ASP.NET, too.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;using System;&lt;br /&gt;using System.Data;&lt;br /&gt;using System.Configuration;&lt;br /&gt;using System.Web;&lt;br /&gt;using System.Web.Security;&lt;br /&gt;using System.Web.UI;&lt;br /&gt;using System.Web.UI.WebControls;&lt;br /&gt;using System.Web.UI.WebControls.WebParts;&lt;br /&gt;using System.Web.UI.HtmlControls;&lt;br /&gt;using TallComponents.PDF.Layout;&lt;br /&gt;using TallComponents.PDF.Layout.Paragraphs;&lt;br /&gt;&lt;br /&gt;public partial class _Default : System.Web.UI.Page&lt;br /&gt;{&lt;br /&gt;   protected void Page_Load(object sender, EventArgs e)&lt;br /&gt;   {&lt;br /&gt;       Document doc = new Document();&lt;br /&gt;       Section sec = new Section();&lt;br /&gt;       doc.Sections.Add(sec);&lt;br /&gt;       TextParagraph par = new TextParagraph();&lt;br /&gt;       par.Fragments.Add(new Fragment(&quot;Hello from ASP.NET!&quot;));&lt;br /&gt;       sec.Paragraphs.Add(par);&lt;br /&gt;       doc.Write(Response);&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://www.geocities.com/technoyoga/blog_images/2007_11_06_aspnet.GIF&quot; alt=&quot;ASP.NET example&quot; /&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/11/test-driving-tallpdfnet-30.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-6772280756854500900</guid><pubDate>Sat, 03 Nov 2007 18:09:00 +0000</pubDate><atom:updated>2007-11-03T11:11:27.616-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SubSonic</category><title>SubSonic for Database Versioning</title><description>&lt;p&gt;If you&#39;re a regular &lt;a href=&#39;http://www.subsonicproject.com/&#39;&gt;SubSonic&lt;/a&gt; user, then you are probably well aware of its database versioning feature.  Even if you&#39;re into other &lt;a href=&#39;http://en.wikipedia.org/wiki/Create,_read,_update_and_delete&#39;&gt;CRUD&lt;/a&gt; glue like &lt;a href=&#39;http://msdn2.microsoft.com/en-us/library/system.data.common.dataadapter.aspx&#39;&gt;data adapters&lt;/a&gt;, &lt;a href=&#39;http://msdn2.microsoft.com/en-us/netframework/aa904594.aspx&#39;&gt;LINQ&lt;/a&gt;, or &lt;a href=&#39;http://www.nhibernate.org/&#39;&gt;NHibernate&lt;/a&gt;, I think you will find this feature&lt;br /&gt;a reason to check out SubSonic.  This is &lt;i&gt;not&lt;/i&gt; about source control (there are plenty of other tools for that).  This is about taking a snapshot of your database objects (including the data in the tables) at a point in time and being able to recreate an exact copy of that snapshot.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;With SubSonic installed, open Visual Studio and go into the Tools | External Tools... menu and configure a new tool as shown.  In this case, I&#39;m calling the new tool SubSonic DB Versioner.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&#39;http://www.geocities.com/technoyoga/blog_images/2007_11_03_ToolSetup.GIF&#39; alt=&#39;SubSonic tool setup in Visual Studio&#39;&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Click ok, then add an app.config/web.config to your project.  I&#39;ll explain this using the app.config of a Winforms app.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;To set up SubSonic for database versioning, here is the minimum you will need to specify in the config file.  This is boilerplate code you can easily reuse by just changing the provider/connection string.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&#39;http://www.geocities.com/technoyoga/blog_images/2007_11_03_Config.GIF&#39; alt=&#39;SubSonic app.config settings&#39;&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I recommend creating a new folder in your project called something like DatabaseSnapshots.  You are now ready to make a database snapshot using the versioning tool.  All you have to do is go back into the Tools menu and select SubSonic DB Versioner.  A window will pop up asking you if you want to accept the default command line arguments (because we checked Prompt for arguments).  For the purposes of this demonstration, we need to change the Arguments from &quot;version /out App_Code\DB&quot; to &quot;version /out DatabaseSnapshots&quot;.  Then let &#39;er rip, keeping an eye on the Output window to see the progress messages.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;When it finishes, you will end up with two new .sql scripts in your DatabaseSnapshots folder (e.g., SqlDataProvider_Schema_2007_10_30.sql and SqlDataProvider_Data_2007_10_30.sql).  The first script, the one with Schema in the name, contains code to recreate all tables, views, stored procedures, etc.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Here&#39;s just a tiny portion of the schema script.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/****** Object:  Table [dbo].[CustomerDemographics]    Script Date: 10/30/2007 13:52:13 ******/&lt;br /&gt;SET ANSI_NULLS ON&lt;br /&gt;SET QUOTED_IDENTIFIER ON&lt;br /&gt;IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N&#39;[dbo].[CustomerDemographics]&#39;) AND OBJECTPROPERTY(id, N&#39;IsUserTable&#39;) = 1)&lt;br /&gt;BEGIN&lt;br /&gt;CREATE TABLE [dbo].[CustomerDemographics](&lt;br /&gt; [CustomerTypeID] [nchar](10) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,&lt;br /&gt; [CustomerDesc] [ntext] COLLATE SQL_Latin1_General_CP1_CI_AS NULL,&lt;br /&gt; CONSTRAINT [PK_CustomerDemographics] PRIMARY KEY NONCLUSTERED &lt;br /&gt;(&lt;br /&gt; [CustomerTypeID] ASC&lt;br /&gt;) ON [PRIMARY]&lt;br /&gt;) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]&lt;br /&gt;END&lt;br /&gt;/****** Object:  Table [dbo].[Region]    Script Date: 10/30/2007 13:52:14 ******/&lt;br /&gt;SET ANSI_NULLS ON&lt;br /&gt;SET QUOTED_IDENTIFIER ON&lt;br /&gt;IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N&#39;[dbo].[Region]&#39;) AND OBJECTPROPERTY(id, N&#39;IsUserTable&#39;) = 1)&lt;br /&gt;BEGIN&lt;br /&gt;CREATE TABLE [dbo].[Region](&lt;br /&gt; [RegionID] [int] NOT NULL,&lt;br /&gt; [RegionDescription] [nchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,&lt;br /&gt; CONSTRAINT [PK_Region] PRIMARY KEY NONCLUSTERED &lt;br /&gt;(&lt;br /&gt; [RegionID] ASC&lt;br /&gt;) ON [PRIMARY]&lt;br /&gt;) ON [PRIMARY]&lt;br /&gt;END&lt;br /&gt;/****** Object:  Table [dbo].[Employees]    Script Date: 10/30/2007 13:52:14 ******/&lt;br /&gt;SET ANSI_NULLS ON&lt;br /&gt;SET QUOTED_IDENTIFIER ON&lt;br /&gt;IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N&#39;[dbo].[Employees]&#39;) AND OBJECTPROPERTY(id, N&#39;IsUserTable&#39;) = 1)&lt;br /&gt;BEGIN&lt;br /&gt;CREATE TABLE [dbo].[Employees](&lt;br /&gt; [EmployeeID] [int] IDENTITY(1,1) NOT NULL,&lt;br /&gt; [LastName] [nvarchar](20) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,&lt;br /&gt; [FirstName] [nvarchar](10) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,&lt;br /&gt; [Title] [nvarchar](30) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,&lt;br /&gt; [TitleOfCourtesy] [nvarchar](25) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,&lt;br /&gt; [BirthDate] [datetime] NULL,&lt;br /&gt; [HireDate] [datetime] NULL,&lt;br /&gt; [Address] [nvarchar](60) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,&lt;br /&gt; [City] [nvarchar](15) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,&lt;br /&gt; [Region] [nvarchar](15) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,&lt;br /&gt; [PostalCode] [nvarchar](10) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,&lt;br /&gt; [Country] [nvarchar](15) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,&lt;br /&gt; [HomePhone] [nvarchar](24) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,&lt;br /&gt; [Extension] [nvarchar](4) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,&lt;br /&gt; [Photo] [image] NULL,&lt;br /&gt; [Notes] [ntext] COLLATE SQL_Latin1_General_CP1_CI_AS NULL,&lt;br /&gt; [ReportsTo] [int] NULL,&lt;br /&gt; [PhotoPath] [nvarchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,&lt;br /&gt; CONSTRAINT [PK_Employees] PRIMARY KEY CLUSTERED &lt;br /&gt;(&lt;br /&gt; [EmployeeID] ASC&lt;br /&gt;) ON [PRIMARY]&lt;br /&gt;) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]&lt;br /&gt;END&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;That&#39;s pretty neat, but the second script is the coolest.  It contains code to populate the database tables with your data.  Here&#39;s a snippet of the data script.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;ALTER TABLE [Region] NOCHECK CONSTRAINT ALL&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;PRINT &#39;Begin inserting data in Region&#39;&lt;br /&gt;INSERT INTO [Region] ([RegionID], [RegionDescription])&lt;br /&gt;VALUES(1, &#39;Eastern                                           &#39;)&lt;br /&gt;INSERT INTO [Region] ([RegionID], [RegionDescription])&lt;br /&gt;VALUES(2, &#39;Western                                           &#39;)&lt;br /&gt;INSERT INTO [Region] ([RegionID], [RegionDescription])&lt;br /&gt;VALUES(3, &#39;Northern                                          &#39;)&lt;br /&gt;INSERT INTO [Region] ([RegionID], [RegionDescription])&lt;br /&gt;VALUES(4, &#39;Southern                                          &#39;)&lt;br /&gt;ALTER TABLE [Region] CHECK CONSTRAINT ALL&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;ALTER TABLE [Shippers] NOCHECK CONSTRAINT ALL&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;SET IDENTITY_INSERT [Shippers] ON &lt;br /&gt;PRINT &#39;Begin inserting data in Shippers&#39;&lt;br /&gt;INSERT INTO [Shippers] ([ShipperID], [CompanyName], [Phone])&lt;br /&gt;VALUES(1, &#39;Speedy Express&#39;, &#39;(503) 555-9831&#39;)&lt;br /&gt;INSERT INTO [Shippers] ([ShipperID], [CompanyName], [Phone])&lt;br /&gt;VALUES(2, &#39;United Package&#39;, &#39;(503) 555-3199&#39;)&lt;br /&gt;INSERT INTO [Shippers] ([ShipperID], [CompanyName], [Phone])&lt;br /&gt;VALUES(3, &#39;Federal Shipping&#39;, &#39;(503) 555-9931&#39;)&lt;br /&gt;SET IDENTITY_INSERT [Shippers] OFF &lt;br /&gt;ALTER TABLE [Shippers] CHECK CONSTRAINT ALL&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;ALTER TABLE [Suppliers] NOCHECK CONSTRAINT ALL&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;SET IDENTITY_INSERT [Suppliers] ON &lt;br /&gt;PRINT &#39;Begin inserting data in Suppliers&#39;&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(1, &#39;Exotic Liquids&#39;, &#39;Charlotte Cooper&#39;, &#39;Purchasing Manager&#39;, &#39;49 Gilbert St.&#39;, &#39;London&#39;, NULL, &#39;EC1 4SD&#39;, &#39;UK&#39;, &#39;(171) 555-2222&#39;, NULL, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(2, &#39;New Orleans Cajun Delights&#39;, &#39;Shelley Burke&#39;, &#39;Order Administrator&#39;, &#39;P.O. Box 78934&#39;, &#39;New Orleans&#39;, &#39;LA&#39;, &#39;70117&#39;, &#39;USA&#39;, &#39;(100) 555-4822&#39;, NULL, &#39;#CAJUN.HTM#&#39;)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(3, &#39;Grandma Kelly&#39;&#39;s Homestead&#39;, &#39;Regina Murphy&#39;, &#39;Sales Representative&#39;, &#39;707 Oxford Rd.&#39;, &#39;Ann Arbor&#39;, &#39;MI&#39;, &#39;48104&#39;, &#39;USA&#39;, &#39;(313) 555-5735&#39;, &#39;(313) 555-3349&#39;, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(4, &#39;Tokyo Traders&#39;, &#39;Yoshi Nagase&#39;, &#39;Marketing Manager&#39;, &#39;9-8 Sekimai Musashino-shi&#39;, &#39;Tokyo&#39;, NULL, &#39;100&#39;, &#39;Japan&#39;, &#39;(03) 3555-5011&#39;, NULL, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(5, &#39;Cooperativa de Quesos &#39;&#39;Las Cabras&#39;&#39;&#39;, &#39;Antonio del Valle Saavedra&#39;, &#39;Export Administrator&#39;, &#39;Calle del Rosal 4&#39;, &#39;Oviedo&#39;, &#39;Asturias&#39;, &#39;33007&#39;, &#39;Spain&#39;, &#39;(98) 598 76 54&#39;, NULL, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(6, &#39;Mayumi&#39;&#39;s&#39;, &#39;Mayumi Ohno&#39;, &#39;Marketing Representative&#39;, &#39;92 Setsuko Chuo-ku&#39;, &#39;Osaka&#39;, NULL, &#39;545&#39;, &#39;Japan&#39;, &#39;(06) 431-7877&#39;, NULL, &#39;Mayumi&#39;&#39;s (on the World Wide Web)#http://www.microsoft.com/accessdev/sampleapps/mayumi.htm#&#39;)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(7, &#39;Pavlova, Ltd.&#39;, &#39;Ian Devling&#39;, &#39;Marketing Manager&#39;, &#39;74 Rose St. Moonie Ponds&#39;, &#39;Melbourne&#39;, &#39;Victoria&#39;, &#39;3058&#39;, &#39;Australia&#39;, &#39;(03) 444-2343&#39;, &#39;(03) 444-6588&#39;, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(8, &#39;Specialty Biscuits, Ltd.&#39;, &#39;Peter Wilson&#39;, &#39;Sales Representative&#39;, &#39;29 King&#39;&#39;s Way&#39;, &#39;Manchester&#39;, NULL, &#39;M14 GSD&#39;, &#39;UK&#39;, &#39;(161) 555-4448&#39;, NULL, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(9, &#39;PB Knäckebröd AB&#39;, &#39;Lars Peterson&#39;, &#39;Sales Agent&#39;, &#39;Kaloadagatan 13&#39;, &#39;Göteborg&#39;, NULL, &#39;S-345 67&#39;, &#39;Sweden&#39;, &#39;031-987 65 43&#39;, &#39;031-987 65 91&#39;, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(10, &#39;Refrescos Americanas LTDA&#39;, &#39;Carlos Diaz&#39;, &#39;Marketing Manager&#39;, &#39;Av. das Americanas 12.890&#39;, &#39;Sao Paulo&#39;, NULL, &#39;5442&#39;, &#39;Brazil&#39;, &#39;(11) 555 4640&#39;, NULL, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(11, &#39;Heli Süßwaren GmbH &amp; Co. KG&#39;, &#39;Petra Winkler&#39;, &#39;Sales Manager&#39;, &#39;Tiergartenstraße 5&#39;, &#39;Berlin&#39;, NULL, &#39;10785&#39;, &#39;Germany&#39;, &#39;(010) 9984510&#39;, NULL, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(12, &#39;Plutzer Lebensmittelgroßmärkte AG&#39;, &#39;Martin Bein&#39;, &#39;International Marketing Mgr.&#39;, &#39;Bogenallee 51&#39;, &#39;Frankfurt&#39;, NULL, &#39;60439&#39;, &#39;Germany&#39;, &#39;(069) 992755&#39;, NULL, &#39;Plutzer (on the World Wide Web)#http://www.microsoft.com/accessdev/sampleapps/plutzer.htm#&#39;)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(13, &#39;Nord-Ost-Fisch Handelsgesellschaft mbH&#39;, &#39;Sven Petersen&#39;, &#39;Coordinator Foreign Markets&#39;, &#39;Frahmredder 112a&#39;, &#39;Cuxhaven&#39;, NULL, &#39;27478&#39;, &#39;Germany&#39;, &#39;(04721) 8713&#39;, &#39;(04721) 8714&#39;, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(14, &#39;Formaggi Fortini s.r.l.&#39;, &#39;Elio Rossi&#39;, &#39;Sales Representative&#39;, &#39;Viale Dante, 75&#39;, &#39;Ravenna&#39;, NULL, &#39;48100&#39;, &#39;Italy&#39;, &#39;(0544) 60323&#39;, &#39;(0544) 60603&#39;, &#39;#FORMAGGI.HTM#&#39;)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(15, &#39;Norske Meierier&#39;, &#39;Beate Vileid&#39;, &#39;Marketing Manager&#39;, &#39;Hatlevegen 5&#39;, &#39;Sandvika&#39;, NULL, &#39;1320&#39;, &#39;Norway&#39;, &#39;(0)2-953010&#39;, NULL, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(16, &#39;Bigfoot Breweries&#39;, &#39;Cheryl Saylor&#39;, &#39;Regional Account Rep.&#39;, &#39;3400 - 8th Avenue Suite 210&#39;, &#39;Bend&#39;, &#39;OR&#39;, &#39;97101&#39;, &#39;USA&#39;, &#39;(503) 555-9931&#39;, NULL, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(17, &#39;Svensk Sjöföda AB&#39;, &#39;Michael Björn&#39;, &#39;Sales Representative&#39;, &#39;Brovallavägen 231&#39;, &#39;Stockholm&#39;, NULL, &#39;S-123 45&#39;, &#39;Sweden&#39;, &#39;08-123 45 67&#39;, NULL, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(18, &#39;Aux joyeux ecclésiastiques&#39;, &#39;Guylène Nodier&#39;, &#39;Sales Manager&#39;, &#39;203, Rue des Francs-Bourgeois&#39;, &#39;Paris&#39;, NULL, &#39;75004&#39;, &#39;France&#39;, &#39;(1) 03.83.00.68&#39;, &#39;(1) 03.83.00.62&#39;, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(19, &#39;New England Seafood Cannery&#39;, &#39;Robb Merchant&#39;, &#39;Wholesale Account Agent&#39;, &#39;Order Processing Dept. 2100 Paul Revere Blvd.&#39;, &#39;Boston&#39;, &#39;MA&#39;, &#39;02134&#39;, &#39;USA&#39;, &#39;(617) 555-3267&#39;, &#39;(617) 555-3389&#39;, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(20, &#39;Leka Trading&#39;, &#39;Chandra Leka&#39;, &#39;Owner&#39;, &#39;471 Serangoon Loop, Suite #402&#39;, &#39;Singapore&#39;, NULL, &#39;0512&#39;, &#39;Singapore&#39;, &#39;555-8787&#39;, NULL, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(21, &#39;Lyngbysild&#39;, &#39;Niels Petersen&#39;, &#39;Sales Manager&#39;, &#39;Lyngbysild Fiskebakken 10&#39;, &#39;Lyngby&#39;, NULL, &#39;2800&#39;, &#39;Denmark&#39;, &#39;43844108&#39;, &#39;43844115&#39;, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(22, &#39;Zaanse Snoepfabriek&#39;, &#39;Dirk Luchte&#39;, &#39;Accounting Manager&#39;, &#39;Verkoop Rijnweg 22&#39;, &#39;Zaandam&#39;, NULL, &#39;9999 ZZ&#39;, &#39;Netherlands&#39;, &#39;(12345) 1212&#39;, &#39;(12345) 1210&#39;, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(23, &#39;Karkki Oy&#39;, &#39;Anne Heikkonen&#39;, &#39;Product Manager&#39;, &#39;Valtakatu 12&#39;, &#39;Lappeenranta&#39;, NULL, &#39;53120&#39;, &#39;Finland&#39;, &#39;(953) 10956&#39;, NULL, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(24, &#39;G&#39;&#39;day, Mate&#39;, &#39;Wendy Mackenzie&#39;, &#39;Sales Representative&#39;, &#39;170 Prince Edward Parade Hunter&#39;&#39;s Hill&#39;, &#39;Sydney&#39;, &#39;NSW&#39;, &#39;2042&#39;, &#39;Australia&#39;, &#39;(02) 555-5914&#39;, &#39;(02) 555-4873&#39;, &#39;G&#39;&#39;day Mate (on the World Wide Web)#http://www.microsoft.com/accessdev/sampleapps/gdaymate.htm#&#39;)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(25, &#39;Ma Maison&#39;, &#39;Jean-Guy Lauzon&#39;, &#39;Marketing Manager&#39;, &#39;2960 Rue St. Laurent&#39;, &#39;Montréal&#39;, &#39;Québec&#39;, &#39;H1J 1C3&#39;, &#39;Canada&#39;, &#39;(514) 555-9022&#39;, NULL, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(26, &#39;Pasta Buttini s.r.l.&#39;, &#39;Giovanni Giudici&#39;, &#39;Order Administrator&#39;, &#39;Via dei Gelsomini, 153&#39;, &#39;Salerno&#39;, NULL, &#39;84100&#39;, &#39;Italy&#39;, &#39;(089) 6547665&#39;, &#39;(089) 6547667&#39;, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(27, &#39;Escargots Nouveaux&#39;, &#39;Marie Delamare&#39;, &#39;Sales Manager&#39;, &#39;22, rue H. Voiron&#39;, &#39;Montceau&#39;, NULL, &#39;71300&#39;, &#39;France&#39;, &#39;85.57.00.07&#39;, NULL, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(28, &#39;Gai pâturage&#39;, &#39;Eliane Noz&#39;, &#39;Sales Representative&#39;, &#39;Bat. B 3, rue des Alpes&#39;, &#39;Annecy&#39;, NULL, &#39;74000&#39;, &#39;France&#39;, &#39;38.76.98.06&#39;, &#39;38.76.98.58&#39;, NULL)&lt;br /&gt;INSERT INTO [Suppliers] ([SupplierID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax], [HomePage])&lt;br /&gt;VALUES(29, &#39;Forêts d&#39;&#39;érables&#39;, &#39;Chantal Goulet&#39;, &#39;Accounting Manager&#39;, &#39;148 rue Chasseur&#39;, &#39;Ste-Hyacinthe&#39;, &#39;Québec&#39;, &#39;J2S 7S8&#39;, &#39;Canada&#39;, &#39;(514) 555-2955&#39;, &#39;(514) 555-2921&#39;, NULL)&lt;br /&gt;SET IDENTITY_INSERT [Suppliers] OFF &lt;br /&gt;ALTER TABLE [Suppliers] CHECK CONSTRAINT ALL&lt;br /&gt;GO&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I was so excited the first time I saw this, I nearly choked on my Diet Coke.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;There&#39;s a nice introduction to SubSonic &lt;a href=&#39;http://www.wekeroad.com/ss_setup2.html&#39;&gt;here&lt;/a&gt;.  The latest news (&lt;a href=&#39;http://blog.wekeroad.com/2007/10/26/microsoft-subsonic-and-me/&#39;&gt;here&lt;/a&gt; and &lt;a href=&#39;http://haacked.com/archive/2007/10/26/mr-subsonic-joins-microsoft.aspx&#39;&gt;here&lt;/a&gt;) is that Rob Conery (a.k.a. Mr. SubSonic) has joined Microsoft and MS will be paying him to continue developing SS.  Rock on.&lt;/p&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/11/subsonic-for-database-versioning.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-4108945181468702239</guid><pubDate>Thu, 01 Nov 2007 04:32:00 +0000</pubDate><atom:updated>2007-10-31T21:36:31.029-07:00</atom:updated><title>Your Friends, The Visual Studio Code Snippets</title><description>&lt;p&gt;There are some great and well-known third-party tools that enhance or complement Visual Studio .NET, such as &lt;a href=&#39;http://www.nunit.org/&#39;&gt;NUnit&lt;/a&gt; and Roeder&#39;s &lt;a href=&#39;http://www.aisto.com/roeder/dotnet/&#39;&gt;Reflector&lt;/a&gt;.  Visual Studio&#39;s Code Snippets, both the &lt;a href=&#39;http://msdn2.microsoft.com/en-us/library/z41h7fat(VS.80).aspx&#39;&gt;default (built-in) snippets&lt;/a&gt;, and the &lt;a href=&#39;http://msdn2.microsoft.com/en-us/vstudio/aa718338.aspx&#39;&gt;C#&lt;/a&gt; and &lt;a href=&#39;http://msdn2.microsoft.com/en-us/vstudio/ms789079.aspx&#39;&gt;VB.NET&lt;/a&gt; add on snippets are amazingly useful and seemingly under-advertised and under-utilized.  I don&#39;t hear many people talking about them and I haven&#39;t seen much use of them.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;What are code snippets?  Code shortcuts for writing bigger chunks of code.  A quick and powerful example is a good way to introduce them.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Here&#39;s the default code snippet &quot;prop&quot; in action in C# that codes up an entire read/write property complete with private storage variable.  They way I use this is instead of writing everything out by hand to code out a property, I simply type &quot;prop&quot; followed by TAB, TAB.  As you can see, prop even appears in Intellisense.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&#39;http://www.geocities.com/technoyoga/blog_images/2007_10_31_prop1.GIF&#39; alt=&quot;prop code snippet in Intellisense dropdown list&quot; border=&quot;1&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;After I hit TAB the 2nd time, I see this.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&#39;http://www.geocities.com/technoyoga/blog_images/2007_10_31_prop2.GIF&#39; alt=&quot;prop code snippet fills in a lot of code for you&quot; border=&quot;1&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Then I just type a quick &quot;string&quot;, TAB, TAB, &quot;myName&quot;, TAB, &quot;Name&quot; and I&#39;m done!&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&#39;http://www.geocities.com/technoyoga/blog_images/2007_10_31_prop3.GIF&#39; alt=&quot;quick product of using the prop code snippet&quot; border=&quot;1&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Will I ever type out another private member/getter/setter property combo again?  I doubt it.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The official documentation says, &quot;The Code Snippet Inserter is invoked through the Insert Code Snippet or Surround With commands on the IntelliSense menu, or by using the keyboard shortcuts CTRL+K, then X and CTRL+K, then S respectively.&quot;  I find it easiest to just type out the snippet&#39;s keyword and press TAB, TAB. Things are a little different in the VB.NET editor.  Snippet keywords don&#39;t appear on Intellisense lists, but simply right-click and select Insert Snippet.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&#39;http://www.geocities.com/technoyoga/blog_images/2007_10_31_vb1.GIF&#39; alt=&quot;in VB.NET right-click and select Insert Snippet&quot; border=&quot;1&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;After clicking Insert Snippet, a categorized menu of snippets is presented.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&#39;http://www.geocities.com/technoyoga/blog_images/2007_10_31_vb2.GIF&#39; alt=&quot;snippet menu&quot; border=&quot;1&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Selecting Common Code Patterns and then Exception Handling and shows the available snippets in that category.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&#39;http://www.geocities.com/technoyoga/blog_images/2007_10_31_vb3.GIF&#39; alt=&quot;Exception handling code snippets&quot; border=&quot;1&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Selecting Try...Catch...Finally...End Try Statement inserts this code.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&#39;http://www.geocities.com/technoyoga/blog_images/2007_10_31_vb4.GIF&#39; alt=&quot;Try...Catch...Finally...End Try code snippet&quot; border=&quot;1&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Now I just need to decide what class of Exception I want to catch and I&#39;m off and running.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;To add on more snippets from MSDN or &lt;a href=&#39;http://msdn2.microsoft.com/en-us/library/ms165394(VS.80).aspx&#39;&gt;make&lt;/a&gt; and add your own, use the Code Snippets Manager.&lt;/p&gt; &lt;br /&gt;&lt;br /&gt;&lt;img src=&#39;http://www.geocities.com/technoyoga/blog_images/2007_10_31_mgr1.GIF&#39; alt=&quot;Code Snippets Manager&quot; border=&quot;1&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;For example, adding the Database code snippets gives you nice shortcuts such as &quot;adoCreateSqlConn&quot; which generates the following code.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection();&lt;br /&gt;conn.ConnectionString = @&quot;Data Source=ServerName;Initial Catalog=Northwind;Persist Security Info=True;User ID=&lt;user name&gt;;Password=&lt;your password&gt;&quot;;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I hope this was helpful.  Enjoy!&lt;/p&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/10/your-friends-visual-studio-code.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-2120566323860955954</guid><pubDate>Tue, 30 Oct 2007 02:19:00 +0000</pubDate><atom:updated>2007-10-29T19:34:45.207-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">FTP</category><category domain="http://www.blogger.com/atom/ns#">Ruby</category><title>Microsoft .NET (2.0) FTP Support Still Sucks</title><description>An FTP client is coaxed out of the .NET 1.1 framework by &lt;a href=&quot;http://www.ondotnet.com/pub/a/dotnet/2004/05/10/ftpdotnet.htm&quot;&gt;Liberty&lt;/a&gt; (&amp;amp; Enterprise Distributed Technologies –LOL) and Howard Richards shows how to &lt;a href=&quot;http://www.codeproject.com/vb/net/FtpClient.asp&quot;&gt;get the job done with .NET 2.0&lt;/a&gt;.  Whidbey (2.0) was billed as having &lt;a href=&quot;http://blogs.msdn.com/brada/archive/2004/06/11/153523.aspx&quot;&gt;FTP support&lt;/a&gt;, but when you have to write this code does that qualify as support?&lt;br /&gt;&lt;pre&gt;string URI = &quot;ftp://mirror.x10.com/gnuftp/gnu/README.DESCRIPTIONS&quot;;&lt;br /&gt;System.IO.FileInfo fi = new System.IO.FileInfo(@&quot;c:\temp\deleteme.txt&quot;);&lt;br /&gt;System.Net.FtpWebRequest ftp =&lt;br /&gt;(System.Net.FtpWebRequest)System.Net.FtpWebRequest.Create(URI);&lt;br /&gt;ftp.Credentials = new System.Net.NetworkCredential(&quot;anonymous&quot;, &quot;&quot;);&lt;br /&gt;ftp.KeepAlive = false;&lt;br /&gt;ftp.Method = System.Net.WebRequestMethods.Ftp.DownloadFile;&lt;br /&gt;ftp.UseBinary = true;&lt;br /&gt;using (System.Net.FtpWebResponse response = (System.Net.FtpWebResponse)ftp.GetResponse())&lt;br /&gt;{&lt;br /&gt; using (System.IO.Stream responseStream = response.GetResponseStream())&lt;br /&gt; {&lt;br /&gt;     using (System.IO.FileStream fs = fi.OpenWrite())&lt;br /&gt;     {&lt;br /&gt;         try&lt;br /&gt;         {&lt;br /&gt;             byte[] buffer = new byte[2048];&lt;br /&gt;             int read = 0;&lt;br /&gt;             do&lt;br /&gt;             {&lt;br /&gt;                 read = responseStream.Read(buffer, 0, buffer.Length);&lt;br /&gt;                 fs.Write(buffer, 0, read);&lt;br /&gt;             } while (!(read == 0));&lt;br /&gt;             responseStream.Close();&lt;br /&gt;             fs.Flush();&lt;br /&gt;             fs.Close();&lt;br /&gt;         }&lt;br /&gt;         catch (Exception)&lt;br /&gt;         {&lt;br /&gt;             fs.Close();&lt;br /&gt;             fi.Delete();&lt;br /&gt;             throw;&lt;br /&gt;         }&lt;br /&gt;     }&lt;br /&gt;     responseStream.Close();&lt;br /&gt; }&lt;br /&gt; response.Close();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;It almost makes more sense to shell out to c:\windows\system32\ftp.exe as in this VBA code.&lt;br /&gt;&lt;pre&gt;Dim ff As Integer&lt;br /&gt;ff = FreeFile&lt;br /&gt;ff = FreeFile&lt;br /&gt;Open &quot;c:\temp\ftpcommands.scr&quot; For Output As #ff&lt;br /&gt;    Print #ff, &quot;open mirror.x10.com&quot;&lt;br /&gt;    Print #ff, &quot;anonymous&quot;&lt;br /&gt;    Print #ff, &quot;no_password&quot;&lt;br /&gt;    Print #ff, &quot;cd gnuftp/gnu&quot;&lt;br /&gt;    Print #ff, &quot;binary&quot;&lt;br /&gt;    Print #ff, &quot;get README.DESCRIPTIONS c:\temp\deleteme3.txt&quot;&lt;br /&gt;    Print #ff, &quot;bye&quot;&lt;br /&gt;Close #ff&lt;br /&gt;ff = FreeFile&lt;br /&gt;Open &quot;c:\temp\ftpcommands.bat&quot; For Output As #ff&lt;br /&gt;    Print #ff, &quot;ftp -s:c:\temp\ftpcommands.scr&quot;&lt;br /&gt;    Print #ff, &quot;Echo &quot;&quot;Complete&quot;&quot; &gt; c:\temp\ftpcommands.done&quot;&lt;br /&gt;Close #ff&lt;br /&gt;Shell &quot;c:\temp\ftpcommands.bat&quot;, vbHide&lt;br /&gt;Do While Dir(&quot;c:\temp\ftpcommands.done&quot;) = &quot;&quot;&lt;br /&gt;    DoEvents&lt;br /&gt;Loop&lt;br /&gt;Dim dtmContinueAt As Date&lt;br /&gt;dtmContinueAt = Now + TimeValue(&quot;0:00:03&quot;)&lt;br /&gt;Do While Now &lt; dtmContinueAt&lt;br /&gt;Loop&lt;br /&gt;Kill &quot;c:\temp\ftpcommands.scr&quot;&lt;br /&gt;Kill &quot;c:\temp\ftpcommands.bat&quot;&lt;br /&gt;Kill &quot;c:\temp\ftpcommands.done&quot;&lt;/pre&gt;&lt;br /&gt;Now contemplate this equivalent Ruby code.&lt;br /&gt;&lt;pre&gt;require &#39;net/ftp&#39;&lt;br /&gt;Net::FTP.open(&#39;mirror.x10.com&#39;) do |ftp|&lt;br /&gt; ftp.login&lt;br /&gt; files = ftp.chdir(&#39;gnuftp/gnu&#39;)&lt;br /&gt; ftp.getbinaryfile(&#39;README.DESCRIPTIONS&#39;, &#39;c:\temp\deleteme2.txt&#39;)&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;I feel prompted to ask, just wtf does Microsoft have against FTP?  Aside from that, the obvious question is “will .NET 3.x ‘Orcas’ do better?”  Or maybe the obvious question is, “is .NET a misnomer*?”&lt;br /&gt;&lt;br /&gt;* Because last time I checked FTP was an Inter&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;net&lt;/span&gt; protocol: &lt;a href=&quot;http://www.webster.com/dictionary/ftp&quot;&gt;“a system for transferring computer files especially via the Internet.”&lt;/a&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/10/microsoft-net-20-ftp-support-still.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-6790398825087211279.post-8351521089006233599</guid><pubDate>Tue, 30 Oct 2007 01:31:00 +0000</pubDate><atom:updated>2007-10-29T19:00:20.594-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Meditation</category><category domain="http://www.blogger.com/atom/ns#">Programming</category><category domain="http://www.blogger.com/atom/ns#">Technology</category><category domain="http://www.blogger.com/atom/ns#">Yoga</category><title>First Post</title><description>Dear readers, welcome to my blog.  This is the first of what I hope will be many posts.&lt;br /&gt;&lt;br /&gt;You may be wondering...what is “technoyoga”?  I chose the name of this blog because I am interested in and practicing both computer technology and spirituality (among other things).  A large bulk of my time is spent working in the computer programming arena as well as the spiritual practice domain.  And who says they can&#39;t co-exist?  What programmer hasn’t experienced the meditative bliss of an &lt;span style=&quot;font-style: italic;&quot;&gt;n&lt;/span&gt;-hour long &lt;a href=&quot;http://www.glitchnyc.com/2006/01/03/the-first-great-coding-binge-of-2006/&quot;&gt;coding binge&lt;/a&gt; in which the ego subsides and is replaced by classes, methods, unit tests, and compilations?  I think a lot of programmers out there know what I’m talking about even if they haven’t put it in such terms.&lt;br /&gt;&lt;br /&gt;I was introduced to the term “karma yoga” by &lt;a href=&quot;http://www.ramdass.org/&quot;&gt;Ram Dass&lt;/a&gt; many years ago and quite like &lt;a href=&quot;http://www.hinduism.co.za/karma.htm#The%20Teachings%20of%20Sri%20Ramana%20Maharshi&quot;&gt;the practice&lt;/a&gt;, hard as it is.  As a blogger, &lt;a href=&quot;http://www.realization.org/page/doc0/doc0072.htm&quot;&gt;I-I&lt;/a&gt; will strive to &lt;a href=&quot;http://www.codinghorror.com/blog/archives/000983.html&quot;&gt;jab and throw haymakers on a regular basis&lt;/a&gt; without expectation and have some fun.  I figure if Scott Hanselman can have his &lt;a href=&quot;http://www.hanselman.com/blog/&quot;&gt;Computer Zen&lt;/a&gt;, then I can have my technoyoga.  :)&lt;br /&gt;&lt;br /&gt;Please note I am not affiliated in any way with &lt;a href=&quot;http://technoyoga.com/&quot;&gt;Technoyoga.com&lt;/a&gt; or the &lt;a href=&quot;http://www.lufee.com/comments/id/7c4de8e84165d29fa8bc2280b5d8fc2efitsugar/&quot;&gt;Techno Yoga Mat&lt;/a&gt;.  Hopefully they won’t ask me to change the name of my blog.&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;&lt;img src=&quot;http://www.feedburner.com/fb/images/pub/feed-icon32x32.png&quot; alt=&quot;&quot; style=&quot;vertical-align:middle;border:0&quot;/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://feeds.feedburner.com/Technoyoga&quot; rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt;Subscribe in a reader&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://technoyoga.blogspot.com/2007/10/first-post.html</link><author>noreply@blogger.com (DavidR)</author><thr:total>0</thr:total></item></channel></rss>